/*
 * Copyright 2010 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.editor.client.adapters;

import com.google.gwt.editor.client.CompositeEditor;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.EditorDelegate;

import java.util.Collections;
import java.util.List;

/**
 * Manages a list of objects and their associated Editors.
 * <p>
 * The ListEditor will have no backing list until {@link #setValue(List)} is
 * called with a non-null value.
 * 
 * @param <T> The type of data being managed
 * @param <E> The type of Editor
 */
public class ListEditor<T, E extends Editor<? super T>> implements
    CompositeEditor<List<T>, T, E> {

  /**
   * Create a ListEditor backed by an EditorSource.
   * 
   * @param <T> The type of data being managed
   * @param <E> The type of Editor
   * @param source the EditorSource which will create sub-Editors
   * @return a new instance of ListEditor
   */
  public static <T, E extends Editor<? super T>> ListEditor<T, E> of(
      EditorSource<E> source) {
    return new ListEditor<T, E>(source);
  }

  private CompositeEditor.EditorChain<T, E> chain;
  private EditorSource<E> editorSource;
  private ListEditorWrapper<T, E> list;

  /**
   * Create a ListEditor backed by an EditorSource.
   * 
   * @param source the EditorSource which will create sub-Editors
   */
  protected ListEditor(EditorSource<E> source) {
    this.editorSource = source;
  }

  /**
   * Creates a temporary sub-Editor to use for traversal.
   * 
   * @return an {@link Editor} of type E
   */
  public E createEditorForTraversal() {
    return editorSource.createEditorForTraversal();
  }

  public void flush() {
    if (list != null) {
      list.flush();
    }
  }

  /**
   * Returns an unmodifiable, live view of the Editors managed by the
   * ListEditor.
   * <p>
   * The returned list will be live until the next call to {@link #setValue(List)}
   * and shouldn't be used after that. Editors might (or might not) be reused
   * after a call to {@link #setValue(List)}.
   * <p>
   * If there is no backing list, an empty list will be returned.
   * 
   * @return a List of {@link Editor Editors} of type E
   */
  public List<E> getEditors() {
    if (list == null) {
      return Collections.emptyList();
    }
    return Collections.unmodifiableList(list.getEditors());
  }

  /**
   * Returns a live view of the ListEditor's backing data.
   * <p>
   * The structure of the List may be mutated arbitrarily, subject to the
   * limitations of the backing List, but the elements themselves should not
   * be mutated except through {@link #getEditors()} to avoid data
   * inconsistency.
   * <p>
   * Returns null if there is no backing list, and edits cannot be made.
   * 
   * <pre>
   * ListEditor&lt;Foo, MyFooEditor> listEditor = ListEditor.of(...);
   * listEditor.setValue(listOfFoo); // Usually called by EditorDriver
   * listEditor.getList().set(1, new Foo());
   * listEditor.getEditors().get(1).getFooFieldEditor().setValue(....);
   * </pre>
   * 
   * @return a live view of the ListEditor's backing data, or <code>null</code>
   *         if there is no backing list.
   */
  public List<T> getList() {
    return list;
  }

  public String getPathElement(E subEditor) {
    return "[" + list.getEditors().indexOf(subEditor) + "]";
  }

  public void onPropertyChange(String... paths) {
  }

  public void setDelegate(EditorDelegate<List<T>> delegate) {
  }

  public void setEditorChain(CompositeEditor.EditorChain<T, E> chain) {
    this.chain = chain;
  }

  /**
   * Sets the ListEditor's backing data.
   * <p>
   * If a null is passed in, the ListEditor will have no backing list and edits
   * cannot be made.
   * 
   * @param value a List of data objects of type T
   */
  public void setValue(List<T> value) {
    if (list == null && value == null) {
      // fast exit
      return;
    }
    if (list != null && list.isSameValue(value)) {
      // setting the same value as the one being edited
      list.refresh();
      return;
    }

    if (list != null) {
      // Having entire value reset, so dump the wrapper gracefully
      list.detach();
    }
    if (value == null) {
      list = null;
    } else {
      list = new ListEditorWrapper<T, E>(value, chain, editorSource);
      list.attach();
    }
  }
}
