package jfdi.storage.data;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Objects;
import java.util.TreeSet;

import jfdi.storage.Constants;
import jfdi.storage.exceptions.InvalidIdException;
import jfdi.storage.exceptions.InvalidTaskParametersException;
import jfdi.storage.exceptions.NoAttributesChangedException;

public class TaskAttributes {

    private Integer id = null;
    private String description = null;
    private LocalDateTime startDateTime = null;
    private LocalDateTime endDateTime = null;
    private HashSet<String> tags = new HashSet<String>();
    private TreeSet<Duration> reminders = new TreeSet<Duration>();
    private boolean isCompleted = false;

    public TaskAttributes() {
    }

    @SuppressWarnings("unchecked")
    public TaskAttributes(Task task) {
        this.id = task.getId();
        this.description = task.getDescription();
        this.startDateTime = task.getStartDateTime();
        this.endDateTime = task.getEndDateTime();
        this.isCompleted = task.isCompleted();
        if (task.getTags() instanceof HashSet<?>) {
            this.tags = (HashSet<String>) task.getTags().clone();
        }
        if (task.getReminders() instanceof TreeSet<?>) {
            this.reminders = (TreeSet<Duration>) task.getReminders().clone();
        }
    }

    public Integer getId() {
        return id;
    }

    /**
     * This method should only be used internally by the database/test.
     *
     * @param id
     *            the id of the task
     */
    public void setId(Integer id) {
        this.id = id;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public LocalDateTime getStartDateTime() {
        return startDateTime;
    }

    public void setStartDateTime(LocalDateTime startDateTime) {
        this.startDateTime = startDateTime;
    }

    public LocalDateTime getEndDateTime() {
        return endDateTime;
    }

    public void setEndDateTime(LocalDateTime endDateTime) {
        this.endDateTime = endDateTime;
    }

    public HashSet<String> getTags() {
        return tags;
    }

    public void setTags(String... tags) {
        this.tags = new HashSet<String>();
        for (String tag : tags) {
            assert tag != null;
            this.tags.add(tag);
        }
    }

    public boolean addTag(String tag) {
        HashSet<String> tags = this.getTags();
        return tags.add(tag);
    }

    public boolean removeTag(String tag) {
        HashSet<String> tags = this.getTags();
        return tags.remove(tag);
    }

    public boolean hasTag(String tag) {
        HashSet<String> tags = this.getTags();
        return tags.contains(tag);
    }

    public TreeSet<Duration> getReminders() {
        return reminders;
    }

    public void setReminders(Duration... reminders) {
        this.reminders = new TreeSet<Duration>();
        for (Duration reminder : reminders) {
            assert reminder != null;
            this.reminders.add(reminder);
        }
    }

    public boolean addReminder(Duration reminder) {
        TreeSet<Duration> reminders = this.getReminders();
        return reminders.add(reminder);
    }

    public boolean isCompleted() {
        return isCompleted;
    }

    public void setCompleted(boolean isCompleted) {
        this.isCompleted = isCompleted;
    }

    public void save() throws InvalidTaskParametersException, NoAttributesChangedException,
            InvalidIdException {
        validateAttributes();
        TaskDb.createOrUpdate(this);
    }

    public Task toEntity() {
        return new Task(id, description, startDateTime, endDateTime, tags, reminders);
    }

    private void validateAttributes() throws InvalidTaskParametersException {
        if (description == null) {
            throw new InvalidTaskParametersException(Constants.MESSAGE_MISSING_DESCRIPTION);
        }
    }

    public boolean equalTo(Task task) {
        return Objects.equals(this.id, task.getId())
                && Objects.equals(this.description, task.getDescription())
                && Objects.equals(this.startDateTime, task.getStartDateTime())
                && Objects.equals(this.endDateTime, task.getEndDateTime())
                && Objects.equals(this.tags, task.getTags())
                && Objects.equals(this.reminders, task.getReminders())
                && this.isCompleted == task.isCompleted();
    }

}

@@ -3471,32 +3471,726 @@
 mpleted;%0A    %7D%0A%0A
+    /**%0A     * This method saves the attributes object into a data store object in the%0A     * database. The attributes are first validated before the data store object%0A     * is either created or updated.%0A     *%0A     * @throws InvalidTaskParametersException%0A     *             if the attributes object contains an invalid attribute (e.g.%0A     *             missing description)%0A     * @throws NoAttributesChangedException%0A     *             if the save operation does not change the data store object%0A     *             in any way%0A     * @throws InvalidIdException%0A     *             if the ID stored in the attributes object is invalid (e.g.%0A     *             null or does not exist)%0A     */%0A
     public void 

Document the TaskAttributes save method