package seedu.emeraldo.model;

import javafx.collections.transformation.FilteredList;
import seedu.emeraldo.commons.core.ComponentManager;
import seedu.emeraldo.commons.core.LogsCenter;
import seedu.emeraldo.commons.core.UnmodifiableObservableList;
import seedu.emeraldo.commons.events.model.EmeraldoChangedEvent;
import seedu.emeraldo.commons.util.StringUtil;
import seedu.emeraldo.model.task.ReadOnlyTask;
import seedu.emeraldo.model.task.Task;
import seedu.emeraldo.model.task.UniquePersonList;
import seedu.emeraldo.model.task.UniquePersonList.TaskNotFoundException;

import java.util.Set;
import java.util.logging.Logger;

/**
 * Represents the in-memory model of the address book data.
 * All changes to any model should be synchronized.
 */
public class ModelManager extends ComponentManager implements Model {
    private static final Logger logger = LogsCenter.getLogger(ModelManager.class);

    private final Emeraldo addressBook;
    private final FilteredList<Task> filteredPersons;

    /**
     * Initializes a ModelManager with the given AddressBook
     * AddressBook and its variables should not be null
     */
    public ModelManager(Emeraldo src, UserPrefs userPrefs) {
        super();
        assert src != null;
        assert userPrefs != null;

        logger.fine("Initializing with address book: " + src + " and user prefs " + userPrefs);

        addressBook = new Emeraldo(src);
        filteredPersons = new FilteredList<>(addressBook.getTasks());
    }

    public ModelManager() {
        this(new Emeraldo(), new UserPrefs());
    }

    public ModelManager(ReadOnlyEmeraldo initialData, UserPrefs userPrefs) {
        addressBook = new Emeraldo(initialData);
        filteredPersons = new FilteredList<>(addressBook.getTasks());
    }

    @Override
    public void resetData(ReadOnlyEmeraldo newData) {
        addressBook.resetData(newData);
        indicateAddressBookChanged();
    }

    @Override
    public ReadOnlyEmeraldo getAddressBook() {
        return addressBook;
    }

    /** Raises an event to indicate the model has changed */
    private void indicateAddressBookChanged() {
        raise(new EmeraldoChangedEvent(addressBook));
    }

    @Override
    public synchronized void deleteTask(ReadOnlyTask target) throws TaskNotFoundException {
        addressBook.removePerson(target);
        indicateAddressBookChanged();
    }

    @Override
    public synchronized void addPerson(Task person) throws UniquePersonList.DuplicateTaskException {
        addressBook.addPerson(person);
        updateFilteredListToShowAll();
        indicateAddressBookChanged();
    }
    @Override
    public synchronized void editTask(ReadOnlyTask target) throws TaskNotFoundException {
        addressBook.editTask(target);
        indicateAddressBookChanged();
    }
    //=========== Filtered Person List Accessors ===============================================================

    @Override
    public UnmodifiableObservableList<ReadOnlyTask> getFilteredPersonList() {
        return new UnmodifiableObservableList<>(filteredPersons);
    }

    @Override
    public void updateFilteredListToShowAll() {
        filteredPersons.setPredicate(null);
    }

    @Override
    public void updateFilteredTaskList(Set<String> keywords){
        updateFilteredTaskList(new PredicateExpression(new NameQualifier(keywords)));
    }

    private void updateFilteredTaskList(Expression expression) {
        filteredPersons.setPredicate(expression::satisfies);
    }

    //========== Inner classes/interfaces used for filtering ==================================================

    interface Expression {
        boolean satisfies(ReadOnlyTask person);
        String toString();
    }

    private class PredicateExpression implements Expression {

        private final Qualifier qualifier;

        PredicateExpression(Qualifier qualifier) {
            this.qualifier = qualifier;
        }

        @Override
        public boolean satisfies(ReadOnlyTask person) {
            return qualifier.run(person);
        }

        @Override
        public String toString() {
            return qualifier.toString();
        }
    }

    interface Qualifier {
        boolean run(ReadOnlyTask person);
        String toString();
    }

    private class NameQualifier implements Qualifier {
        private Set<String> nameKeyWords;

        NameQualifier(Set<String> nameKeyWords) {
            this.nameKeyWords = nameKeyWords;
        }

        @Override
        public boolean run(ReadOnlyTask person) {
            return nameKeyWords.stream()
                    .filter(keyword -> StringUtil.containsIgnoreCase(person.getDescription().fullName, keyword))
                    .findAny()
                    .isPresent();
        }

        @Override
        public String toString() {
            return "name=" + String.join(", ", nameKeyWords);
        }
    }

}

@@ -922,27 +922,24 @@
 meraldo 
-addressBook
+emeraldo
 ;%0A    pr
@@ -1362,35 +1362,32 @@
 );%0A%0A        
-addressBook
+emeraldo
  = new Emera
@@ -1437,35 +1437,32 @@
 teredList%3C%3E(
-addressBook
+emeraldo
 .getTasks())
@@ -1637,27 +1637,24 @@
         
-addressBook
+emeraldo
  = new E
@@ -1720,27 +1720,24 @@
 dList%3C%3E(
-addressBook
+emeraldo
 .getTask
@@ -1825,27 +1825,24 @@
         
-addressBook
+emeraldo
 .resetDa
@@ -1975,27 +1975,24 @@
  return 
-addressBook
+emeraldo
 ;%0A    %7D%0A
@@ -2140,27 +2140,24 @@
 edEvent(
-addressBook
+emeraldo
 ));%0A    
@@ -2273,27 +2273,24 @@
         
-addressBook
+emeraldo
 .removeP
@@ -2472,27 +2472,24 @@
         
-addressBook
+emeraldo
 .addPers
@@ -2699,19 +2699,16 @@
     
-addressBook
+emeraldo
 .edi

Rename addressBook to emeraldo