package seedu.address.model.task;

import java.util.Iterator;
import java.util.List;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import seedu.address.commons.core.UnmodifiableObservableList;
import seedu.address.commons.exceptions.DuplicateDataException;

/**
 * A list of tasks that enforces uniqueness between its elements and does not
 * allow nulls.
 *
 * Supports a minimal set of list operations.
 *
 * @see Task#equals(Object)
 */
public class UniqueTaskList implements Iterable<Task> {

    private final ObservableList<Task> internalList = FXCollections.observableArrayList();

    /**
     * Returns true if the list contains an equivalent task as the given
     * argument.
     */
    public boolean contains(ReadOnlyTask toCheck) {
        assert toCheck != null;
        return internalList.contains(toCheck);
    }

    /**
     * Adds a task to the list.
     *
     * @throws DuplicateTaskException
     *             if the task to add is a duplicate of an existing task in the
     *             list.
     */
    public void add(Task toAdd) throws DuplicateTaskException {
        assert toAdd != null;
        if (contains(toAdd)) {
            throw new DuplicateTaskException();
        }
        internalList.add(toAdd);
    }

    /**
     * Updates the task in the list at position {@code index} with
     * {@code editedTask}.
     *
     * @throws DuplicateTaskException
     *             if updating the task's details causes the task to be
     *             equivalent to another existing task in the list.
     * @throws IndexOutOfBoundsException
     *             if {@code index} < 0 or >= the size of the list.
     */
    public void updateTask(int index, ReadOnlyTask editedTask) throws DuplicateTaskException {
        assert editedTask != null;

        Task taskToUpdate = internalList.get(index);
        if (!taskToUpdate.equals(editedTask) && internalList.contains(editedTask)) {
            throw new DuplicateTaskException();
        }

        taskToUpdate.resetData(editedTask);
        // TODO: The code below is just a workaround to notify observers of the
        // updated task.
        // The right way is to implement observable properties in the Task
        // class.
        // Then, TaskCard should then bind its text labels to those observable
        // properties.
        internalList.set(index, taskToUpdate);
    }

    /**
     * Removes the equivalent task from the list.
     *
     * @throws TaskNotFoundException
     *             if no such task could be found in the list.
     */
    public boolean remove(ReadOnlyTask toRemove) throws TaskNotFoundException {
        assert toRemove != null;
        final boolean taskFoundAndDeleted = internalList.remove(toRemove);
        if (!taskFoundAndDeleted) {
            throw new TaskNotFoundException();
        }
        return taskFoundAndDeleted;
    }

    public void setTasks(UniqueTaskList replacement) {
        this.internalList.setAll(replacement.internalList);
    }

    public void setTasks(List<? extends ReadOnlyTask> tasks) throws DuplicateTaskException {
        final UniqueTaskList replacement = new UniqueTaskList();
        for (final ReadOnlyTask task : tasks) {
            replacement.add(new Task(task));
        }
        setTasks(replacement);
    }

    public UnmodifiableObservableList<Task> asObservableList() {
        return new UnmodifiableObservableList<>(internalList);
    }

    public ObservableList<Task> getInternalList() {
        return internalList;
    }

    @Override
    public Iterator<Task> iterator() {
        return internalList.iterator();
    }

    @Override
    public boolean equals(Object other) {
        return other == this // short circuit if same object
                || (other instanceof UniqueTaskList // instanceof handles nulls
                        && this.internalList.equals(((UniqueTaskList) other).internalList));
    }

    @Override
    public int hashCode() {
        return internalList.hashCode();
    }

    /**
     * Signals that an operation would have violated the 'no duplicates'
     * property of the list.
     */
    public static class DuplicateTaskException extends DuplicateDataException {
        protected DuplicateTaskException() {
            super("Operation would result in duplicate tasks");
        }
    }

    /**
     * Signals that an operation targeting a specified task in the list would
     * fail because there is no such matching task in the list.
     */
    public static class TaskNotFoundException extends Exception {
    }

}

@@ -287,16 +287,66 @@
 ception;
+%0Aimport seedu.address.commons.util.CollectionUtil;
 %0A%0A/**%0A *
@@ -420,19 +420,16 @@
 does not
-%0A *
  allow n
@@ -514,16 +514,69 @@
 Object)%0A
+ * @see CollectionUtil#elementsAreUnique(Collection)%0A
  */%0Apubl
@@ -800,23 +800,16 @@
 he given
-%0A     *
  argumen
@@ -1041,35 +1041,16 @@
 xception
-%0A     *            
  if the 
@@ -1102,35 +1102,16 @@
 k in the
-%0A     *            
  list.%0A 
@@ -1414,23 +1414,16 @@
 ex%7D with
-%0A     *
  %7B@code 
@@ -1479,35 +1479,16 @@
 xception
-%0A     *            
  if upda
@@ -1532,35 +1532,16 @@
 sk to be
-%0A     *            
  equival
@@ -1546,16 +1546,28 @@
 alent to
+%0A     *     
  another
@@ -1634,35 +1634,16 @@
 xception
-%0A     *            
  if %7B@co
@@ -2143,27 +2143,16 @@
 s of the
-%0A        //
  updated
@@ -2232,27 +2232,16 @@
 the Task
-%0A        //
  class.%0A
@@ -2318,27 +2318,16 @@
 servable
-%0A        //
  propert
@@ -2490,27 +2490,8 @@
 tion
-%0A     *            
  if 
@@ -3410,32 +3410,36 @@
 nalList);%0A    %7D%0A
+    
 %0A    public Obse
@@ -3824,24 +3824,16 @@
         
-        
 && this.
@@ -3852,16 +3852,33 @@
 .equals(
+%0A                
 ((Unique
@@ -4086,23 +4086,16 @@
 licates'
-%0A     *
  propert
@@ -4408,23 +4408,16 @@
 st would
-%0A     *
  fail be
@@ -4421,16 +4421,23 @@
  because
+%0A     *
  there i
@@ -4550,14 +4550,10 @@
 on %7B
-%0A    
 %7D%0A%0A%7D%0A
+%0A

Update UniqueTaskList.java