package seedu.jimi.model;

import javafx.collections.ObservableList;
import seedu.jimi.model.tag.Tag;
import seedu.jimi.model.tag.UniqueTagList;
import seedu.jimi.model.task.FloatingTask;
import seedu.jimi.model.task.ReadOnlyTask;
import seedu.jimi.model.task.UniqueTaskList;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Wraps all data at the address-book level
 * Duplicates are not allowed (by .equals comparison)
 */
public class TaskBook implements ReadOnlyTaskBook {

    private final UniqueTaskList<ReadOnlyTask> tasks;
    private final UniqueTagList tags;

    {
        tasks = new UniqueTaskList();
        tags = new UniqueTagList();
    }

    public TaskBook() {}

    /**
     * Tasks and Tags are copied into this addressbook
     */
    public TaskBook(ReadOnlyTaskBook toBeCopied) {
        this(toBeCopied.getUniqueTaskList(), toBeCopied.getUniqueTagList());
    }

    /**
     * Tasks and Tags are copied into this addressbook
     */
    public TaskBook(UniqueTaskList tasks, UniqueTagList tags) {
        resetData(tasks.getInternalList(), tags.getInternalList());
    }

    public static ReadOnlyTaskBook getEmptyTaskBook() {
        return new TaskBook();
    }

//// list overwrite operations

    public ObservableList<ReadOnlyTask> getTasks() {
        return tasks.getInternalList();
    }

    public void setReadOnlyTasks(List<ReadOnlyTask> tasks) {
        this.tasks.getInternalList().setAll(tasks);
    }

    public void setTags(Collection<Tag> tags) {
        this.tags.getInternalList().setAll(tags);
    }

    public void resetData(Collection<? extends ReadOnlyTask> newTasks, Collection<Tag> newTags) {
        setReadOnlyTasks(newTasks.stream().map(FloatingTask::new).collect(Collectors.toList()));
        setTags(newTags);
    }

    public void resetData(ReadOnlyTaskBook newData) {
        resetData(newData.getTaskList(), newData.getTagList());
    }

//// task-level operations

    /**
     * Adds a task to the address book.
     * Also checks the new task's tags and updates {@link #tags} with any new tags found,
     * and updates the Tag objects in the task to point to those in {@link #tags}.
     *
     * @throws UniqueTaskList.DuplicateTaskException if an equivalent task already exists.
     */
    public void addTask(ReadOnlyTask p) throws UniqueTaskList.DuplicateTaskException {
        syncTagsWithMasterList((ReadOnlyTask) p);
        tasks.add(p);
    }

    /**
     * Ensures that every tag in this task:
     *  - exists in the master list {@link #tags}
     *  - points to a Tag object in the master list
     */
    private void syncTagsWithMasterList(ReadOnlyTask task) {
        final UniqueTagList taskTags = task.getTags();
        tags.mergeFrom(taskTags);

        // Create map with values = tag object references in the master list
        final Map<Tag, Tag> masterTagObjects = new HashMap<>();
        for (Tag tag : tags) {
            masterTagObjects.put(tag, tag);
        }

        // Rebuild the list of task tags using references from the master list
        final Set<Tag> commonTagReferences = new HashSet<>();
        for (Tag tag : taskTags) {
            commonTagReferences.add(masterTagObjects.get(tag));
        }
        task.setTags(new UniqueTagList(commonTagReferences));
    }

    public boolean removeTask(ReadOnlyTask key) throws UniqueTaskList.TaskNotFoundException {
        if (tasks.remove(key)) {
            return true;
        } else {
            throw new UniqueTaskList.TaskNotFoundException();
        }
    }
    
    public void editTask(int targetIndex, ReadOnlyTask newTask) {
        tasks.edit(targetIndex, newTask);
    }

    public void completeTask(ReadOnlyTask taskToComplete, boolean isComplete) {
        tasks.complete(taskToComplete, isComplete);
    }
//// tag-level operations

    public void addTag(Tag t) throws UniqueTagList.DuplicateTagException {
        tags.add(t);
    }

//// util methods

    @Override
    public String toString() {
        return tasks.getInternalList().size() + " tasks, " + tags.getInternalList().size() +  " tags";
        // TODO: refine later
    }

    @Override
    public List<ReadOnlyTask> getTaskList() {
        return Collections.unmodifiableList(tasks.getInternalList());
    }

    @Override
    public List<Tag> getTagList() {
        return Collections.unmodifiableList(tags.getInternalList());
    }

    @Override
    public UniqueTaskList getUniqueTaskList() {
        return this.tasks;
    }

    @Override
    public UniqueTagList getUniqueTagList() {
        return this.tags;
    }


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

    @Override
    public int hashCode() {
        // use this method for custom fields hashing instead of implementing your own
        return Objects.hash(tasks, tags);
    }
}

@@ -35,36 +35,342 @@
 java
-fx.collections.ObservableLis
+.util.ArrayList;%0Aimport java.util.Collection;%0Aimport java.util.Collections;%0Aimport java.util.HashMap;%0Aimport java.util.HashSet;%0Aimport java.util.List;%0Aimport java.util.Map;%0Aimport java.util.Objects;%0Aimport java.util.Set;%0Aimport java.util.stream.Collectors;%0A%0Aimport javafx.collections.ObservableList;%0Aimport seedu.jimi.model.event.Even
 t;%0Ai
@@ -440,24 +440,67 @@
 queTagList;%0A
+import seedu.jimi.model.task.DeadlineTask;%0A
 import seedu
@@ -569,24 +569,24 @@
 adOnlyTask;%0A
+
 import seedu
@@ -623,65 +623,8 @@
 t;%0A%0A
-import java.util.*;%0Aimport java.util.stream.Collectors;%0A%0A
 /**%0A
@@ -1961,94 +1961,463 @@
     
-setReadOnlyTasks(newTasks.stream().map(Floating
+ArrayList%3CReadOnlyTask%3E newList = new ArrayList%3CReadOnlyTask%3E();%0A        for (ReadOnlyTask t : newTasks) %7B%0A            if (t instanceof DeadlineTask) %7B%0A                newList.add(new DeadlineTask((Deadline
 Task
-::new).collect(Collectors.toList())
+) t));%0A            %7D else if (t instanceof Event) %7B%0A                newList.add(new Event((Event) t));%0A            %7D else %7B%0A                newList.add(new FloatingTask((FloatingTask) t));%0A            %7D%0A        %7D%0A        setReadOnlyTasks(newList
 );%0A 

Fix crap in TaskBook that only instantiates FTs