diff --git a/.gitignore b/.gitignore
index f69985ef1f..a3bbcefcae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,5 +13,7 @@ src/main/resources/docs/
*.iml
bin/
-/text-ui-test/ACTUAL.txt
-text-ui-test/EXPECTED-UNIX.TXT
+/text-duke.ui-test/ACTUAL.txt
+text-duke.ui-test/EXPECTED-UNIX.TXT
+
+main.jar
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000000..fdce4e149d
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,63 @@
+
+plugins {
+ id 'java'
+ id 'application'
+ id 'checkstyle'
+ id 'com.github.johnrengelman.shadow' version '5.1.0'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation 'org.jetbrains:annotations:20.1.0'
+ String javaFxVersion = '11'
+
+ implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win'
+ implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac'
+ implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux'
+ implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win'
+ implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac'
+ implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux'
+ implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win'
+ implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac'
+ implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux'
+ implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win'
+ implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac'
+ implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux'
+
+ testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0'
+ testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0'
+}
+
+test {
+ useJUnitPlatform()
+
+ testLogging {
+ events "passed", "skipped", "failed"
+
+ showExceptions true
+ exceptionFormat "full"
+ showCauses true
+ showStackTraces true
+ showStandardStreams = false
+ }
+}
+
+application {
+ mainClassName = "duke.Launcher"
+}
+
+shadowJar {
+ archiveBaseName = "duke"
+ archiveClassifier = null
+}
+
+checkstyle {
+ toolVersion = '8.29'
+}
+
+run{
+ standardInput = System.in
+}
\ No newline at end of file
diff --git a/config/checkstyle/Checkstyle.xml b/config/checkstyle/Checkstyle.xml
new file mode 100644
index 0000000000..7951f3a6c4
--- /dev/null
+++ b/config/checkstyle/Checkstyle.xml
@@ -0,0 +1,398 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/checkstyle/Suppressions.xml b/config/checkstyle/Suppressions.xml
new file mode 100644
index 0000000000..135ea49ee0
--- /dev/null
+++ b/config/checkstyle/Suppressions.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index 8077118ebe..aa8dd6c1ab 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -2,28 +2,184 @@
## Features
-### Feature-ABC
+#### Interactive Task List
-Description of the feature.
+Why keep track of your tasks in your head when a bot can do it for you?
+Duke separates your tasks into three categories: todo, deadline and event. The Duke task list allows you to add, delete, find, view and update tasks. Then, when you're done with a task, simply mark it as done.
-### Feature-XYZ
+## Usage
-Description of the feature.
+### Viewing Help: `help`
-## Usage
+Displays a message which contains all the commands of the Duke bot and their usage.
+
+Example of usage:
+
+`help`
+
+Expected outcome:
+
+A message that lists all the commands of the bot.
+
+
+### Viewing tasks: `list`
+
+Lists all the tasks in the task list.
+
+Example of usage:
+
+`list`
+
+Expected outcome:
+
+A message that lists all the tasks in the user's task list.
+
+```
+Here are the tasks in your list:
+ 1. [T][] borrow book
+ 2. [E][] family dinner (at: Sep 12 2021)
+ 3. [D][] assignment 1 (by: Sep 14 2021)
+```
+
+### Adding a todo: `todo`
+
+Adds a todo task to the task list.
+
+Format:
+
+`todo `
+
+Example of usage:
+
+`todo borrow book`
+
+Expected outcome:
+
+A todo is added to the task list and a message is returned to confirm the addition of the todo task.
+
+```
+Got it. I've added this task:
+ [T][] borrow book
+Now you have 1 task in the list.
+```
+
+### Adding a deadline: `deadline`
+
+Adds a deadline task to the task list.
+
+Format:
+
+`deadline /by `
+
+Example of usage:
+
+`deadline assignment 1 /by 2021-09-14`
+
+Expected outcome:
+
+A deadline is added to the task list and a message is returned to confirm the addition of the deadline task.
+
+```
+Got it. I've added this task:
+ [D][] assignment 1 (by: Sep 14 2021)
+Now you have 1 task in the list.
+```
+
+### Adding an event: `event`
+
+Adds an event task to the task list.
-### `Keyword` - Describe action
+Format:
-Describe the action and its outcome.
+`event /at `
Example of usage:
-`keyword (optional arguments)`
+`event family dinner /by 2021-09-12`
Expected outcome:
-Description of the outcome.
+An event is added to the task list and a message is returned to confirm the addition of the event task.
```
-expected output
+Got it. I've added this task:
+ [E][] family dinner (by: Sep 12 2021)
+Now you have 1 task in the list.
```
+
+### Finding tasks: `find`
+
+Finds all the tasks that contain the user's search query.
+
+Format:
+
+`find `
+
+Example of usage:
+
+`find book`
+
+Expected outcome:
+
+A message that lists all the tasks that fit the user's search query.
+
+```
+Here are the matching tasks in your list:
+ [T][] borrow book
+```
+
+### Deleting a task: `delete`
+
+Deletes the specified task from the task list and returns a message to confirm the deletion.
+
+Format:
+
+`delete `
+
+Example of usage:
+
+`delete 1`
+
+Expected outcome:
+
+The task is deleted from the task list and a message is returned to confirm the deletion.
+
+```
+Noted. I've removed the this task:
+ [T][] borrow book
+Now you have 0 tasks in the list.
+```
+
+### Updating a task: `delete`
+
+Updates the specified task and returns a message to confirm the update.
+
+Format:
+
+`update `
+
+Example of usage:
+
+`udpate 1 assignment 1 and 2 /by 2021-09-20`
+
+Expected outcome:
+
+The task is updated and a message is returned to confirm the update.
+
+```
+Your task has been updated!
+ [D][] assignment 1 and 2 (by: Sep 20 2021)
+```
+
+### Exiting: `exit` or `bye`
+
+Closes the application
+
+
+Example of usage:
+
+`exit`
+
+Expected outcome:
+
+The application closes
diff --git a/docs/Ui.png b/docs/Ui.png
new file mode 100644
index 0000000000..807305c7a6
Binary files /dev/null and b/docs/Ui.png differ
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000000..259a24e4d2
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-tactile
\ No newline at end of file
diff --git a/gradle.bat b/gradle.bat
new file mode 100644
index 0000000000..a14a3d051d
--- /dev/null
+++ b/gradle.bat
@@ -0,0 +1,103 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..f3d88b1c2f
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..c14115d7e0
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
\ No newline at end of file
diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java
deleted file mode 100644
index 5d313334cc..0000000000
--- a/src/main/java/Duke.java
+++ /dev/null
@@ -1,10 +0,0 @@
-public class Duke {
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- }
-}
diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java
new file mode 100644
index 0000000000..1b63caff93
--- /dev/null
+++ b/src/main/java/duke/Duke.java
@@ -0,0 +1,40 @@
+package duke;
+
+import duke.command.Command;
+import duke.exception.DukeException;
+import duke.parser.Parser;
+import duke.storage.Storage;
+import duke.task.TaskList;
+
+
+public class Duke {
+
+ private Storage storage;
+ private TaskList taskList;
+
+ /**
+ * Constructor for Duke object.
+ *
+ * @param filePath the file path of the document which stores the users tasks.
+ */
+ public Duke(String filePath) {
+ this.storage = new Storage(filePath);
+ this.taskList = this.storage.load();
+ }
+
+ /**
+ * Gets the program output give the user input.
+ *
+ * @param input User input
+ * @return Duke's response
+ */
+ public String getResponse(String input) {
+ try {
+ Command command = Parser.parse(input);
+ return command.execute(this.taskList, input, this.storage);
+ } catch (DukeException e) {
+ return e.getMessage();
+ }
+ }
+
+}
diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java
new file mode 100644
index 0000000000..e4ef6b4628
--- /dev/null
+++ b/src/main/java/duke/Launcher.java
@@ -0,0 +1,12 @@
+package duke;
+
+import javafx.application.Application;
+
+/**
+ * A launcher class to workaround classpath issues.
+ */
+public class Launcher {
+ public static void main(String[] args) {
+ Application.launch(Main.class, args);
+ }
+}
diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java
new file mode 100644
index 0000000000..1564a08e79
--- /dev/null
+++ b/src/main/java/duke/Main.java
@@ -0,0 +1,33 @@
+package duke;
+
+import java.io.IOException;
+
+import duke.gui.MainWindow;
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.AnchorPane;
+import javafx.stage.Stage;
+
+/**
+ * A GUI for Duke using FXML.
+ */
+public class Main extends Application {
+
+ private Duke duke = new Duke("Data\\taskList.txt");
+
+ @Override
+ public void start(Stage stage) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml"));
+ AnchorPane ap = fxmlLoader.load();
+ Scene scene = new Scene(ap);
+ stage.setScene(scene);
+ stage.setTitle("Duke - your personal task list");
+ fxmlLoader.getController().setDuke(duke);
+ stage.show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/duke/command/Command.java b/src/main/java/duke/command/Command.java
new file mode 100644
index 0000000000..6e36775740
--- /dev/null
+++ b/src/main/java/duke/command/Command.java
@@ -0,0 +1,25 @@
+package duke.command;
+
+import duke.exception.DukeException;
+import duke.storage.Storage;
+import duke.task.TaskList;
+
+/**
+ * Represents a command for Duke bot.
+ *
+ * @author Li Ming Gao Rickie
+ */
+public abstract class Command {
+
+ /**
+ * Executes the command.
+ *
+ * @param taskList the list of user tasks.
+ * @param input the user's input.
+ * @param storage the storage which holds all the user's data.
+ * @return a string that states the outcome of the execution of the command.
+ * @throws DukeException if input is invalid.
+ */
+ public abstract String execute(TaskList taskList, String input, Storage storage) throws DukeException;
+
+}
diff --git a/src/main/java/duke/command/DeadlineCommand.java b/src/main/java/duke/command/DeadlineCommand.java
new file mode 100644
index 0000000000..f85dcc2231
--- /dev/null
+++ b/src/main/java/duke/command/DeadlineCommand.java
@@ -0,0 +1,30 @@
+package duke.command;
+
+import duke.exception.DukeException;
+import duke.storage.Storage;
+import duke.task.Deadline;
+import duke.task.TaskList;
+import duke.ui.Ui;
+
+public class DeadlineCommand extends Command {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String execute(TaskList taskList, String input, Storage storage) throws DukeException {
+ try {
+ if (input.replaceAll("\\s", "").length() == 8) {
+ throw new DukeException(DukeException.Type.EmptyDeadline);
+ } else {
+ String[] processedInput = input.substring(9).split(" /by ", 2);
+ Deadline d = new Deadline(processedInput[0], processedInput[1]);
+ storage.addTask(d);
+ taskList.addTask(d);
+ return Ui.printTaskCreated(d, taskList.getNumTasks());
+ }
+ } catch (DukeException e) {
+ return e.getMessage();
+ }
+ }
+}
diff --git a/src/main/java/duke/command/DeleteCommand.java b/src/main/java/duke/command/DeleteCommand.java
new file mode 100644
index 0000000000..e1a69a08ba
--- /dev/null
+++ b/src/main/java/duke/command/DeleteCommand.java
@@ -0,0 +1,48 @@
+package duke.command;
+
+import duke.exception.DukeException;
+import duke.storage.Storage;
+import duke.task.Task;
+import duke.task.TaskList;
+import duke.ui.Ui;
+
+public class DeleteCommand extends Command {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String execute(TaskList taskList, String input, Storage storage) throws DukeException {
+ try {
+ if (input.replaceAll("\\s", "").length() == 6) {
+ throw new DukeException(DukeException.Type.EmptyDelete);
+ } else {
+ return processDelete(taskList, input, storage);
+ }
+ } catch (DukeException e) {
+ return e.getMessage();
+ }
+ }
+
+ /**
+ * Deletes the given task, saves task list to storage and returns confirmation string.
+ *
+ * @param taskList the list of user tasks.
+ * @param input the user's input;
+ * @param storage the storage which holds all the user's data.
+ * @return
+ */
+ private String processDelete(TaskList taskList, String input, Storage storage) {
+ try {
+ int index = Integer.parseInt(input.substring(7));
+ if (index < 1 || taskList.getNumTasks() < index) {
+ throw new DukeException(DukeException.Type.InvalidTaskNumber);
+ } else {
+ Task task = taskList.removeTask(index, storage);
+ return Ui.printTaskDeleted(task, taskList.getNumTasks());
+ }
+ } catch (DukeException e) {
+ return e.getMessage();
+ }
+ }
+}
diff --git a/src/main/java/duke/command/DoneCommand.java b/src/main/java/duke/command/DoneCommand.java
new file mode 100644
index 0000000000..0786389850
--- /dev/null
+++ b/src/main/java/duke/command/DoneCommand.java
@@ -0,0 +1,48 @@
+package duke.command;
+
+import duke.exception.DukeException;
+import duke.storage.Storage;
+import duke.task.Task;
+import duke.task.TaskList;
+import duke.ui.Ui;
+
+public class DoneCommand extends Command {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String execute(TaskList taskList, String input, Storage storage) {
+ try {
+ if (input.replaceAll("\\s", "").length() == 4) {
+ throw new DukeException(DukeException.Type.EmptyDone);
+ } else {
+ return processDone(taskList, input, storage);
+ }
+ } catch (DukeException e) {
+ return e.getMessage();
+ }
+ }
+
+ /**
+ * Marks the task as done, saves task list to storage and returns confirmation string.
+ *
+ * @param taskList the list of user tasks.
+ * @param input the user's input;
+ * @param storage the storage which holds all the user's data.
+ * @return
+ */
+ private String processDone(TaskList taskList, String input, Storage storage) {
+ try {
+ int index = Integer.parseInt(input.substring(5));
+ if (index < 1 || taskList.getNumTasks() < index) {
+ throw new DukeException(DukeException.Type.InvalidTaskNumber);
+ } else {
+ Task task = taskList.markTaskAsDone(index, storage);
+ return Ui.printTaskDone(task);
+ }
+ } catch (DukeException e) {
+ return e.getMessage();
+ }
+ }
+}
diff --git a/src/main/java/duke/command/EventCommand.java b/src/main/java/duke/command/EventCommand.java
new file mode 100644
index 0000000000..cd274c5cf7
--- /dev/null
+++ b/src/main/java/duke/command/EventCommand.java
@@ -0,0 +1,30 @@
+package duke.command;
+
+import duke.exception.DukeException;
+import duke.storage.Storage;
+import duke.task.Event;
+import duke.task.TaskList;
+import duke.ui.Ui;
+
+public class EventCommand extends Command {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String execute(TaskList taskList, String input, Storage storage) throws DukeException {
+ try {
+ if (input.replaceAll("\\s", "").length() == 5) {
+ throw new DukeException(DukeException.Type.EmptyEvent);
+ } else {
+ String[] processedInput = input.substring(6).split(" /at ", 2);
+ Event ev = new Event(processedInput[0], processedInput[1]);
+ storage.addTask(ev);
+ taskList.addTask(ev);
+ return Ui.printTaskCreated(ev, taskList.getNumTasks());
+ }
+ } catch (DukeException e) {
+ return e.getMessage();
+ }
+ }
+}
diff --git a/src/main/java/duke/command/ExitCommand.java b/src/main/java/duke/command/ExitCommand.java
new file mode 100644
index 0000000000..6f362b1c2e
--- /dev/null
+++ b/src/main/java/duke/command/ExitCommand.java
@@ -0,0 +1,19 @@
+package duke.command;
+
+import duke.exception.DukeException;
+import duke.storage.Storage;
+import duke.task.TaskList;
+import duke.ui.Ui;
+import javafx.application.Platform;
+
+public class ExitCommand extends Command {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String execute(TaskList taskList, String input, Storage storage) throws DukeException {
+ Platform.exit();
+ return Ui.printGoodbyeMessage();
+ }
+}
diff --git a/src/main/java/duke/command/FindCommand.java b/src/main/java/duke/command/FindCommand.java
new file mode 100644
index 0000000000..a7b530fbf3
--- /dev/null
+++ b/src/main/java/duke/command/FindCommand.java
@@ -0,0 +1,27 @@
+package duke.command;
+
+import duke.exception.DukeException;
+import duke.storage.Storage;
+import duke.task.TaskList;
+import duke.ui.Ui;
+
+public class FindCommand extends Command {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String execute(TaskList taskList, String input, Storage storage) throws DukeException {
+ try {
+ if (input.replaceAll("\\s", "").length() == 4) {
+ throw new DukeException(DukeException.Type.EmptyFind);
+ } else {
+ String query = input.substring(5).toLowerCase();
+ TaskList matchingTasks = taskList.findTasks(query);
+ return Ui.printTasksFound(matchingTasks);
+ }
+ } catch (DukeException e) {
+ return e.getMessage();
+ }
+ }
+}
diff --git a/src/main/java/duke/command/HelpCommand.java b/src/main/java/duke/command/HelpCommand.java
new file mode 100644
index 0000000000..e6fb3ece5b
--- /dev/null
+++ b/src/main/java/duke/command/HelpCommand.java
@@ -0,0 +1,17 @@
+package duke.command;
+
+import duke.exception.DukeException;
+import duke.storage.Storage;
+import duke.task.TaskList;
+import duke.ui.Ui;
+
+public class HelpCommand extends Command {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String execute(TaskList taskList, String input, Storage storage) throws DukeException {
+ return Ui.printHelpMessage();
+ }
+}
diff --git a/src/main/java/duke/command/ListCommand.java b/src/main/java/duke/command/ListCommand.java
new file mode 100644
index 0000000000..abb9e461d8
--- /dev/null
+++ b/src/main/java/duke/command/ListCommand.java
@@ -0,0 +1,17 @@
+package duke.command;
+
+import duke.exception.DukeException;
+import duke.storage.Storage;
+import duke.task.TaskList;
+import duke.ui.Ui;
+
+public class ListCommand extends Command {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String execute(TaskList taskList, String input, Storage storage) throws DukeException {
+ return Ui.printTaskList(taskList);
+ }
+}
diff --git a/src/main/java/duke/command/TodoCommand.java b/src/main/java/duke/command/TodoCommand.java
new file mode 100644
index 0000000000..7ec9feae59
--- /dev/null
+++ b/src/main/java/duke/command/TodoCommand.java
@@ -0,0 +1,29 @@
+package duke.command;
+
+import duke.exception.DukeException;
+import duke.storage.Storage;
+import duke.task.TaskList;
+import duke.task.Todo;
+import duke.ui.Ui;
+
+public class TodoCommand extends Command {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String execute(TaskList taskList, String input, Storage storage) throws DukeException {
+ try {
+ if (input.replaceAll("\\s", "").length() == 4) {
+ throw new DukeException(DukeException.Type.EmptyTodo);
+ } else {
+ Todo t = new Todo(input.substring(5));
+ storage.addTask(t);
+ taskList.addTask(t);
+ return Ui.printTaskCreated(t, taskList.getNumTasks());
+ }
+ } catch (DukeException e) {
+ return e.getMessage();
+ }
+ }
+}
diff --git a/src/main/java/duke/command/UpdateCommand.java b/src/main/java/duke/command/UpdateCommand.java
new file mode 100644
index 0000000000..053c895347
--- /dev/null
+++ b/src/main/java/duke/command/UpdateCommand.java
@@ -0,0 +1,40 @@
+package duke.command;
+
+import duke.exception.DukeException;
+import duke.storage.Storage;
+import duke.task.Task;
+import duke.task.TaskList;
+import duke.ui.Ui;
+
+public class UpdateCommand extends Command {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String execute(TaskList taskList, String input, Storage storage) throws DukeException {
+ try {
+ if (input.replaceAll("\\s", "").length() <= 7) {
+ throw new DukeException(DukeException.Type.EmptyUpdate);
+ } else {
+ return processUpdate(taskList, input, storage);
+ }
+ } catch (DukeException e) {
+ return e.getMessage();
+ }
+ }
+
+ private String processUpdate(TaskList taskList, String input, Storage storage) {
+ try {
+ int taskNum = Integer.parseInt(input.substring(7, 8));
+ if (taskList.getNumTasks() < taskNum) {
+ throw new DukeException(DukeException.Type.InvalidTaskNumber);
+ } else {
+ Task task = taskList.updateTask(taskNum, input.substring(9), storage);
+ return Ui.printTaskUpdated(task);
+ }
+ } catch (DukeException e) {
+ return e.getMessage();
+ }
+ }
+}
diff --git a/src/main/java/duke/exception/DukeException.java b/src/main/java/duke/exception/DukeException.java
new file mode 100644
index 0000000000..37cb204ea3
--- /dev/null
+++ b/src/main/java/duke/exception/DukeException.java
@@ -0,0 +1,66 @@
+package duke.exception;
+public class DukeException extends RuntimeException {
+
+ public enum Type {
+ EmptyDone {
+ @Override
+ public String getMessage() {
+ return "(O_O) OOPS!!! You haven't specified the task you have completed.";
+ }
+ },
+ EmptyDelete {
+ @Override
+ public String getMessage() {
+ return "(O_O) OOPS!!! You haven't specified the task you want to delete.";
+ }
+ },
+ InvalidTaskNumber {
+ @Override
+ public String getMessage() {
+ return "(O_O) OOPS!!! The task number you have specified is invalid.";
+ }
+ },
+ EmptyTodo {
+ @Override
+ public String getMessage() {
+ return "(O_O) OOPS!!! The description of a todo cannot be empty.";
+ }
+ },
+ EmptyEvent {
+ @Override
+ public String getMessage() {
+ return "(O_O) OOPS!!! The description of an event cannot be empty.";
+ }
+ },
+ EmptyDeadline {
+ @Override
+ public String getMessage() {
+ return "(O_O) OOPS!!! The description of a deadline cannot be empty.";
+ }
+ },
+ EmptyFind {
+ @Override
+ public String getMessage() {
+ return "(O_O) OOPS!!! The query for find cannot be empty.";
+ }
+ },
+ EmptyUpdate {
+ @Override
+ public String getMessage() {
+ return "(O_O) OOPS!!! The update cannot be empty.";
+ }
+ },
+ InvalidCommand {
+ @Override
+ public String getMessage() {
+ return "(O_O) OOPS!!! I'm sorry, but I don't know what that means :-(";
+ }
+ };
+
+ public abstract String getMessage();
+ }
+
+ public DukeException(Type type) {
+ super(type.getMessage());
+ }
+}
diff --git a/src/main/java/duke/gui/DialogBox.java b/src/main/java/duke/gui/DialogBox.java
new file mode 100644
index 0000000000..cc7f6b1b70
--- /dev/null
+++ b/src/main/java/duke/gui/DialogBox.java
@@ -0,0 +1,60 @@
+package duke.gui;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.HBox;
+import javafx.scene.shape.Circle;
+
+public class DialogBox extends HBox {
+ @FXML
+ private Label dialog;
+ @FXML
+ private ImageView displayPicture;
+ private Circle clip = new Circle(50, 50, 45);
+
+ private DialogBox(String text, Image img) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml"));
+ fxmlLoader.setController(this);
+ fxmlLoader.setRoot(this);
+ fxmlLoader.load();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ dialog.setText(text);
+ dialog.setAlignment(Pos.CENTER);
+ displayPicture.setImage(img);
+ displayPicture.setClip(clip);
+ }
+
+ /**
+ * Flips the dialog box such that the ImageView is on the left and text on the right.
+ */
+ private void flip() {
+ ObservableList tmp = FXCollections.observableArrayList(this.getChildren());
+ Collections.reverse(tmp);
+ getChildren().setAll(tmp);
+ setAlignment(Pos.TOP_LEFT);
+ }
+
+ public static DialogBox getUserDialog(String text, Image img) {
+ return new DialogBox(text, img);
+ }
+
+ public static DialogBox getDukeDialog(String text, Image img) {
+ var db = new DialogBox(text, img);
+ db.flip();
+ return db;
+ }
+}
diff --git a/src/main/java/duke/gui/MainWindow.java b/src/main/java/duke/gui/MainWindow.java
new file mode 100644
index 0000000000..6da8bd3b68
--- /dev/null
+++ b/src/main/java/duke/gui/MainWindow.java
@@ -0,0 +1,56 @@
+package duke.gui;
+
+import duke.Duke;
+import duke.ui.Ui;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextField;
+import javafx.scene.image.Image;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.VBox;
+/**
+ * Controller for MainWindow. Provides the layout for the other controls.
+ */
+public class MainWindow extends AnchorPane {
+ @FXML
+ private ScrollPane scrollPane;
+ @FXML
+ private VBox dialogContainer;
+ @FXML
+ private TextField userInput;
+ @FXML
+ private Button sendButton;
+
+ private Duke duke;
+
+ private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png"));
+ private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png"));
+
+ @FXML
+ public void initialize() {
+ scrollPane.vvalueProperty().bind(dialogContainer.heightProperty());
+ dialogContainer.getChildren().addAll(DialogBox.getDukeDialog(Ui.printWelcomeMessage(), dukeImage));
+ }
+
+ public void setDuke(Duke d) {
+ duke = d;
+ }
+
+ /**
+ * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to
+ * the dialog container. Clears the user input after processing.
+ */
+ @FXML
+ private void handleUserInput() {
+ String input = userInput.getText();
+ assert input != null;
+ String response = duke.getResponse(input);
+ assert response != null;
+ dialogContainer.getChildren().addAll(
+ DialogBox.getUserDialog(input, userImage),
+ DialogBox.getDukeDialog(response, dukeImage)
+ );
+ userInput.clear();
+ }
+}
diff --git a/src/main/java/duke/parser/Parser.java b/src/main/java/duke/parser/Parser.java
new file mode 100644
index 0000000000..29ee2a2e1f
--- /dev/null
+++ b/src/main/java/duke/parser/Parser.java
@@ -0,0 +1,65 @@
+package duke.parser;
+
+import duke.command.Command;
+import duke.command.DeadlineCommand;
+import duke.command.DeleteCommand;
+import duke.command.DoneCommand;
+import duke.command.EventCommand;
+import duke.command.ExitCommand;
+import duke.command.FindCommand;
+import duke.command.HelpCommand;
+import duke.command.ListCommand;
+import duke.command.TodoCommand;
+import duke.command.UpdateCommand;
+import duke.exception.DukeException;
+
+public class Parser {
+
+ /**
+ * Returns the command inputted by the user.
+ *
+ * @param input user's input.
+ * @return command
+ */
+ public static Command parse(String input) {
+ String[] commandWord = input.split("\\s+");
+ Command command;
+ switch (commandWord[0]) {
+ case "help":
+ command = new HelpCommand();
+ break;
+ case "list":
+ command = new ListCommand();
+ break;
+ case "done":
+ command = new DoneCommand();
+ break;
+ case "delete":
+ command = new DeleteCommand();
+ break;
+ case "todo":
+ command = new TodoCommand();
+ break;
+ case "event":
+ command = new EventCommand();
+ break;
+ case "deadline":
+ command = new DeadlineCommand();
+ break;
+ case "find":
+ command = new FindCommand();
+ break;
+ case "update":
+ command = new UpdateCommand();
+ break;
+ case "bye":
+ case "exit":
+ command = new ExitCommand();
+ break;
+ default:
+ throw new DukeException(DukeException.Type.InvalidCommand);
+ }
+ return command;
+ }
+
+}
diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java
new file mode 100644
index 0000000000..059a1ae887
--- /dev/null
+++ b/src/main/java/duke/storage/Storage.java
@@ -0,0 +1,170 @@
+package duke.storage;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+import duke.task.Deadline;
+import duke.task.Event;
+import duke.task.Task;
+import duke.task.TaskList;
+import duke.task.Todo;
+
+public class Storage {
+
+ protected static File taskFile;
+ protected String filePath;
+ protected TaskList taskList;
+
+ /**
+ * Constructor for storage.
+ *
+ * @param filePath the file path to the text file that stores the user's tasks.
+ */
+ public Storage(String filePath) {
+ this.filePath = filePath;
+ this.taskFile = new File(filePath);
+ this.taskList = new TaskList();
+ }
+
+ /**
+ * Returns user's list of tasks
+ *
+ * @return Task List
+ */
+ public TaskList load() {
+ assert taskFile.exists() : "there is no such file";
+ try {
+ taskFile.getParentFile().mkdirs();
+ if (!taskFile.createNewFile()) {
+ Scanner fileReader = new Scanner(taskFile);
+ this.taskList = loadList(this.taskList, fileReader);
+ fileReader.close();
+ }
+ } catch (IOException e) {
+ System.out.println("An error occurred.");
+ e.printStackTrace();
+ }
+ return taskList;
+ }
+
+ private Todo makeTodo(String description, char done) {
+ Todo t = new Todo(description);
+ if (done == '1') {
+ t.markAsDone();
+ }
+ return t;
+ }
+
+ private Deadline makeDeadline(String[] processedInput) {
+ Deadline d = new Deadline(processedInput[2], processedInput[3]);
+ if (processedInput[1].equals("1")) {
+ d.markAsDone();
+ }
+ return d;
+ }
+
+ private Event makeEvent(String[] processedInput) {
+ Event ev = new Event(processedInput[2], processedInput[3]);
+ if (processedInput[1].equals("1")) {
+ ev.markAsDone();
+ }
+ return ev;
+ }
+
+ private TaskList loadList(TaskList taskList, Scanner fileReader) {
+ while (fileReader.hasNextLine()) {
+ String line = fileReader.nextLine();
+ String[] processedInput = line.split(" \\| ");
+ if (processedInput[0].equals("T")) {
+ Todo t = makeTodo(line.substring(8), line.charAt(4));
+ taskList.addTask(t);
+ } else if (processedInput[0].equals("D")) {
+ Deadline d = makeDeadline(processedInput);
+ taskList.addTask(d);
+ } else {
+ Event e = makeEvent(processedInput);
+ taskList.addTask(e);
+ }
+ }
+ return taskList;
+ }
+
+ /**
+ * Saves the users tasks to a text file.
+ *
+ * @param taskList the list of user tasks.
+ */
+ public void save(ArrayList taskList) {
+ File list = new File(this.filePath);
+ list.delete();
+ try {
+ FileWriter writer = new FileWriter(this.filePath);
+ writeToFile(taskList, writer);
+ writer.close();
+ } catch (IOException e) {
+ System.out.println("An error occurred.");
+ e.printStackTrace();
+ }
+ }
+
+ private void writeToFile(ArrayList taskList, FileWriter writer) throws IOException {
+ try {
+ assert taskList != null;
+ for (int i = 0; i < taskList.size(); i++) {
+ Task task = taskList.get(i);
+ String isDone = task.getIsDone() ? "1" : "0";
+ if (task instanceof Todo) {
+ writer.write(getTodoString(task, isDone) + "\n");
+ } else if (task instanceof Deadline) {
+ writer.write(getDeadlineString(task, isDone) + "\n");
+ } else if (task instanceof Event) {
+ writer.write(getEventString(task, isDone) + "\n");
+ }
+ }
+ } catch (IOException e) {
+ System.out.println("An error occurred.");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Writes new task to file.
+ *
+ * @param task the task to be added to the file.
+ */
+ public void addTask(Task task) {
+ assert task != null;
+ try {
+ FileWriter writer = new FileWriter(this.filePath, true);
+ if (task instanceof Todo) {
+ writer.write(getTodoString(task, "0") + "\n");
+ } else if (task instanceof Deadline) {
+ writer.write(getDeadlineString(task, "0") + "\n");
+ } else if (task instanceof Event) {
+ writer.write(getEventString(task, "0") + "\n");
+ }
+ writer.close();
+ } catch (IOException e) {
+ System.out.println("An error occurred.");
+ e.printStackTrace();
+ }
+ }
+
+ private String getTodoString(Task task, String isDone) {
+ return "T | " + isDone + " | " + task.getDescription();
+ }
+
+ private String getDeadlineString(Task task, String isDone) {
+ Deadline d = (Deadline) task;
+ return "D | " + isDone + " | " + d.getDescription() + " | " + d.getBy();
+ }
+
+ private String getEventString(Task task, String isDone) {
+ Event e = (Event) task;
+ return "E | " + isDone + " | " + e.getDescription() + " | " + e.getAt();
+ }
+
+}
diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java
new file mode 100644
index 0000000000..3205f7dfee
--- /dev/null
+++ b/src/main/java/duke/task/Deadline.java
@@ -0,0 +1,62 @@
+package duke.task;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+public class Deadline extends Task {
+
+ protected String by;
+ protected LocalDate date;
+
+ /**
+ * Constructor for a Deadline object.
+ *
+ * @param description the description of the deadline task.
+ * @param by the due date of the task.
+ */
+ public Deadline(String description, String by) {
+ super(description);
+ this.by = by;
+ String[] d = by.split("-");
+ if (d.length == 3) {
+ date = LocalDate.parse(by);
+ }
+ }
+
+ /**
+ * Updates the description and due date of the deadline.
+ *
+ * @param input user's input for updating the deadline.
+ * @return new Deadline with updated description and due date.
+ */
+ @Override
+ public Deadline update(String input) {
+ if (input.contains("/by")) {
+ String[] splitInput = input.split(" /by ", 2);
+ return new Deadline(splitInput[0], splitInput[1]);
+ } else {
+ return new Deadline(input, this.by);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ if (this.date != null) {
+ return "[D]" + super.toString() + " (by: "
+ + this.date.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + ")";
+ } else {
+ return "[D]" + super.toString() + " (by: " + by + ")";
+ }
+ }
+
+ /**
+ * Returns the due date of the deadline.
+ *
+ * @return the value of by.
+ */
+ public String getBy() {
+ return this.by;
+ }
+}
diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java
new file mode 100644
index 0000000000..d209e6f1da
--- /dev/null
+++ b/src/main/java/duke/task/Event.java
@@ -0,0 +1,62 @@
+package duke.task;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+public class Event extends Task {
+
+ protected String at;
+ protected LocalDate date;
+
+ /**
+ * Constructor for an event.
+ *
+ * @param description a description of the event.
+ * @param at the date the event is happening.
+ */
+ public Event(String description, String at) {
+ super(description);
+ this.at = at;
+ String[] d = at.split("-");
+ if (d.length == 3) {
+ date = LocalDate.parse(at);
+ }
+ }
+
+ /**
+ * Updates the description and due date of the Event.
+ *
+ * @param input the description of the event task.
+ * @return new Event with updated description and due date.
+ */
+ @Override
+ public Event update(String input) {
+ if (input.contains("/at")) {
+ String[] splitInput = input.split(" /at ", 2);
+ return new Event(splitInput[0], splitInput[1]);
+ } else {
+ return new Event(input, this.at);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ if (this.date != null) {
+ return "[E]" + super.toString() + " (at: "
+ + this.date.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + ")";
+ } else {
+ return "[E]" + super.toString() + " (at: " + this.at + ")";
+ }
+ }
+
+ /**
+ * Returns the date of the event.
+ *
+ * @return the value of at.
+ */
+ public String getAt() {
+ return this.at;
+ }
+}
diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java
new file mode 100644
index 0000000000..0de3b6ecab
--- /dev/null
+++ b/src/main/java/duke/task/Task.java
@@ -0,0 +1,66 @@
+package duke.task;
+public abstract class Task {
+ protected String description;
+ protected boolean isDone;
+
+ /**
+ * Constructor for task object.
+ *
+ * @param description description of the task.
+ */
+ public Task(String description) {
+ this.description = description;
+ this.isDone = false;
+ }
+
+ /**
+ * Returns the status of the task (X for done, " " for not yet done).
+ *
+ * @return the icon that represents the status of the task.
+ */
+ public String getStatusIcon() {
+ return (isDone ? "X" : " ");
+ }
+
+ /**
+ * Marks the task as done.
+ */
+ public void markAsDone() {
+ this.isDone = true;
+ }
+
+ /**
+ * Returns a boolean representing the status of the task.
+ *
+ * @return the task's isDone value.
+ */
+ public boolean getIsDone() {
+ return this.isDone;
+ }
+
+ /**
+ * Updates the description and due date of the Event.
+ *
+ * @param input the description of the event task.
+ * @return new Task with updated description.
+ */
+ public abstract Task update(String input);
+
+ /**
+ * Returns the description of the task.
+ *
+ * @return the description of the task.
+ */
+ public String getDescription() {
+ return this.description;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "[" + this.getStatusIcon() + "] " + this.description;
+ }
+}
diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java
new file mode 100644
index 0000000000..1ea9b52f55
--- /dev/null
+++ b/src/main/java/duke/task/TaskList.java
@@ -0,0 +1,106 @@
+package duke.task;
+
+import java.util.ArrayList;
+
+import duke.storage.Storage;
+
+
+public class TaskList {
+ private final ArrayList taskList;
+
+ /**
+ * Constructor for taskList.
+ */
+ public TaskList() {
+ this.taskList = new ArrayList();
+ }
+
+ /**
+ * Adds a task to the task list.
+ *
+ * @param task the task to be added to the task list.
+ */
+ public void addTask(Task task) {
+ this.taskList.add(task);
+ }
+
+ /**
+ * Removes a task from the task list.
+ *
+ * @param taskNum the index of the task (1-indexed).
+ * @param storage the storage which holds all the user's data.
+ * @return string that confirms the removal of the task.
+ */
+ public Task removeTask(int taskNum, Storage storage) {
+ Task task = getTask(taskNum);
+ this.taskList.remove(task);
+ storage.save(this.taskList);
+ return task;
+ }
+
+ /**
+ * Marks task indicated by the task number as done.
+ *
+ * @param taskNum the index of the task (1-indexed).
+ * @param storage the storage which holds all the user's data.
+ * @return string that confirms the task has been marked done.
+ */
+ public Task markTaskAsDone(int taskNum, Storage storage) {
+ Task task = getTask(taskNum);
+ assert task != null;
+ task.markAsDone();
+ storage.save(this.taskList);
+ return task;
+ }
+
+ /**
+ * Returns the task indicated by the task number.
+ *
+ * @param taskNum the index of the task (1-indexed).
+ * @return the task indicated by the task number.
+ */
+ public Task getTask(int taskNum) {
+ return this.taskList.get(taskNum - 1);
+ }
+
+ /**
+ * Returns a TaskList containing all the tasks that contain the search query.
+ *
+ * @param query the user's search query.
+ * @return a tasklist that contains all the tasks that contain the search query.
+ */
+ public TaskList findTasks(String query) {
+ TaskList matchingTasks = new TaskList();
+ for (Task t : this.taskList) {
+ if (t.getDescription().toLowerCase().contains(query)) {
+ matchingTasks.addTask(t);
+ }
+ }
+ return matchingTasks;
+ }
+
+ /**
+ * Updates task given the task number and update, then updates file.
+ *
+ * @param taskNum the index of the task (1-indexed).
+ * @param update the update to be applied to the task.
+ * @param storage the storage which holds all the user's data.
+ */
+ public Task updateTask(int taskNum, String update, Storage storage) {
+ Task task = getTask(taskNum);
+ task = task.update(update);
+ this.taskList.set(taskNum - 1, task);
+ assert this.taskList != null;
+ storage.save(this.taskList);
+ return task;
+ }
+
+ /**
+ * Returns the number of tasks in the task list.
+ *
+ * @return the number of tasks in the task list.
+ */
+ public int getNumTasks() {
+ return taskList.size();
+ }
+}
diff --git a/src/main/java/duke/task/Todo.java b/src/main/java/duke/task/Todo.java
new file mode 100644
index 0000000000..862751ac33
--- /dev/null
+++ b/src/main/java/duke/task/Todo.java
@@ -0,0 +1,32 @@
+package duke.task;
+
+public class Todo extends Task {
+
+ /**
+ * Constructor for a todo object.
+ *
+ * @param description the description of the todo object.
+ */
+ public Todo(String description) {
+ super(description);
+ }
+
+ /**
+ * Updates the description of the Todo.
+ *
+ * @param description the description of the todo task.
+ * @return new Todo with updated description and due date.
+ */
+ @Override
+ public Todo update(String description) {
+ return new Todo(description);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "[T]" + super.toString();
+ }
+}
diff --git a/src/main/java/duke/ui/Ui.java b/src/main/java/duke/ui/Ui.java
new file mode 100644
index 0000000000..ee9acffdb4
--- /dev/null
+++ b/src/main/java/duke/ui/Ui.java
@@ -0,0 +1,130 @@
+package duke.ui;
+
+import duke.task.Task;
+import duke.task.TaskList;
+
+public class Ui {
+
+ /**
+ * Returns the welcome message of the duke chat bot.
+ *
+ * @return welcome message.
+ */
+ public static String printWelcomeMessage() {
+ return "Hello! I'm Duke" + "\n" + "What can I do for you?" + "\n";
+ }
+
+ /**
+ * Returns the goodbye message of the duke chat bot.
+ *
+ * @return goodbye message.
+ */
+ public static String printGoodbyeMessage() {
+ return "Bye! Hope to see you soon!" + "\n";
+ }
+
+ /**
+ * Returns message that contains all the tasks in the user's task list.
+ *
+ * @param taskList the user's task list.
+ * @return string that contains all the tasks in the user's task list.
+ */
+ public static String printTaskList(TaskList taskList) {
+ if (taskList.getNumTasks() == 0) {
+ return "You have no tasks in your list!\n";
+ } else {
+ StringBuilder output = new StringBuilder("Here are the tasks in your list:\n");
+ for (int i = 1; i <= taskList.getNumTasks(); i++) {
+ Task item = taskList.getTask(i);
+ output.append(String.format(" %d. %s\n", (i), item.toString()));
+ }
+ return output.toString();
+ }
+ }
+
+ /**
+ * Returns message that confirms the task has been marked done.
+ *
+ * @param task the task that has been completed.
+ * @return string that confirms the task has been marked done.
+ */
+ public static String printTaskDone(Task task) {
+ return "Nice! I've marked this task as done:\n"
+ + String.format(" %s\n", task);
+ }
+
+ /**
+ * Returns message that confirms the creation of the task.
+ *
+ * @param task the task taht has been created.
+ * @param numTasks the total number of tasks in the user's task list.
+ * @return
+ */
+ public static String printTaskCreated(Task task, int numTasks) {
+ return "Got it. I've added this task:\n " + task.toString() + "\n"
+ + String.format("Now you have %d tasks in the list.\n", numTasks);
+ }
+
+ /**
+ * Returns message that confirms the task has been deleted.
+ *
+ * @param task the task that has been deleted.
+ * @param numTasks the total number of tasks in the task list.
+ * @return string that confirms the deletion of the task.
+ */
+ public static String printTaskDeleted(Task task, int numTasks) {
+ return "Noted. I've removed this task:\n"
+ + String.format(" %s\nNow you have %d tasks in the list.\n",
+ task.toString(), numTasks);
+ }
+
+ /**
+ * Returns message that confirms the task has been updated.
+ *
+ * @param task the task that has been updated.
+ * @return string that confirms the update of the task.
+ */
+ public static String printTaskUpdated(Task task) {
+ return "Your task has been updated!\n " + task;
+ }
+
+ /**
+ * Returns message that contains all the tasks that match the user's query.
+ *
+ * @param matchingTasks the tasks that match the user's query.
+ * @return string that lists all the tasks that matches the user's query.
+ */
+ public static String printTasksFound(TaskList matchingTasks) {
+ if (matchingTasks.getNumTasks() == 0) {
+ return "You have no matching tasks in your list!\n";
+ } else {
+ StringBuilder output = new StringBuilder("Here are the matching tasks in your list:\n");
+ for (int i = 1; i <= matchingTasks.getNumTasks(); i++) {
+ Task item = matchingTasks.getTask(i);
+ output.append(String.format(" %d. %s\n", i, item.toString()));
+ }
+ return output.toString();
+ }
+ }
+
+ /**
+ * Returns message explaining how to use the duke bot.
+ *
+ * @return message explaining how to use the duke bot.s
+ */
+ public static String printHelpMessage() {
+ String helpMessage = "Try these commands: \n\n";
+ helpMessage += "View help: help \n\n";
+ helpMessage += "Listing all tasks: list \n\n";
+ helpMessage += "Adding a todo: todo \n\n";
+ helpMessage += "Adding a deadline: deadline /by \n\n";
+ helpMessage += "Adding an event: event /at \n\n";
+ helpMessage += "Deleting a task: delete \n\n";
+ helpMessage += "Updating a task: update (/at or /by if applicable) "
+ + " \n\n";
+ helpMessage += "Finding a task: find \n\n";
+ helpMessage += "Exiting: exit or bye";
+ return helpMessage;
+ }
+
+}
diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png
new file mode 100644
index 0000000000..b74b55e1bb
Binary files /dev/null and b/src/main/resources/images/DaDuke.png differ
diff --git a/src/main/resources/images/DaUser.png b/src/main/resources/images/DaUser.png
new file mode 100644
index 0000000000..353636795b
Binary files /dev/null and b/src/main/resources/images/DaUser.png differ
diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml
new file mode 100644
index 0000000000..aaa558c390
--- /dev/null
+++ b/src/main/resources/view/DialogBox.fxml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
new file mode 100644
index 0000000000..ea33cc7de6
--- /dev/null
+++ b/src/main/resources/view/MainWindow.fxml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/DukeTest.java b/src/test/java/DukeTest.java
new file mode 100644
index 0000000000..3625e68f94
--- /dev/null
+++ b/src/test/java/DukeTest.java
@@ -0,0 +1,36 @@
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import duke.Duke;
+import org.junit.jupiter.api.Test;
+
+import duke.parser.Parser;
+import duke.storage.Storage;
+import duke.ui.Ui;
+
+public class DukeTest {
+
+ Ui ui = new Ui();
+ Storage storage = new Storage("Test\\taskList.txt");
+ Duke duke = new Duke("Test\\taskList.txt");
+
+ @Test
+ public void parserTest() {
+ assertEquals("bye", Parser.parse("bye"));
+ assertEquals("list", Parser.parse("list"));
+ assertEquals("todo", Parser.parse("todo borrow book"));
+ }
+
+ @Test
+ public void dukeExceptionTest() {
+ assertEquals("(ㆆ_ㆆ) OOPS!!! You haven't specified the task you have completed.",
+ this.duke.getResponse("done"));
+ assertEquals("(O_O) OOPS!!! You haven't specified the task you want to delete.",
+ this.duke.getResponse("delete"));
+ }
+
+ @Test
+ public void storageTest() {
+ assertEquals(0, storage.load().getNumTasks());
+ }
+
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e7..a1c46a013d 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,7 +1,54 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
+ ____________________________________________________________
+ Hello! I'm Duke
+ What can I do for you?
+ ____________________________________________________________
+ ____________________________________________________________
+ Got it. I've added this task:
+ [T][ ] borrow book
+ Now you have 1 tasks in the list.
+ ____________________________________________________________
+ ____________________________________________________________
+ Got it. I've added this task:
+ [T][ ] read book
+ Now you have 2 tasks in the list.
+ ____________________________________________________________
+ ____________________________________________________________
+ Here are the tasks in your list:
+ 1. [T][ ] borrow book
+ 2. [T][ ] read book
+ ____________________________________________________________
+ ____________________________________________________________
+ Got it. I've added this task:
+ [D][ ] return book (by: Sunday)
+ Now you have 3 tasks in the list.
+ ____________________________________________________________
+ ____________________________________________________________
+ Got it. I've added this task:
+ [E][ ] project meeting (at: Monday 2-4pm)
+ Now you have 4 tasks in the list.
+ ____________________________________________________________
+ ____________________________________________________________
+ Nice! I've marked this task as done:
+ [T][X] borrow book
+ ____________________________________________________________
+ ____________________________________________________________
+ Here are the tasks in your list:
+ 1. [T][X] borrow book
+ 2. [T][ ] read book
+ 3. [D][ ] return book (by: Sunday)
+ 4. [E][ ] project meeting (at: Monday 2-4pm)
+ ____________________________________________________________
+ ____________________________________________________________
+ Nice! I've marked this task as done:
+ [D][X] return book (by: Sunday)
+ ____________________________________________________________
+ ____________________________________________________________
+ Here are the tasks in your list:
+ 1. [T][X] borrow book
+ 2. [T][ ] read book
+ 3. [D][X] return book (by: Sunday)
+ 4. [E][ ] project meeting (at: Monday 2-4pm)
+ ____________________________________________________________
+ ____________________________________________________________
+ Bye. Hope to see you again soon!
+ ____________________________________________________________
\ No newline at end of file
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index e69de29bb2..933de66667 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -0,0 +1,10 @@
+todo borrow book
+todo read book
+list
+deadline return book /by Sunday
+event project meeting /at Monday 2-4pm
+done 1
+list
+done 3
+list
+bye
\ No newline at end of file