package jfdi.logic.commands;

import jfdi.logic.events.RescheduleTaskDoneEvent;
import jfdi.logic.events.RescheduleTaskFailedEvent;
import jfdi.logic.interfaces.Command;
import jfdi.storage.apis.TaskAttributes;
import jfdi.storage.apis.TaskDb;
import jfdi.storage.exceptions.DuplicateTaskException;
import jfdi.storage.exceptions.InvalidIdException;
import jfdi.storage.exceptions.InvalidTaskParametersException;
import jfdi.storage.exceptions.NoAttributesChangedException;
import jfdi.ui.UI;

import java.time.LocalDateTime;

/**
 * @author Liu Xinan
 */
public class RescheduleTaskCommand extends Command {

    private int screenId;
    private LocalDateTime startDateTime;
    private LocalDateTime endDateTime;
    private LocalDateTime oldStartDateTime;
    private LocalDateTime oldEndDateTime;


    private RescheduleTaskCommand(Builder builder) {
        this.screenId = builder.screenId;
        this.startDateTime = builder.startDateTime;
        this.endDateTime = builder.endDateTime;
    }

    public static class Builder {

        int screenId;
        LocalDateTime startDateTime;
        LocalDateTime endDateTime;

        public Builder setId(int screenId) {
            this.screenId = screenId;
            return this;
        }

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

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

        public RescheduleTaskCommand build() {
            return new RescheduleTaskCommand(this);
        }

    }

    @Override
    public void execute() {
        int taskId = UI.getInstance().getTaskId(screenId);

        try {
            TaskAttributes task = TaskDb.getInstance().getById(taskId);

            oldStartDateTime = task.getStartDateTime();
            oldEndDateTime = task.getEndDateTime();

            task.setStartDateTime(startDateTime);
            task.setEndDateTime(endDateTime);
            task.save();

            pushToUndoStack();
            eventBus.post(new RescheduleTaskDoneEvent(task));
        } catch (InvalidIdException e) {
            eventBus.post(new RescheduleTaskFailedEvent(screenId, startDateTime, endDateTime,
                RescheduleTaskFailedEvent.Error.NON_EXISTENT_ID));
        } catch (InvalidTaskParametersException e) {
            // Should not happen
            assert false;
        } catch (NoAttributesChangedException e) {
            eventBus.post(new RescheduleTaskFailedEvent(screenId, startDateTime, endDateTime,
                RescheduleTaskFailedEvent.Error.NO_CHANGES));
        } catch (DuplicateTaskException e) {
            eventBus.post(new RescheduleTaskFailedEvent(screenId, startDateTime, endDateTime,
                RescheduleTaskFailedEvent.Error.DUPLICATED_TASK));
        }
    }

    @Override
    public void undo() {
        int taskId = UI.getInstance().getTaskId(screenId);

        try {
            TaskAttributes task = TaskDb.getInstance().getById(taskId);

            task.setStartDateTime(oldStartDateTime);
            task.setEndDateTime(oldEndDateTime);
            task.save();

            pushToRedoStack();
        } catch (InvalidIdException | InvalidTaskParametersException
            | NoAttributesChangedException | DuplicateTaskException e) {
            assert false;
        }
    }

}

@@ -487,16 +487,43 @@
 ui.UI;%0A%0A
+import java.time.Duration;%0A
 import j
@@ -546,16 +546,16 @@
 teTime;%0A
-
 %0A/**%0A * 
@@ -652,24 +652,67 @@
 t screenId;%0A
+    private LocalDateTime shiftedDateTime;%0A
     private 
@@ -954,32 +954,88 @@
 ilder.screenId;%0A
+        this.shiftedDateTime = builder.shiftedDateTime;%0A
         this.sta
@@ -1174,32 +1174,71 @@
   int screenId;%0A
+        LocalDateTime shiftedDateTime;%0A
         LocalDat
@@ -1405,32 +1405,195 @@
 his;%0A        %7D%0A%0A
+        public Builder setShiftedDateTime(LocalDateTime shiftedDateTime) %7B%0A            this.shiftedDateTime = shiftedDateTime;%0A            return this;%0A        %7D%0A%0A
         public B
@@ -2289,24 +2289,131 @@
 ateTime();%0A%0A
+            if (shiftedDateTime != null) %7B%0A                shiftStartAndEndDateTimes(task);%0A            %7D%0A%0A
             
@@ -3838,32 +3838,32 @@
 kException e) %7B%0A
-
             asse
@@ -3889,10 +3889,995 @@
 %0A    %7D%0A%0A
+    private void shiftStartAndEndDateTimes(TaskAttributes task) %7B%0A        assert shiftedDateTime != null;%0A        LocalDateTime taskStart = task.getStartDateTime();%0A        LocalDateTime taskEnd = task.getEndDateTime();%0A%0A        // Set floating task to point task%0A        if(taskStart == null && taskEnd == null) %7B%0A            startDateTime = shiftedDateTime;%0A            endDateTime = null;%0A%0A        // Shift point task%0A        %7D else if (taskStart != null && taskEnd == null) %7B%0A            startDateTime = shiftedDateTime;%0A            endDateTime = null;%0A%0A        // Shift deadline task%0A        %7D else if (taskStart == null && taskEnd != null) %7B%0A            startDateTime = null;%0A            endDateTime = shiftedDateTime;%0A%0A        // Shift event, preserving duration%0A        %7D else %7B%0A            Duration eventDuration = Duration.between(taskStart, taskEnd);%0A            startDateTime = shiftedDateTime;%0A            endDateTime = startDateTime.plus(eventDuration);%0A        %7D%0A    %7D%0A%0A
 %7D%0A

Change RescheduleTaskCommand to enable shifting of current datetimes