import java.util.AbstractList;
import java.util.List;

import com.google.gwt.editor.client.EditorDelegate;
import com.google.gwt.editor.client.IsEditor;
import com.google.gwt.editor.client.ValueAwareEditor;
import com.google.gwt.editor.client.adapters.EditorSource;
import com.google.gwt.editor.client.adapters.ListEditor;
import com.google.gwt.view.client.ListDataProvider;

/**
 * A merge between ListDataProvider and ListEditor to make sure the same list
 * will be shared by both of them, simplifying user code. Editions to the
 * List<T> returned by this class getList() method should apply both to the
 * ListDataProvider (and, consequently, to any display attached to it) and to
 * the ListEditor, editing the object under edition by the Editor framework.
 * 
 * @author tiago
 * 
 * @param <T>
 * @param <E>
 */
public class ListEditorDataProvider<T, E extends ListEditorDataProvider.SubEditor<T>> extends ListDataProvider<T> implements
		IsEditor<ListEditor<T, E>>, ValueAwareEditor<List<T>> {

	/**
	 * We need all sub-editors to extend this class so we can forward
	 * modifications done via the editor framework to the ListDataProvider
	 * internal list.
	 * 
	 * @author tiago
	 * 
	 * @param <T>
	 */
	public static class SubEditor<T> implements ValueAwareEditor<T> {
		private int indexInList;
		private ListEditorDataProvider<T, ?> listManager;

		/**
		 * Constructs a new wrapper, using the listProvider which should get
		 * modifications forwarded to, and the index in that list where the item
		 * to be edited by this editor is.
		 * 
		 * @param listProvider
		 *            The ListDataProvider which we'll forward modifications to.
		 * @param indexInList
		 *            The index in the list.
		 */
		protected SubEditor(ListEditorDataProvider<T,?> listProvider, int indexInList) {
			this.indexInList = indexInList;
			this.listManager = listProvider;
		}

		@Override
		public void onPropertyChange(String... paths) {
			//From ListEditor to the ListDataProvider
			listManager.getDataProviderList().set(indexInList, listManager.listEditor.getList().get(indexInList));
		}
		
		protected int getIndexInList() {
			return indexInList;
		}

		//Methods we don't care about
		@Override public void setValue(T value) {}
		@Override public void setDelegate(EditorDelegate<T> delegate) {}
		@Override public void flush() {}
	}

	/**
	 * This is a façade that's given whenever the outside user requests this
	 * object internal list. This is a fake list that perform all meaningful
	 * operations directly in the ListEditor and ListDataProvider at the same
	 * time.
	 * 
	 * @author tiago
	 * 
	 */
	private class ListFacade extends AbstractList<T> {

		@Override
		public T get(int index) {
			return listEditor.getList().get(index);
		}

		@Override
		public int size() {
			return listEditor.getList().size();
		}

		@Override
		public T set(int index, T element) {
			getDataProviderList().set(index, element);
			return listEditor.getList().set(index, element);
		}

		@Override
		public void add(int index, T element) {
			getDataProviderList().add(index, element);
			listEditor.getList().add(index, element);
		}

		@Override
		public T remove(int index) {
			getDataProviderList().remove(index);
			return listEditor.getList().remove(index);
		}		
	}
	
	private ListEditor<T, E> listEditor;
	private final ListFacade listFacade = new ListFacade();
	
	protected ListEditorDataProvider(EditorSource<E> editorSource) {
		listEditor = ListEditor.of(editorSource);
	}

	@Override
	public ListEditor<T, E> asEditor() {
		return listEditor;
	}

	@Override
	public void setValue(List<T> value) {
		// When the editor framework defines the List to be edited, we change
		// the current list on ListDataProvider
		super.setList(value);
	}

	/**
	 * Creates a ListEditorDataProvider backed by an EditorSource.
	 * 
	 * @param <T>
	 *            The type of the data being managed (the type of the List
	 *            items).
	 * @param <E>
	 *            The type of the editor for data being managed (the editor
	 *            which will edit the list items).
	 * @param editorSource
	 *            The object which will provide the sub-editors of type E
	 * @return
	 */
	public static <T, E extends SubEditor<T>> ListEditorDataProvider<T, E> of (EditorSource<E> editorSource) {
		return new ListEditorDataProvider<T, E>(editorSource);
	}
	
	/**
	 * Just an utility method to access the sub-editors list.
	 * @return
	 */
	public List<E> getEditors() {
		return asEditor().getEditors();
	}
	
	@Override
	public List<T> getList() {
		return listFacade;
	}

	/**
	 * Returns the list managed by the ListDataProvider.
	 * 
	 * @return
	 */
	private List<T> getDataProviderList() {
		return super.getList();
	}

	// Adding empty bodies to these inherited methods since they might be
	// irrelevant to the user.
	@Override public void setDelegate(EditorDelegate<List<T>> delegate) {}
	@Override public void onPropertyChange(String... paths) {}
}
