diff --git a/.gitignore b/.gitignore
index 2873e189e1..84951c9b1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,13 @@ bin/
/text-ui-test/ACTUAL.TXT
text-ui-test/EXPECTED-UNIX.TXT
+
+build.gradle
+data/tasklist.txt
+gradle/wrapper/gradle-wrapper.jar
+gradle/wrapper/gradle-wrapper.properties
+gradlew
+text-ui-test/runtest.bat
+
+# Class files
+src/main/java/*.class
diff --git a/README.md b/README.md
index 8715d4d915..7d63353154 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# Duke project template
+# chatbot.evan.Evan project template
-This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it.
+This is a project template for a greenfield Java project. It's named after my english name "Evan". Given below are instructions on how to use it.
## Setting up in Intellij
@@ -13,12 +13,19 @@ Prerequisites: JDK 11, update Intellij to the most recent version.
1. If there are any further prompts, accept the defaults.
1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
In the same dialog, set the **Project language level** field to the `SDK default` option.
-3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output:
+3. After that, locate the `src/main/java/chatbot.evan.Evan.java` file, right-click it, and choose `Run chatbot.evan.Evan.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output:
```
- Hello from
- ____ _
- | _ \ _ _| | _____
- | | | | | | | |/ / _ \
- | |_| | |_| | < __/
- |____/ \__,_|_|\_\___|
+ Hello! I'm Evan, your personal task planning assistant.
+ What can I do for you?
+
+ Available commands:
+ todo: create a new todo task
+ deadline: create a new deadline task
+ event: create a new event task
+ mark: mark a task as complete
+ unmark: mark a task as incomplete
+ delete: delete a task from the list
+ find: find a task from the list
+ list: show a list of all the tasks saved
```
+ For more information on how to use the commands, do check out our README documentation in the docs folder!
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000000..eb761a9b9a
--- /dev/null
+++ b/config/checkstyle/checkstyle.xml
@@ -0,0 +1,434 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml
new file mode 100644
index 0000000000..39efb6e4ac
--- /dev/null
+++ b/config/checkstyle/suppressions.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/docs/README.md b/docs/README.md
index 8077118ebe..fd450613c5 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,29 +1,313 @@
-# User Guide
+# Evan - User Guide
## Features
-### Feature-ABC
+### Adding Tasks into the list
-Description of the feature.
+We allow you to add 3 different types of tasks into the list.
+1. ToDo Task
+2. Deadline Task
+3. Event Task
-### Feature-XYZ
+A ToDo task allows you to add a task without any dates specified.
+A Deadline task allows you to add a task with 1 date/time specified as it's deadline.
+An Event task allows you to add a task with 2 date/times. The first is specified as the start time and the second is specified as the end time of the event.
-Description of the feature.
+### View List
+
+Users can view the entire list of their pending tasks.
+
+### Delete Task
+
+You can delete a specified task on the list.
+
+### Task Status
+
+Users can mark a task as incompleted or completed.
+
+### Find Task
+
+Users can find a particular task they want to see by searching keywords.
## Usage
-### `Keyword` - Describe action
+### `list` - Show the entire list of tasks
+
+All the user has to type is "list", no arguments required.
+
+Format: `list`
+
+Example of usage:
+
+`list`
+
+Expected outcome:
+
+The Chatbot will respond with a list of all the tasks that is tracked
+
+```
+Here are the tasks in your list:
+1. [T][] Read Book
+2. [D][X] Finish CS2100 Assignment (by: 18-Sep-2023 1300)
+3. [D][X] Finish CS2103T ip (by: 22-Sep-2023)
+4. [E][] NUSBS Dharma Camp (from: 1-Jul-2024 to: 2-Jul-2024)
+```
+
+### `todo` - Starts a process to add a ToDo entry into the list
+
+All the user has to type is "todo", no arguments required.
+Further follow-up actions are required to complete the insertion of the task as instructed by the chatbot.
+
+Follow-up actions required:
+1. Type in name of the task
+
+
+Format: `todo`
+
+Example of usage (2-steps):
+
+User types `todo`
+
+Expected outcome:
+
+The Chatbot will instruct users on follow up actions to create a ToDo task
+
+```
+So you want to add a ToDo task. Tell me what's the task.
+```
+
+User types in the `name of task`
+
+Expected outcome:
+
+The Chatbot will respond that the task has been inserted
+
+```
+Got it. I've added this task:
+[T][] Read Book
+Now you have 1 tasks in the list.
+```
+
+### `deadline` - Starts a process to add a Deadline entry into the list
+
+All the user has to type is "deadline", no arguments required.
+Further follow-up actions are required to complete the insertion of the task as instructed by the chatbot.
+
+Follow-up actions required:
+1. Type in name of the task
+2. Type in date of deadline
+3. (Optional) Type in time of deadline
+
+
+Format: `deadline`
+
+Example of usage (4-steps):
+
+User types `deadline`
+
+Expected outcome:
+
+The Chatbot will prompt for the name of the task.
+
+```
+So you want to add a task with deadline. Tell me what's the task.
+```
+
+User types in the `name of task`
+
+Expected outcome:
+
+The Chatbot will prompt for the deadline date
+
+```
+Now indicate the deadline date.
+```
+
+User types in the `date of deadline`
+
+Expected outcome:
+
+The Chatbot will prompt for the deadline time
+
+```
+Indicate a start time ranging from 0000 - 2359. You may enter 'Skip' to not indicate a time.
+```
+
+User types in the `time of deadline` or `skip`
+
+Expected outcome:
+
+The Chatbot will respond that the task has been inserted
+
+```
+Got it. I've added this task:
+[D][] Finish CS2100 Assignment (by: 18-Sep-2023)
+Now you have 2 tasks in the list.
+```
+
+### `event` - Starts a process to add a Event entry into the list
+
+All the user has to type is "event", no arguments required.
+Further follow-up actions are required to complete the insertion of the task as instructed by the chatbot.
+
+Follow-up actions required:
+1. Type in name of the task
+2. Type in date of start date
+3. (Optional) Type in time of start date
+4. Type in date of end date
+5. (Optional) Type in time of end date
+
+
+Format: `event`
+
+Example of usage (6-steps):
+
+User types `event`
+
+Expected outcome:
+
+The Chatbot will prompt for the name of the task.
+
+```
+So you want to add an event task. Tell me what's the task.
+```
+
+User types in the `name of task`
+
+Expected outcome:
+
+The Chatbot will prompt for the start date
+
+```
+Now indicate the start date.
+```
+
+User types in the `date of start date`
+
+Expected outcome:
+
+The Chatbot will prompt for the start time
+
+```
+Indicate a start time ranging from 0000 - 2359. You may enter 'Skip' to not indicate a time.
+```
+
+User types in the `start time` or `skip`
-Describe the action and its outcome.
+Expected outcome:
+
+The Chatbot will prompt for the end date
+
+```
+Now indicate the end date.
+```
+
+User types in the `date of end date`
+
+Expected outcome:
+
+The Chatbot will prompt for the end time
+
+```
+Indicate an end time ranging from 0000 - 2359. You may enter 'Skip' to not indicate a time.
+```
+
+User types in the `end time` or `skip`
+
+Expected outcome:
+
+The Chatbot will respond that the task has been inserted
+
+```
+Got it. I've added this task:
+[E][] NUSBS Dharma Camp (from: 1-Jul-2024 0930 to: 2-Jul-2024 1800)
+Now you have 3 tasks in the list.
+```
+
+### `mark` - Marks a specified task in the list as complete
+
+User has to type `mark` followed by the corresponding index number of the task in the list
+
+Format: `mark INDEX`
+- Deletes the task at the specified `INDEX`
+- `INDEX` must not exceed the size of the list
+- `INDEX` must be a positive number
+
+Example of usage:
+
+`mark 1`
+
+Expected outcome:
+
+The Chatbot will respond that the task has been successfully marked as done
+
+```
+Nice! I've marked this task as done
+[T][X] Read Book
+```
+
+### `unmark` - Marks a specified task in the list as incomplete
+
+User has to type `unmark` followed by the corresponding index number of the task in the list
+
+Format: `unmark INDEX`
+- Deletes the task at the specified `INDEX`
+- `INDEX` must not exceed the size of the list
+- `INDEX` must be a positive number
+
+Example of usage:
+
+`unmark 1`
+
+Expected outcome:
+
+The Chatbot will respond that the task has been successfully marked as incomplete
+
+```
+OK! I've marked this task as not done yet:
+[T][] Read Book
+```
+
+### `delete` - Delete a specified task in the list
+
+User has to type `delete` followed by the corresponding index number of the task in the list
+
+Format: `delete INDEX`
+- Deletes the task at the specified `INDEX`
+- `INDEX` must not exceed the size of the list
+- `INDEX` must be a positive number
Example of usage:
-`keyword (optional arguments)`
+`delete 1`
Expected outcome:
-Description of the outcome.
+The Chatbot will respond that the task has been successfully deleted
+
+```
+Noted. I've removed this task:
+[T][] Read Book
+Now you have 2 tasks in the list
+```
+
+### `find` - Finds task with the associated specified keyword/phrase
+
+User has to type `find` followed by the keyword/phrase that they want to find.
+
+Format: `find WORDS`
+- Finds the task with the specified `WORDS`
+- `WORDS` can be a part of a word or phrase (e.g. find accum would find tasks with the word "accumulator" in it)
+
+Example of usage:
+
+`find read`
+
+Expected outcome:
+The Chatbot will provide a list of the tasks with the word "read" in it
```
-expected output
+Here are the matching tasks in your list with its correct corresponding index numbers:
+1.[T][] Read Book
```
+Note - The Index number displayed in the chatbot response will correspond to the actual index number of the task in the full list
diff --git a/docs/Ui.png b/docs/Ui.png
new file mode 100644
index 0000000000..2dc32a5340
Binary files /dev/null and b/docs/Ui.png differ
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000000..6689b85bee
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,92 @@
+@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=.
+@rem This is normally unused
+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% equ 0 goto execute
+
+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 execute
+
+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
+
+: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 %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 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!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
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/chatbot/evan/Evan.java b/src/main/java/chatbot/evan/Evan.java
new file mode 100644
index 0000000000..2bf16504e4
--- /dev/null
+++ b/src/main/java/chatbot/evan/Evan.java
@@ -0,0 +1,94 @@
+package chatbot.evan;
+
+import enums.Command;
+import exception.InvalidCommandException;
+import process.ComplexProcess;
+import process.Deadline;
+import process.Delete;
+import process.Event;
+import process.Find;
+import process.Mark;
+import process.SimpleProcess;
+import process.ToDo;
+import process.Unmark;
+import task.TaskManager;
+
+/**
+ * Main class of the chatbot
+ */
+public class Evan {
+ private ComplexProcess process = null;
+
+ /**
+ * Returns the response of the chatbot for a given input
+ * @param input of user
+ * @return response of chatbot
+ */
+ public String getResponse(String input) {
+ if (process != null) {
+ return getExistingProcessResponse(input);
+ }
+
+ return startProcessThenGetResponse(input);
+ }
+
+ private String getExistingProcessResponse(String input) {
+ String response = process.processInput(input);
+ if (process.isComplete()) {
+ process = null;
+ }
+ return response;
+ }
+
+ private String startProcessThenGetResponse(String input) {
+ if (input.equals(Command.BYE.toString())) {
+ return "Bye. Hope to see you again soon!";
+ } else if (input.equals(Command.LIST.toString())) {
+ TaskManager taskManager = TaskManager.init();
+ return taskManager.getAllTasks();
+ } else if (input.equals(Command.TODO.toString()) || input.equals("t")) {
+ process = new ToDo();
+ return process.firstInstruction();
+ } else if (input.equals(Command.DEADLINE.toString()) || input.equals("d")) {
+ process = new Deadline();
+ return process.firstInstruction();
+ } else if (input.equals(Command.EVENT.toString()) || input.equals("e")) {
+ process = new Event();
+ return process.firstInstruction();
+ } else if (input.startsWith(Command.DELETE.toString())) {
+ SimpleProcess simpleProcess = new Delete();
+ return simpleProcess.processInput(input);
+ } else if (input.startsWith(Command.MARK.toString())) {
+ SimpleProcess simpleProcess = new Mark();
+ return simpleProcess.processInput(input);
+ } else if (input.startsWith(Command.UNMARK.toString())) {
+ SimpleProcess simpleProcess = new Unmark();
+ return simpleProcess.processInput(input);
+ } else if (input.startsWith(Command.FIND.toString())) {
+ SimpleProcess simpleProcess = new Find();
+ return simpleProcess.processInput(input);
+ } else {
+ InvalidCommandException e = new InvalidCommandException();
+ return e.toString();
+ }
+ }
+
+ /**
+ * Gets the string introduction of the bot and its commands
+ * @return string introduction of the chatbot and its commands
+ */
+ public String getIntro() {
+ StringBuilder stringBuilder = new StringBuilder("Hello! I'm Evan, your personal task planning assistant.\n")
+ .append("What can I do for you?\n\n")
+ .append("Available commands:\n")
+ .append("todo: create a new todo task\n")
+ .append("deadline: create a new deadline task\n")
+ .append("event: create a new event task\n")
+ .append("mark: mark a task as complete\n")
+ .append("unmark: mark a task as incomplete\n")
+ .append("delete: delete a task from the list\n")
+ .append("find: find a task from the list\n")
+ .append("list: show a list of all the tasks saved\n");
+ return stringBuilder.toString();
+ }
+}
diff --git a/src/main/java/enums/Command.java b/src/main/java/enums/Command.java
new file mode 100644
index 0000000000..9a9278508d
--- /dev/null
+++ b/src/main/java/enums/Command.java
@@ -0,0 +1,36 @@
+package enums;
+
+/**
+ * Enum class with all the commands listed
+ */
+public enum Command {
+ BYE("bye"),
+ LIST("list"),
+ MARK("mark"),
+ UNMARK("unmark"),
+ DELETE("delete"),
+ DEADLINE("deadline"),
+ TODO("todo"),
+ EVENT("event"),
+ FIND("find"),
+ SKIP("skip");
+
+ private String cmd;
+
+ /**
+ * Constructor for the enum class
+ * @param cmd String of the command
+ */
+ Command(String cmd) {
+ this.cmd = cmd;
+ }
+
+ /**
+ * returns the String representation of the command
+ * @return the command String
+ */
+ @Override
+ public String toString() {
+ return cmd;
+ }
+}
diff --git a/src/main/java/exception/InvalidCommandException.java b/src/main/java/exception/InvalidCommandException.java
new file mode 100644
index 0000000000..676ca83fd6
--- /dev/null
+++ b/src/main/java/exception/InvalidCommandException.java
@@ -0,0 +1,15 @@
+package exception;
+
+/**
+ * Checked Exception for the event where the input command is not a valid command.
+ */
+public class InvalidCommandException extends Exception {
+ /**
+ * Returns a string to inform users that the command is invalid
+ * @return string information to inform user of exception
+ */
+ @Override
+ public String toString() {
+ return "This is not a registered command that I know of";
+ }
+}
diff --git a/src/main/java/exception/InvalidDateException.java b/src/main/java/exception/InvalidDateException.java
new file mode 100644
index 0000000000..b88e73143e
--- /dev/null
+++ b/src/main/java/exception/InvalidDateException.java
@@ -0,0 +1,16 @@
+package exception;
+
+/**
+ * Checked Exception for the event where the input date is not a valid date.
+ */
+public class InvalidDateException extends Exception {
+
+ /**
+ * Returns a string to inform users that the date is invalid
+ * @return string information to inform user of exception
+ */
+ @Override
+ public String toString() {
+ return "The given date is invalid. Please type in a valid date.";
+ }
+}
diff --git a/src/main/java/exception/InvalidInputException.java b/src/main/java/exception/InvalidInputException.java
new file mode 100644
index 0000000000..da41c87519
--- /dev/null
+++ b/src/main/java/exception/InvalidInputException.java
@@ -0,0 +1,16 @@
+package exception;
+
+/**
+ * Checked Exception for the event where the input is not valid.
+ */
+public class InvalidInputException extends Exception {
+
+ /**
+ * Returns a string to inform users that the input is invalid
+ * @return string information to inform user of exception
+ */
+ @Override
+ public String toString() {
+ return "Your given input is invalid. Please type an appropriate input.";
+ }
+}
diff --git a/src/main/java/exception/InvalidTimeException.java b/src/main/java/exception/InvalidTimeException.java
new file mode 100644
index 0000000000..58c92b6a7d
--- /dev/null
+++ b/src/main/java/exception/InvalidTimeException.java
@@ -0,0 +1,17 @@
+package exception;
+
+/**
+ * Checked Exception for the event where the input time is not a valid time.
+ */
+public class InvalidTimeException extends Exception {
+
+ /**
+ * Returns a string to inform users that the time is invalid
+ * @return string information to inform user of exception
+ */
+ @Override
+ public String toString() {
+ return "The given time is invalid. "
+ + "Please type in a valid time in the range between 0000 - 2359";
+ }
+}
diff --git a/src/main/java/exception/MissingArgumentException.java b/src/main/java/exception/MissingArgumentException.java
new file mode 100644
index 0000000000..99fc156b23
--- /dev/null
+++ b/src/main/java/exception/MissingArgumentException.java
@@ -0,0 +1,11 @@
+package exception;
+
+/**
+ * Checked Exception for the event where there are missing arguments.
+ */
+public class MissingArgumentException extends Exception {
+ @Override
+ public String toString() {
+ return "There are missing arguments to this command";
+ }
+}
diff --git a/src/main/java/exception/MissingIndexException.java b/src/main/java/exception/MissingIndexException.java
new file mode 100644
index 0000000000..2dfd16db37
--- /dev/null
+++ b/src/main/java/exception/MissingIndexException.java
@@ -0,0 +1,11 @@
+package exception;
+
+/**
+ * Checked Exception for the event where there are missing index arguments.
+ */
+public class MissingIndexException extends MissingArgumentException {
+ @Override
+ public String toString() {
+ return "The following arguments are missing: index of the task";
+ }
+}
diff --git a/src/main/java/exception/MissingKeywordException.java b/src/main/java/exception/MissingKeywordException.java
new file mode 100644
index 0000000000..a232b6f818
--- /dev/null
+++ b/src/main/java/exception/MissingKeywordException.java
@@ -0,0 +1,11 @@
+package exception;
+
+/**
+ * Checked Exception for the event where there are missing keyword arguments.
+ */
+public class MissingKeywordException extends MissingArgumentException {
+ @Override
+ public String toString() {
+ return "The following arguments are missing: keyword of the task";
+ }
+}
diff --git a/src/main/java/parser/CommandParser.java b/src/main/java/parser/CommandParser.java
new file mode 100644
index 0000000000..01bcf7e3c4
--- /dev/null
+++ b/src/main/java/parser/CommandParser.java
@@ -0,0 +1,82 @@
+package parser;
+
+import enums.Command;
+import exception.InvalidCommandException;
+import exception.MissingArgumentException;
+import exception.MissingIndexException;
+import exception.MissingKeywordException;
+
+/**
+ * Class to help parse complex strings from user input
+ */
+public class CommandParser {
+
+ /**
+ * Validates if the first word is a valid command and filters it to return the arguments
+ * @param input string of a single user input
+ * @return command arguments
+ * @throws MissingArgumentException if there are no arguments
+ * @throws InvalidCommandException if the first word is an invalid command
+ */
+ public static String getCommandArguments(String input) throws
+ MissingArgumentException,
+ InvalidCommandException {
+
+ assert input != null : "string input is null";
+
+ String firstWord = getFirstWord(input);
+
+ //ensure first word is a valid command
+ if (!isValidCommand(firstWord)) {
+ throw new InvalidCommandException();
+ }
+
+ //ensure that arguments exist
+ String args = input.substring(firstWord.length());
+ if (args.isBlank()) {
+ throwAppropriateMissingArgumentException(firstWord);
+ }
+
+ return args.substring(1);
+ }
+
+ /**
+ * Takes in a string of input and returns the first word
+ * @param input String of user input
+ * @return the first word of the string
+ */
+ public static String getFirstWord(String input) {
+
+ int spaceIndex = input.indexOf(' ');
+ if (spaceIndex == -1) {
+ return input;
+ }
+ return input.substring(0, spaceIndex);
+ }
+
+ private static boolean isValidCommand(String word) {
+ try {
+ Command.valueOf(word.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private static void throwAppropriateMissingArgumentException(String command)
+ throws MissingArgumentException {
+ Command commandEnum = Command.valueOf(command.toUpperCase());
+ switch (commandEnum) {
+ case MARK:
+ throw new MissingIndexException();
+ case UNMARK:
+ throw new MissingIndexException();
+ case DELETE:
+ throw new MissingIndexException();
+ case FIND:
+ throw new MissingKeywordException();
+ default:
+ throw new MissingArgumentException();
+ }
+ }
+}
diff --git a/src/main/java/parser/Time.java b/src/main/java/parser/Time.java
new file mode 100644
index 0000000000..56ada705a3
--- /dev/null
+++ b/src/main/java/parser/Time.java
@@ -0,0 +1,177 @@
+package parser;
+
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+import exception.InvalidDateException;
+import exception.InvalidTimeException;
+
+/**
+ * Class for the parsing of a string of date/time data
+ */
+public class Time {
+
+ private static final DateTimeFormatter[] FORMATS = new DateTimeFormatter[]{
+ DateTimeFormatter.ofPattern("MMM-d-yyyy"),
+ DateTimeFormatter.ofPattern("MMM-dd-yyyy"),
+ DateTimeFormatter.ofPattern("d-MMM-yyyy"),
+ DateTimeFormatter.ofPattern("dd-MM-yyyy"),
+ DateTimeFormatter.ofPattern("d-MM-yyyy"),
+ DateTimeFormatter.ofPattern("MM-dd-yyyy"),
+ DateTimeFormatter.ofPattern("MM-d-yyyy"),
+ DateTimeFormatter.ofPattern("yyyy-MM-dd"),
+ DateTimeFormatter.ofPattern("yyyy-MM-d"),
+ DateTimeFormatter.ofPattern("yyyy-MM-dd"),
+ DateTimeFormatter.ofPattern("MMM-d-yyyy HHmm"),
+ DateTimeFormatter.ofPattern("MMM-dd-yyyy HHmm"),
+ DateTimeFormatter.ofPattern("d-MMM-yyyy HHmm"),
+ DateTimeFormatter.ofPattern("dd-MM-yyyy HHmm"),
+ DateTimeFormatter.ofPattern("d-MM-yyyy HHmm"),
+ DateTimeFormatter.ofPattern("MM-dd-yyyy HHmm"),
+ DateTimeFormatter.ofPattern("MM-d-yyyy HHmm"),
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"),
+ DateTimeFormatter.ofPattern("yyyy-MM-d HHmm"),
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"),
+ DateTimeFormatter.ofPattern("HHmm MMM-d-yyyy"),
+ DateTimeFormatter.ofPattern("HHmm MMM-dd-yyyy"),
+ DateTimeFormatter.ofPattern("HHmm d-MMM-yyyy"),
+ DateTimeFormatter.ofPattern("HHmm dd-MM-yyyy"),
+ DateTimeFormatter.ofPattern("HHmm d-MM-yyyy"),
+ DateTimeFormatter.ofPattern("HHmm MM-dd-yyyy"),
+ DateTimeFormatter.ofPattern("HHmm MM-d-yyyy"),
+ DateTimeFormatter.ofPattern("HHmm yyyy-MM-dd"),
+ DateTimeFormatter.ofPattern("HHmm yyyy-MM-d"),
+ DateTimeFormatter.ofPattern("HHmm yyyy-MM-dd"),
+
+ DateTimeFormatter.ofPattern("MMM d yyyy"),
+ DateTimeFormatter.ofPattern("MMM dd yyyy"),
+ DateTimeFormatter.ofPattern("d MMM yyyy"),
+ DateTimeFormatter.ofPattern("dd MM yyyy"),
+ DateTimeFormatter.ofPattern("d MM yyyy"),
+ DateTimeFormatter.ofPattern("MM dd yyyy"),
+ DateTimeFormatter.ofPattern("MM d yyyy"),
+ DateTimeFormatter.ofPattern("yyyy MM dd"),
+ DateTimeFormatter.ofPattern("yyyy MM d"),
+ DateTimeFormatter.ofPattern("yyyy MM dd"),
+ DateTimeFormatter.ofPattern("MMM d yyyy HHmm"),
+ DateTimeFormatter.ofPattern("MMM dd yyyy HHmm"),
+ DateTimeFormatter.ofPattern("d MMM yyyy HHmm"),
+ DateTimeFormatter.ofPattern("dd MM yyyy HHmm"),
+ DateTimeFormatter.ofPattern("d MM yyyy HHmm"),
+ DateTimeFormatter.ofPattern("MM dd yyyy HHmm"),
+ DateTimeFormatter.ofPattern("MM d yyyy HHmm"),
+ DateTimeFormatter.ofPattern("yyyy MM dd HHmm"),
+ DateTimeFormatter.ofPattern("yyyy MM d HHmm"),
+ DateTimeFormatter.ofPattern("yyyy MM dd HHmm"),
+ DateTimeFormatter.ofPattern("HHmm MMM d yyyy"),
+ DateTimeFormatter.ofPattern("HHmm MMM dd yyyy"),
+ DateTimeFormatter.ofPattern("HHmm d MMM yyyy"),
+ DateTimeFormatter.ofPattern("HHmm dd MM yyyy"),
+ DateTimeFormatter.ofPattern("HHmm d MM yyyy"),
+ DateTimeFormatter.ofPattern("HHmm MM dd yyyy"),
+ DateTimeFormatter.ofPattern("HHmm MM d yyyy"),
+ DateTimeFormatter.ofPattern("HHmm yyyy MM dd"),
+ DateTimeFormatter.ofPattern("HHmm yyyy MM d"),
+ DateTimeFormatter.ofPattern("HHmm yyyy MM dd"),
+
+ DateTimeFormatter.ofPattern("MMM/d/yyyy"),
+ DateTimeFormatter.ofPattern("MMM/dd/yyyy"),
+ DateTimeFormatter.ofPattern("d/MMM/yyyy"),
+ DateTimeFormatter.ofPattern("dd/MM/yyyy"),
+ DateTimeFormatter.ofPattern("d/MM/yyyy"),
+ DateTimeFormatter.ofPattern("MM/dd/yyyy"),
+ DateTimeFormatter.ofPattern("MM/d/yyyy"),
+ DateTimeFormatter.ofPattern("yyyy/MM/dd"),
+ DateTimeFormatter.ofPattern("yyyy/MM/d"),
+ DateTimeFormatter.ofPattern("yyyy/MM/dd"),
+ DateTimeFormatter.ofPattern("MMM/d/yyyy HHmm"),
+ DateTimeFormatter.ofPattern("MMM/dd/yyyy HHmm"),
+ DateTimeFormatter.ofPattern("d/MMM/yyyy HHmm"),
+ DateTimeFormatter.ofPattern("dd/MM/yyyy HHmm"),
+ DateTimeFormatter.ofPattern("d/MM/yyyy HHmm"),
+ DateTimeFormatter.ofPattern("MM/dd/yyyy HHmm"),
+ DateTimeFormatter.ofPattern("MM/d/yyyy HHmm"),
+ DateTimeFormatter.ofPattern("yyyy/MM/dd HHmm"),
+ DateTimeFormatter.ofPattern("yyyy/MM/d HHmm"),
+ DateTimeFormatter.ofPattern("yyyy/MM/dd HHmm"),
+ DateTimeFormatter.ofPattern("HHmm MMM/d yyyy"),
+ DateTimeFormatter.ofPattern("HHmm MMM/dd yyyy"),
+ DateTimeFormatter.ofPattern("HHmm d/MMM/yyyy"),
+ DateTimeFormatter.ofPattern("HHmm dd/MM/yyyy"),
+ DateTimeFormatter.ofPattern("HHmm d/MM/yyyy"),
+ DateTimeFormatter.ofPattern("HHmm MM/dd/yyyy"),
+ DateTimeFormatter.ofPattern("HHmm MM/d/yyyy"),
+ DateTimeFormatter.ofPattern("HHmm yyyy/MM/dd"),
+ DateTimeFormatter.ofPattern("HHmm yyyy/MM/d"),
+ DateTimeFormatter.ofPattern("HHmm yyyy/MM/dd"),
+ };
+
+ /**
+ * Takes in a string containing the date and attempts to find a format that the date is written in
+ * @param date string of the date data
+ * @return DateTimeFormatter containing the corresponding date format
+ * @throws InvalidDateException if no appropriate date format is found
+ */
+ private static DateTimeFormatter findDateFormat(String date) throws InvalidDateException {
+ for (DateTimeFormatter format: FORMATS) {
+ if (hasSameDateFormat(date, format)) {
+ return format;
+ }
+ }
+ throw new InvalidDateException();
+ }
+
+ /**
+ * Checks if the date string has the same date format in the formatter
+ * @param date string of the date data
+ * @param formatter date format to be checked with
+ * @return boolean indicating if its the same format or not
+ */
+ private static boolean hasSameDateFormat(String date, DateTimeFormatter formatter) {
+ try {
+ LocalDate.parse(date, formatter);
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Takes in a string containing date, validates if its a valid date and then
+ * returns the date in a standardized format
+ * @param date string containing the user input date
+ * @return date string in a standardised format
+ * @throws InvalidDateException if date is an invalid date
+ */
+ public static String formatDate(String date) throws InvalidDateException {
+ DateTimeFormatter stdFormat = DateTimeFormatter.ofPattern("dd-MMM-yyyy");
+ DateTimeFormatter currentFormat = findDateFormat(date);
+ return LocalDate.parse(date, currentFormat).format(stdFormat);
+ }
+
+ /**
+ * Takes in a string containing time, validates if its a valid time and then
+ * returns the date/time in a standardized format
+ * @param date string date input by user
+ * @param time string time input by user
+ * @return date and time string in standardised format
+ * @throws InvalidTimeException if input time is not a valid time
+ * @throws InvalidDateException if date is not a valid date
+ */
+ public static String formatTime(String date, String time) throws InvalidTimeException, InvalidDateException {
+ DateTimeFormatter stdFormat = DateTimeFormatter.ofPattern("dd-MMM-yyyy HHmm");
+ DateTimeFormatter currentFormat = findDateFormat(date);
+ LocalDate lDate = LocalDate.parse(date, currentFormat);
+ int intTime = Integer.parseInt(time);
+ int hour = (int) Math.floor(intTime / 100.0);
+ int minute = intTime - (hour * 100);
+ try {
+ return lDate.atTime(hour, minute).format(stdFormat);
+ } catch (DateTimeException e) {
+ throw new InvalidTimeException();
+ }
+
+ }
+}
diff --git a/src/main/java/process/ComplexProcess.java b/src/main/java/process/ComplexProcess.java
new file mode 100644
index 0000000000..a9a73769e5
--- /dev/null
+++ b/src/main/java/process/ComplexProcess.java
@@ -0,0 +1,18 @@
+package process;
+
+/**
+ * An interface to define the behavior of a process that requires multiple inputs
+ */
+public interface ComplexProcess extends Process {
+ /**
+ * Returns a string message to instruct the user on the first step in the process
+ * @return string message of first set of instructions to user
+ */
+ public String firstInstruction();
+
+ /**
+ * Getter method to check if process is completed
+ * @return whether the process is complete (true) or not complete (false)
+ */
+ public boolean isComplete();
+}
diff --git a/src/main/java/process/Deadline.java b/src/main/java/process/Deadline.java
new file mode 100644
index 0000000000..4ed671ac7f
--- /dev/null
+++ b/src/main/java/process/Deadline.java
@@ -0,0 +1,82 @@
+package process;
+
+import exception.InvalidDateException;
+import exception.InvalidTimeException;
+import parser.Time;
+import task.Deadlines;
+import task.TaskManager;
+
+/**
+ * A class for the process of creating a Deadline Task object
+ */
+public class Deadline implements ComplexProcess {
+ private enum Stage {
+ FIRST, SECOND, THIRD
+ }
+ private TaskManager tasks = TaskManager.init();
+ private Stage stage = Stage.FIRST;
+ private String name = null;
+ private String deadline = null;
+ private boolean isComplete = false;
+
+ @Override
+ public String processInput(String input) {
+ String reply = "";
+
+ switch (stage) {
+ case FIRST:
+ reply = processFirstStep(input);
+ break;
+ case SECOND:
+ reply = processSecondStep(input);
+ break;
+ case THIRD:
+ reply = processLastStep(input);
+ break;
+ default:
+ assert false : "should not reach default case in deadline.java";
+ }
+ return reply;
+ }
+
+ private String processFirstStep(String input) {
+ name = input;
+ stage = Stage.SECOND;
+ return "Now indicate the deadline date.";
+ }
+
+ private String processSecondStep(String input) {
+ try {
+ deadline = Time.formatDate(input);
+ } catch (InvalidDateException e) {
+ return e.toString();
+ }
+ stage = Stage.THIRD;
+ return "Indicate a start time ranging from 0000 - 2359. "
+ + "You may enter 'Skip' to not indicate a time";
+ }
+
+ private String processLastStep(String input) {
+ if (!input.toLowerCase().equals(enums.Command.SKIP.toString())) {
+ try {
+ deadline = Time.formatTime(deadline, input);
+ } catch (InvalidTimeException | InvalidDateException e) {
+ return e.toString();
+ } catch (NumberFormatException e) {
+ return "Non-numerical characters detected. Please enter numbers only.";
+ }
+ }
+ isComplete = true;
+ return tasks.addTask(new Deadlines(name, deadline));
+ }
+
+ @Override
+ public String firstInstruction() {
+ return "So you want to add a task with deadline. Tell me what's the task.";
+ }
+
+ @Override
+ public boolean isComplete() {
+ return isComplete;
+ }
+}
diff --git a/src/main/java/process/Delete.java b/src/main/java/process/Delete.java
new file mode 100644
index 0000000000..d99d90b1e1
--- /dev/null
+++ b/src/main/java/process/Delete.java
@@ -0,0 +1,29 @@
+package process;
+
+import exception.InvalidCommandException;
+import exception.MissingArgumentException;
+import parser.CommandParser;
+import task.TaskManager;
+
+/**
+ * A class for the process of creating a Delete Task object
+ */
+public class Delete implements SimpleProcess {
+ private TaskManager tasks = TaskManager.init();
+ @Override
+ public String processInput(String input) {
+ assert input.toLowerCase().startsWith("delete") : "user input does not start with the correct word";
+ try {
+ String number = CommandParser.getCommandArguments(input);
+ return tasks.deleteTask(Integer.parseInt(number));
+ } catch (MissingArgumentException e) {
+ return e.toString();
+ } catch (InvalidCommandException e) {
+ return e.toString();
+ } catch (NumberFormatException e) {
+ return "Strictly type 1 number only";
+ } catch (IndexOutOfBoundsException e) {
+ return "Index number does not exist in our list";
+ }
+ }
+}
diff --git a/src/main/java/process/Event.java b/src/main/java/process/Event.java
new file mode 100644
index 0000000000..cbc09262bd
--- /dev/null
+++ b/src/main/java/process/Event.java
@@ -0,0 +1,115 @@
+package process;
+
+import exception.InvalidDateException;
+import exception.InvalidTimeException;
+import parser.Time;
+import task.Events;
+import task.TaskManager;
+
+/**
+ * A class for the process of creating an event task
+ */
+public class Event implements ComplexProcess {
+ private enum Stage {
+ FIRST, SECOND, THIRD, FOURTH, FIFTH
+ }
+ private TaskManager tasks = TaskManager.init();
+ private Stage stage = Stage.FIRST;
+ private String name = null;
+ private String from = null;
+ private String to = null;
+ private boolean isComplete = false;
+
+ @Override
+ public String processInput(String input) {
+ String reply = "";
+ switch (stage) {
+ case FIRST:
+ reply = processFirstStep(input);
+ break;
+ case SECOND:
+ reply = processSecondStep(input);
+ break;
+ case THIRD:
+ reply = processThirdStep(input);
+ break;
+ case FOURTH:
+ reply = processFourthStep(input);
+ break;
+ case FIFTH:
+ reply = processLastStep(input);
+ break;
+ default:
+ assert false : "should not reach default case in event.java";
+ }
+ return reply;
+ }
+
+ @Override
+ public String firstInstruction() {
+ return "So you want to add an event task. Tell me what's the task.";
+ }
+
+ @Override
+ public boolean isComplete() {
+ return isComplete;
+ }
+
+ private String processFirstStep(String name) {
+ this.name = name;
+ stage = Stage.SECOND;
+ return "Now indicate the start date.";
+ }
+
+ private String processSecondStep(String date) {
+ try {
+ from = Time.formatDate(date);
+ } catch (InvalidDateException e) {
+ return e.toString();
+ }
+ stage = Stage.THIRD;
+ return "Indicate a start time ranging from 0000 - 2359. "
+ + "You may enter 'Skip' to not indicate a time";
+ }
+
+ private String processFourthStep(String date) {
+ try {
+ to = Time.formatDate(date);
+ } catch (InvalidDateException e) {
+ return e.toString();
+ }
+ stage = Stage.FIFTH;
+ return "Indicate an end time ranging from 0000 - 2359. "
+ + "You may enter 'Skip' to not indicate a time";
+ }
+
+ private String processThirdStep(String input) {
+ if (!input.toLowerCase().equals(enums.Command.SKIP.toString())) {
+ try {
+ from = Time.formatTime(from, input);
+ } catch (InvalidTimeException | InvalidDateException e) {
+ return e.toString();
+ } catch (NumberFormatException e) {
+ return "Non-numerical characters detected. Please enter numbers only. "
+ + "Returning to homepage...";
+ }
+ }
+ stage = Stage.FOURTH;
+ return "Now indicate the end date.";
+ }
+
+ private String processLastStep(String input) {
+ if (!input.toLowerCase().equals(enums.Command.SKIP.toString())) {
+ try {
+ to = Time.formatTime(to, input);
+ } catch (InvalidTimeException | InvalidDateException e) {
+ return e.toString();
+ } catch (NumberFormatException e) {
+ return "Non-numerical characters detected. Please enter numbers only. "
+ + "Returning to homepage...";
+ }
+ }
+ isComplete = true;
+ return tasks.addTask(new Events(name, from, to));
+ }
+}
diff --git a/src/main/java/process/Find.java b/src/main/java/process/Find.java
new file mode 100644
index 0000000000..cbc75ddc75
--- /dev/null
+++ b/src/main/java/process/Find.java
@@ -0,0 +1,34 @@
+package process;
+
+import exception.InvalidCommandException;
+import exception.InvalidInputException;
+import exception.MissingArgumentException;
+import parser.CommandParser;
+import task.TaskManager;
+
+/**
+ * A class for the process of creating a find task
+ */
+public class Find implements SimpleProcess {
+ private TaskManager tasks = TaskManager.init();
+
+ @Override
+ public String processInput(String input) {
+ assert input.toLowerCase().startsWith("find") : "User input does not start with the correct word";
+
+ try {
+ String argument = CommandParser.getCommandArguments(input);
+ String[] split = argument.split(" ");
+
+ //Checks if the number of arguments is correct
+ if (split.length > 1) {
+ throw new InvalidInputException();
+ }
+
+ //Performs the task and returns to response log of task
+ return tasks.findTask(argument);
+ } catch (MissingArgumentException | InvalidCommandException | InvalidInputException e) {
+ return e.toString();
+ }
+ }
+}
diff --git a/src/main/java/process/Mark.java b/src/main/java/process/Mark.java
new file mode 100644
index 0000000000..6f20b1ec03
--- /dev/null
+++ b/src/main/java/process/Mark.java
@@ -0,0 +1,29 @@
+package process;
+
+import exception.InvalidCommandException;
+import exception.MissingArgumentException;
+import parser.CommandParser;
+import task.TaskManager;
+
+/**
+ * A class for the process of creating a mark task
+ */
+public class Mark implements SimpleProcess {
+ private TaskManager tasks = TaskManager.init();
+
+ @Override
+ public String processInput(String input) {
+ assert input.toLowerCase().startsWith("mark") : "user input does not start with the correct word";
+
+ try {
+ String number = CommandParser.getCommandArguments(input);
+ return tasks.markDone(Integer.parseInt(number));
+ } catch (NumberFormatException e) {
+ return "Strictly type 1 number only";
+ } catch (IndexOutOfBoundsException e) {
+ return "Index number does not exist in our list";
+ } catch (MissingArgumentException | InvalidCommandException e) {
+ return e.toString();
+ }
+ }
+}
diff --git a/src/main/java/process/Process.java b/src/main/java/process/Process.java
new file mode 100644
index 0000000000..c4b1f1de47
--- /dev/null
+++ b/src/main/java/process/Process.java
@@ -0,0 +1,14 @@
+package process;
+
+/**
+ * An interface to define the behavior of a process of handling user input
+ */
+public interface Process {
+ /**
+ * Takes in a user input, validates it and processes the input in accordance to the respective task behavior.
+ * Then finally returns a string message response to be displayed for the user
+ * @param input String message captured by the user
+ * @return String message of appropriate response for the user input
+ */
+ public String processInput(String input);
+}
diff --git a/src/main/java/process/SimpleProcess.java b/src/main/java/process/SimpleProcess.java
new file mode 100644
index 0000000000..606889f1de
--- /dev/null
+++ b/src/main/java/process/SimpleProcess.java
@@ -0,0 +1,7 @@
+package process;
+
+/**
+ * An interface to define the behavior of a process that can be finished in one input
+ */
+public interface SimpleProcess extends Process {
+}
diff --git a/src/main/java/process/ToDo.java b/src/main/java/process/ToDo.java
new file mode 100644
index 0000000000..8634a6f44b
--- /dev/null
+++ b/src/main/java/process/ToDo.java
@@ -0,0 +1,26 @@
+package process;
+
+import task.TaskManager;
+
+/**
+ * A class for the process of creating a todo task
+ */
+public class ToDo implements ComplexProcess {
+ private TaskManager tasks = TaskManager.init();
+ private boolean isComplete = false;
+ @Override
+ public String firstInstruction() {
+ return "So you want to add a ToDo task. Tell me what's the task.";
+ }
+
+ @Override
+ public boolean isComplete() {
+ return isComplete;
+ }
+
+ @Override
+ public String processInput(String input) {
+ isComplete = true;
+ return tasks.addTask(new task.ToDo(input));
+ }
+}
diff --git a/src/main/java/process/Unmark.java b/src/main/java/process/Unmark.java
new file mode 100644
index 0000000000..6a76b9d937
--- /dev/null
+++ b/src/main/java/process/Unmark.java
@@ -0,0 +1,28 @@
+package process;
+
+import exception.InvalidCommandException;
+import exception.MissingArgumentException;
+import parser.CommandParser;
+import task.TaskManager;
+
+/**
+ * A class for the process of creating a unmark task
+ */
+public class Unmark implements SimpleProcess {
+ private TaskManager tasks = TaskManager.init();
+
+ @Override
+ public String processInput(String input) {
+ assert input.toLowerCase().startsWith("unmark") : "user input does not start with the correct word";
+ try {
+ String number = CommandParser.getCommandArguments(input);
+ return tasks.unmarkDone(Integer.parseInt(number));
+ } catch (NumberFormatException e) {
+ return "Strictly type 1 number only";
+ } catch (IndexOutOfBoundsException e) {
+ return "Index number does not exist in our list";
+ } catch (MissingArgumentException | InvalidCommandException e) {
+ return e.toString();
+ }
+ }
+}
diff --git a/src/main/java/storage/Database.java b/src/main/java/storage/Database.java
new file mode 100644
index 0000000000..a7703f55b7
--- /dev/null
+++ b/src/main/java/storage/Database.java
@@ -0,0 +1,107 @@
+package storage;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import enums.Command;
+import task.Deadlines;
+import task.Events;
+import task.Task;
+import task.ToDo;
+
+/**
+ * Class to interface with the text file storing the user task records
+ */
+public class Database {
+
+ /**
+ * Takes in a list of tasks and overwrites it onto the text file
+ * @param list list of tasks
+ * @throws IOException from file handling errors
+ */
+ public static void saveTasks(ArrayList list) throws IOException {
+ //Open file and feed it into writer
+ File file = getDataFile();
+ FileWriter fileToWrite = new FileWriter(file);
+
+ //Write all contents from the local list into file
+ Task[] tasks = list.toArray(new Task[0]);
+ for (int i = 0; i < tasks.length; i++) {
+ fileToWrite.write(tasks[i].dataFormat());
+ fileToWrite.write(System.lineSeparator());
+ }
+
+ fileToWrite.close();
+ }
+
+ /**
+ * Retrieves the list of task records from the text file and returns it in an arraylist
+ * @return list of tasks saved in the text file
+ * @throws IOException from file handling errors
+ * @throws ArrayIndexOutOfBoundsException for file data corruption errors
+ */
+ public static ArrayList loadTasks() throws IOException, ArrayIndexOutOfBoundsException {
+ ArrayList list = new ArrayList<>();
+
+ //Open the required file and feed it into the reader
+ File file = getDataFile();
+ FileReader fileReader = new FileReader(file);
+ BufferedReader fileToRead = new BufferedReader(fileReader);
+ String nextLine = fileToRead.readLine();
+
+ //Read and add all contents into file
+ while (nextLine != null) {
+ String[] data = nextLine.split("/");
+ String taskType = data[0];
+ boolean status = data[1].equals("true");
+ if (taskType.equals(Command.TODO.toString())) {
+ list.add(new ToDo(status, data[2]));
+ } else if (taskType.equals(Command.DEADLINE.toString())) {
+ list.add(new Deadlines(status, data[2], data[3]));
+ } else if (taskType.equals(Command.EVENT.toString())) {
+ list.add(new Events(status, data[2], data[3], data[4]));
+ }
+ nextLine = fileToRead.readLine();
+ }
+
+ fileReader.close();
+ fileToRead.close();
+ return list;
+ }
+
+ private static File getDataDirectory() {
+ String jarPath = Database.class
+ .getProtectionDomain()
+ .getCodeSource()
+ .getLocation()
+ .getPath();
+ File jarFile = new File(jarPath);
+ File parentDirectory = jarFile.getParentFile();
+ File dataDirectory = new File(parentDirectory, "save_data");
+
+ // Check if the directory exists. If not, create it.
+ if (!dataDirectory.exists()) {
+ dataDirectory.mkdirs(); // This will create the directory.
+ }
+ return dataDirectory;
+ }
+
+ private static File getDataFile() {
+ File directory = getDataDirectory();
+ File file = new File(directory, "tasklist.txt");
+
+ // Check if the file exists. If not, create it.
+ if (!file.exists()) {
+ try {
+ file.createNewFile(); // This will create the file.
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return file;
+ }
+}
diff --git a/src/main/java/task/Deadlines.java b/src/main/java/task/Deadlines.java
new file mode 100644
index 0000000000..bf96774f97
--- /dev/null
+++ b/src/main/java/task/Deadlines.java
@@ -0,0 +1,50 @@
+package task;
+
+/**
+ * Object class for instances of deadline tasks
+ */
+public class Deadlines extends Task {
+ protected String by;
+
+ /**
+ * Public constructor of the class
+ * @param description description of the task
+ * @param by date of the deadline
+ */
+ public Deadlines(String description, String by) {
+ super(description);
+ this.by = by;
+ }
+
+ /**
+ * Overloaded constructor to create classes with known status
+ * @param status done status of task
+ * @param description description of the task
+ * @param by date of the deadline
+ */
+ public Deadlines(boolean status, String description, String by) {
+ super(status, description);
+ this.by = by;
+ }
+
+ /**
+ * Overriden toString implementation for a deadline task
+ * @return String representing a deadline task
+ */
+ @Override
+ public String toString() {
+ return "[D]" + super.toString() + " (by: " + by + ")";
+ }
+
+ /**
+ * formats the properties of the object into a single string line for saving
+ * @return String of formatted properties
+ */
+ @Override
+ public String dataFormat() {
+ return "deadline/"
+ + super.isDone.toString() + "/"
+ + super.description + "/"
+ + this.by;
+ }
+}
diff --git a/src/main/java/task/Events.java b/src/main/java/task/Events.java
new file mode 100644
index 0000000000..814d648840
--- /dev/null
+++ b/src/main/java/task/Events.java
@@ -0,0 +1,56 @@
+package task;
+
+/**
+ * Object class for instances of event tasks
+ */
+public class Events extends Task {
+ protected String from;
+ protected String to;
+
+ /**
+ * Public constructor of the class
+ * @param description description of the task
+ * @param from start date
+ * @param to end date
+ */
+ public Events(String description, String from, String to) {
+ super(description);
+ this.from = from;
+ this.to = to;
+ }
+
+ /**
+ * Overloaded constructor of the class for a known status
+ * @param status status of the task
+ * @param description description of the task
+ * @param from start date
+ * @param to end date
+ */
+ public Events(boolean status, String description, String from, String to) {
+ super(status, description);
+ this.from = from;
+ this.to = to;
+ }
+
+ /**
+ * Overriden toString implementation for a event task
+ * @return String representing a event task
+ */
+ @Override
+ public String toString() {
+ return "[E]" + super.toString() + " (from: " + from + " to: " + to + ")";
+ }
+
+ /**
+ * formats the properties of the object into a single string line for saving
+ * @return String of formatted properties
+ */
+ @Override
+ public String dataFormat() {
+ return "event/"
+ + super.isDone.toString()
+ + "/" + super.description + "/"
+ + this.from + "/"
+ + this.to;
+ }
+}
diff --git a/src/main/java/task/Task.java b/src/main/java/task/Task.java
new file mode 100644
index 0000000000..cf2706c9e4
--- /dev/null
+++ b/src/main/java/task/Task.java
@@ -0,0 +1,65 @@
+package task;
+
+/**
+ * Abstract class for all possible tasks
+ */
+public abstract class Task {
+ protected String description;
+ protected Boolean isDone;
+
+ /**
+ * Public constructor of the class
+ * @param description description of the task
+ */
+ public Task(String description) {
+ this.description = description;
+ this.isDone = false;
+ }
+
+ /**
+ * Overloaded constructor to create classes with known status
+ * @param status done status of task
+ * @param description description of the task
+ */
+ public Task(boolean status, String description) {
+ this.description = description;
+ this.isDone = status;
+ }
+
+ /**
+ * Churns out a string 'X' if its complete and ' ' otherwise
+ * @return display icon
+ */
+ public String getStatusIcon() {
+ return (isDone ? "X" : " ");
+ }
+
+ /**
+ * marks this task as done
+ */
+ public void markAsDone() {
+ this.isDone = true;
+ }
+
+ /**
+ * marks this task as not done
+ */
+ public void markAsNotDone() {
+ this.isDone = false;
+ }
+
+ /**
+ * Overriden toString implementation for a generic task
+ * @return String representing a generic task
+ */
+ @Override
+ public String toString() {
+ return "[" + getStatusIcon() + "] " + description;
+ }
+
+ /**
+ * abstract declaration of a method to format properties of the instance
+ * @return String of formatted data of the instance
+ */
+ public abstract String dataFormat();
+}
diff --git a/src/main/java/task/TaskManager.java b/src/main/java/task/TaskManager.java
new file mode 100644
index 0000000000..b2d498abe4
--- /dev/null
+++ b/src/main/java/task/TaskManager.java
@@ -0,0 +1,181 @@
+package task;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import storage.Database;
+
+/**
+ * Class for manipulating the list of tasks
+ */
+public class TaskManager {
+ private static TaskManager obj;
+ private static final String FILE_ERROR_MSG = "There is an issue with the file database. "
+ + "You are required to delete the file in your User/[username]/EvanData folder "
+ + "and rerun the program.";
+ private ArrayList list;
+
+ /**
+ * Private constructor for TaskManager
+ */
+ private TaskManager() {
+ try {
+ list = Database.loadTasks();
+ } catch (IOException | ArrayIndexOutOfBoundsException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ /**
+ * Factory method to enforce one instance of the list manager
+ * @return an instance of the list manager
+ */
+ public static TaskManager init() {
+ if (obj == null) {
+ obj = new TaskManager();
+ }
+ return obj;
+ }
+
+ /**
+ * Takes in a task, adds it into the list and saves it into the database
+ * @param task task to be added
+ * @return string message log of adding the task
+ */
+ public String addTask(Task task) {
+ list.add(task);
+
+ try {
+ Database.saveTasks(list);
+ } catch (IOException e) {
+ return FILE_ERROR_MSG;
+ }
+
+ StringBuilder dialog = new StringBuilder("Got it. I've added this task:\n")
+ .append(task)
+ .append("\n")
+ .append("Now you have ")
+ .append(list.size())
+ .append(" tasks in the list.");
+ return dialog.toString();
+ }
+
+ /**
+ * Gets a string report of all the tasks in the list
+ * @return String message log of all tasks in the list
+ */
+ public String getAllTasks() {
+ Task[] tasks = list.toArray(new Task[0]);
+ StringBuilder dialog = new StringBuilder("Here are the tasks in your list:\n");
+
+ for (int i = 0; i < tasks.length; i++) {
+ int listIndex = i + 1;
+ Task task = tasks[i];
+ dialog.append(listIndex)
+ .append(".");
+
+ if (i < tasks.length - 1) {
+ dialog.append(task)
+ .append("\n");
+ } else {
+ dialog.append(task);
+ }
+ }
+
+ return dialog.toString();
+ }
+
+ /**
+ * Takes in an integer index and marks the task associated with the index as done
+ * @param index index of the task
+ * @return String message report of marking the task as done
+ */
+ public String markDone(int index) {
+ Task element = list.get(index - 1);
+ element.markAsDone();
+
+ try {
+ Database.saveTasks(list);
+ } catch (IOException e) {
+ return FILE_ERROR_MSG;
+ }
+
+ StringBuilder dialog = new StringBuilder();
+ dialog.append("Nice! I've marked this task as done:\n")
+ .append(element);
+ return dialog.toString();
+ }
+
+ /**
+ * Takes in an integer index and marks the task associated with the index as not done
+ * @param index index of the task
+ * @return String message report of marking the task as not done
+ */
+ public String unmarkDone(int index) {
+ Task element = list.get(index - 1);
+ element.markAsNotDone();
+
+ try {
+ Database.saveTasks(list);
+ } catch (IOException e) {
+ return FILE_ERROR_MSG;
+ }
+
+ StringBuilder dialog = new StringBuilder();
+ dialog.append("OK! I've marked this task as not done yet:\n")
+ .append(element);
+ return dialog.toString();
+ }
+
+ /**
+ * Takes in an integer index and deletes the task associated with the index
+ * @param index index of the task
+ * @return String message report of deleting task
+ */
+ public String deleteTask(int index) {
+
+ Task element = list.get(index - 1);
+ list.remove(index - 1);
+
+ try {
+ Database.saveTasks(list);
+ } catch (IOException e) {
+ return FILE_ERROR_MSG;
+ }
+
+ StringBuilder dialog = new StringBuilder();
+ dialog.append("Noted. I've removed this task:\n")
+ .append(element)
+ .append("\n")
+ .append("Now you have ")
+ .append(list.size())
+ .append(" tasks in the list.");
+ return dialog.toString();
+ }
+
+ /**
+ * Finds all tasks which contains the keyword and returns a string report of filtered tasks
+ * @param keyword in the task description
+ * @return String message report of all tasks with the keyword
+ */
+ public String findTask(String keyword) {
+ StringBuilder dialog = new StringBuilder("Here are the matching tasks in your list with its correct "
+ + "corresponding index numbers: \n");
+
+ Task[] tasks = list.toArray(new Task[0]);
+
+ for (int i = 0; i < tasks.length; i++) {
+ Task task = tasks[i];
+ int listIndex = i + 1;
+
+ if (task.description.contains(keyword)) {
+ dialog.append(listIndex)
+ .append(".")
+ .append(task)
+ .append("\n");
+ }
+ }
+ return dialog.toString();
+ }
+}
diff --git a/src/main/java/task/ToDo.java b/src/main/java/task/ToDo.java
new file mode 100644
index 0000000000..683be4798f
--- /dev/null
+++ b/src/main/java/task/ToDo.java
@@ -0,0 +1,44 @@
+package task;
+
+/**
+ * Object class for instances of todo tasks
+ */
+public class ToDo extends Task {
+
+ /**
+ * Public constructor of the class
+ * @param description description of the task
+ */
+ public ToDo(String description) {
+ super(description);
+ }
+
+ /**
+ * Overloaded constructor to create classes with known status
+ * @param status done status of task
+ * @param description description of the task
+ */
+ public ToDo(boolean status, String description) {
+ super(status, description);
+ }
+
+ /**
+ * Overriden toString implementation for a todo task
+ * @return String representing a todo task
+ */
+ @Override
+ public String toString() {
+ return "[T]" + super.toString();
+ }
+
+ /**
+ * abstract declaration of a method to format properties of the todo task
+ * @return String of formatted data of the instance
+ */
+ @Override
+ public String dataFormat() {
+ return "todo/"
+ + isDone.toString() + "/"
+ + description;
+ }
+}
diff --git a/src/main/java/ui/DialogBox.java b/src/main/java/ui/DialogBox.java
new file mode 100644
index 0000000000..1973e704c9
--- /dev/null
+++ b/src/main/java/ui/DialogBox.java
@@ -0,0 +1,61 @@
+package ui;
+
+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;
+
+/**
+ * An example of a custom control using FXML.
+ * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label
+ * containing text from the speaker.
+ */
+public class DialogBox extends HBox {
+ @FXML
+ private Label dialog;
+ @FXML
+ private ImageView displayPicture;
+
+ 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);
+ displayPicture.setImage(img);
+ }
+
+ /**
+ * 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 getEvanDialog(String text, Image img) {
+ var db = new DialogBox(text, img);
+ db.flip();
+ return db;
+ }
+}
diff --git a/src/main/java/ui/Launcher.java b/src/main/java/ui/Launcher.java
new file mode 100644
index 0000000000..6982c7c6c5
--- /dev/null
+++ b/src/main/java/ui/Launcher.java
@@ -0,0 +1,12 @@
+package ui;
+
+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/ui/Main.java b/src/main/java/ui/Main.java
new file mode 100644
index 0000000000..ec9ff5c0c0
--- /dev/null
+++ b/src/main/java/ui/Main.java
@@ -0,0 +1,37 @@
+package ui;
+
+import java.io.IOException;
+
+import chatbot.evan.Evan;
+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 Evan evan = new Evan();
+
+ @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);
+ MainWindow window = fxmlLoader.getController();
+ window.setEvan(evan);
+ window.onStart();
+ stage.show();
+
+ //Initialise introductory message
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/ui/MainWindow.java b/src/main/java/ui/MainWindow.java
new file mode 100644
index 0000000000..dc6b720b7f
--- /dev/null
+++ b/src/main/java/ui/MainWindow.java
@@ -0,0 +1,63 @@
+package ui;
+
+import chatbot.evan.Evan;
+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 Evan evan;
+
+ private Image userImage = new Image(this.getClass().getResourceAsStream("/images/smol.png"));
+ private Image evanImage = new Image(this.getClass().getResourceAsStream("/images/big.png"));
+
+ @FXML
+ public void initialize() {
+ scrollPane.vvalueProperty().bind(dialogContainer.heightProperty());
+ }
+
+ public void setEvan(Evan d) {
+ evan = 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();
+ String response = evan.getResponse(input);
+ dialogContainer.getChildren().addAll(
+ DialogBox.getUserDialog(input, userImage),
+ DialogBox.getEvanDialog(response, evanImage)
+ );
+ userInput.clear();
+ }
+
+ /**
+ * Display chatbot introductory message upon application start
+ */
+ public void onStart() {
+ String message = evan.getIntro();
+ dialogContainer.getChildren().addAll(
+ DialogBox.getEvanDialog(message, evanImage)
+ );
+ }
+}
diff --git a/src/main/resources/images/big.png b/src/main/resources/images/big.png
new file mode 100644
index 0000000000..7cd2ef6d11
Binary files /dev/null and b/src/main/resources/images/big.png differ
diff --git a/src/main/resources/images/smol.png b/src/main/resources/images/smol.png
new file mode 100644
index 0000000000..fc042e735c
Binary files /dev/null and b/src/main/resources/images/smol.png differ
diff --git a/src/main/resources/images/user.png b/src/main/resources/images/user.png
new file mode 100644
index 0000000000..b0996709fc
Binary files /dev/null and b/src/main/resources/images/user.png differ
diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml
new file mode 100644
index 0000000000..59d16ac91a
--- /dev/null
+++ b/src/main/resources/view/DialogBox.fxml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
new file mode 100644
index 0000000000..40813675a0
--- /dev/null
+++ b/src/main/resources/view/MainWindow.fxml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/parser/CommandParserTest.java b/src/test/java/parser/CommandParserTest.java
new file mode 100644
index 0000000000..614353b0ac
--- /dev/null
+++ b/src/test/java/parser/CommandParserTest.java
@@ -0,0 +1,59 @@
+package parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+import exception.InvalidCommandException;
+import exception.MissingArgumentException;
+
+
+public class CommandParserTest {
+ @Test
+ public void testBlankSpaces() {
+ assertThrows(MissingArgumentException.class, () -> CommandParser.getCommandArguments("mark "));
+ }
+ @Test
+ public void testMispeltCommand() {
+ assertThrows(InvalidCommandException.class, () -> CommandParser.getCommandArguments("markus"));
+ }
+ @Test
+ public void validMarkInput() {
+ try {
+ String output = CommandParser.getCommandArguments("mark 1");
+ assertEquals("1", output);
+ } catch (Exception e) {
+ Assert.fail();
+ }
+ }
+ @Test
+ public void validDeleteInput() {
+ try {
+ String output = CommandParser.getCommandArguments("delete 1");
+ assertEquals("1", output);
+ } catch (Exception e) {
+ Assert.fail();
+ }
+ }
+ @Test
+ public void validUnmarkInput() {
+ try {
+ String output = CommandParser.getCommandArguments("unmark 12");
+ assertEquals("12", output);
+ } catch (Exception e) {
+ Assert.fail();
+ }
+ }
+
+ @Test
+ public void emptyInput() {
+ assertThrows(InvalidCommandException.class, () -> CommandParser.getCommandArguments(""));
+ }
+
+ @Test
+ public void noArguments() {
+ assertThrows(MissingArgumentException.class, () -> CommandParser.getCommandArguments("delete"));
+ }
+}
diff --git a/src/test/java/process/DeadlineTest.java b/src/test/java/process/DeadlineTest.java
new file mode 100644
index 0000000000..6be454204b
--- /dev/null
+++ b/src/test/java/process/DeadlineTest.java
@@ -0,0 +1,46 @@
+package process;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class DeadlineTest {
+ private Deadline process = new Deadline();
+ @Test
+ public void testFirstInput() {
+ String output = process.processInput("test");
+ assertEquals("Now indicate the deadline date.", output);
+ }
+
+ @Test
+ public void testInvalidDate() {
+ process.processInput("test");
+ String output = process.processInput("32 Aug 2023");
+ assertEquals("The given date is invalid. Please type in a valid date.", output);
+ }
+
+ @Test
+ public void testSecondInput() {
+ process.processInput("test");
+ String output = process.processInput("09 Aug 2023");
+ assertEquals("Indicate a start time ranging from 0000 - 2359. "
+ + "You may enter 'Skip' to not indicate a time", output);
+ }
+
+ @Test
+ public void testInvalidTime() {
+ process.processInput("test");
+ process.processInput("10 Aug 2023");
+ String output = process.processInput("2400");
+ assertEquals("The given time is invalid. "
+ + "Please type in a valid time in the range between 0000 - 2359", output);
+ }
+
+ @Test
+ public void testIsComplete() {
+ process.processInput("test");
+ process.processInput("09 Aug 2023");
+ process.processInput("skip");
+ assertEquals(true, process.isComplete());
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e7..2eccb76092 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,7 +1,181 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
+ ________________________________________
+ Hello! I'm chatbot.evan.Evan
+ What can I do for you?
+ ________________________________________
+ ________________________________________
+ Got it. I've added this task:
+ [T][ ] read book
+ Now you have 1 tasks in the list.
+ ________________________________________
+
+ ________________________________________
+ Got it. I've added this task:
+ [D][ ] return book (by: June 6th)
+ Now you have 2 tasks in the list.
+ ________________________________________
+
+ ________________________________________
+ Got it. I've added this task:
+ [E][ ] project meeting (from: Aug 6th 2pm to: 4pm)
+ Now you have 3 tasks in the list.
+ ________________________________________
+
+ ________________________________________
+ Got it. I've added this task:
+ [T][ ] join sports club
+ Now you have 4 tasks in the list.
+ ________________________________________
+
+ ________________________________________
+ Got it. I've added this task:
+ [T][ ] borrow book
+ Now you have 5 tasks in the list.
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! I'm sorry, but I don't know what that means :-(
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! I'm sorry, but I don't know what that means :-(
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! I'm sorry, but I don't know what that means :-(
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! I'm sorry, but I don't know what that means :-(
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! I'm sorry, but I don't know what that means :-(
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! The description of a todo cannot be empty.
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! The description of a deadline cannot be empty.
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! The description of a event cannot be empty.
+ ________________________________________
+
+ ________________________________________
+ Nice! I've marked this task as done:
+ [T][X] read book
+ ________________________________________
+
+ ________________________________________
+ Nice! I've marked this task as done:
+ [T][X] join sports club
+ ________________________________________
+
+ ________________________________________
+ Here are the tasks in your list:
+ 1.[T][X] read book
+ 2.[D][ ] return book (by: June 6th)
+ 3.[E][ ] project meeting (from: Aug 6th 2pm to: 4pm)
+ 4.[T][X] join sports club
+ 5.[T][ ] borrow book
+ ________________________________________
+
+ ________________________________________
+ Nice! I've marked this task as done:
+ [D][X] return book (by: June 6th)
+ ________________________________________
+
+ ________________________________________
+ Here are the tasks in your list:
+ 1.[T][X] read book
+ 2.[D][X] return book (by: June 6th)
+ 3.[E][ ] project meeting (from: Aug 6th 2pm to: 4pm)
+ 4.[T][X] join sports club
+ 5.[T][ ] borrow book
+ ________________________________________
+
+ ________________________________________
+ OK! I've marked this task as not done yet:
+ [D][ ] return book (by: June 6th)
+ ________________________________________
+
+ ________________________________________
+ Here are the tasks in your list:
+ 1.[T][X] read book
+ 2.[D][ ] return book (by: June 6th)
+ 3.[E][ ] project meeting (from: Aug 6th 2pm to: 4pm)
+ 4.[T][X] join sports club
+ 5.[T][ ] borrow book
+ ________________________________________
+
+ ________________________________________
+ OK! I've marked this task as not done yet:
+ [E][ ] project meeting (from: Aug 6th 2pm to: 4pm)
+ ________________________________________
+
+ ________________________________________
+ Here are the tasks in your list:
+ 1.[T][X] read book
+ 2.[D][ ] return book (by: June 6th)
+ 3.[E][ ] project meeting (from: Aug 6th 2pm to: 4pm)
+ 4.[T][X] join sports club
+ 5.[T][ ] borrow book
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! I'm sorry, but I don't know what that means :-(
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! Please type the command: '/by' followed by the specified date in any format.
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! Please type the command: '/from and /to' followed by the specified date in any format.
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! Please type the command: '/from and /to' followed by the specified date in any format.
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! Please type the command: '/from and /to' followed by the specified date in any format.
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! Please type the command: '/from' followed by the specified date in any format.
+ ________________________________________
+
+ ________________________________________
+ ☹ OOPS!!! Please type the command: '/to' followed by the specified date in any format.
+ ________________________________________
+
+ ________________________________________
+ Noted. I've removed this task:
+ [T][X] read book
+ Now you have 4 tasks in the list.
+ ________________________________________
+
+ ________________________________________
+ Here are the tasks in your list:
+ 1.[D][ ] return book (by: June 6th)
+ 2.[E][ ] project meeting (from: Aug 6th 2pm to: 4pm)
+ 3.[T][X] join sports club
+ 4.[T][ ] borrow book
+ ________________________________________
+
+ ________________________________________
+ Index number does not exist in our list
+ ________________________________________
+
+ ________________________________________
+ Strictly type 1 number only
+ ________________________________________
+
+ ________________________________________
+ 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..0e2115deee 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -0,0 +1,34 @@
+todo read book
+deadline return book /by June 6th
+event project meeting /from Aug 6th 2pm /to 4pm
+todo join sports club
+todo borrow book
+%%%%%
+aldkgfkjhwabfrk
+todooo
+de3dline
+3v3nt
+todo
+deadline
+event
+mark 1
+mark 4
+list
+mark 2
+list
+unmark 2
+list
+unmark 3
+list
+
+deadline task without by
+event task without argument
+event task /from with one argument
+event task /to with one argument
+event task /by with wrong argument /to to
+event task /from without from and to but uses /by
+delete 1
+list
+mark 10
+mark 1 2
+bye
\ No newline at end of file
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 0873744649..81a1132afe 100644
--- a/text-ui-test/runtest.bat
+++ b/text-ui-test/runtest.bat
@@ -15,7 +15,7 @@ IF ERRORLEVEL 1 (
REM no error here, errorlevel == 0
REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT
-java -classpath ..\bin Duke < input.txt > ACTUAL.TXT
+java -classpath ..\bin chatbot.evan.Evan < input.txt > ACTUAL.TXT
REM compare the output to the expected output
-FC ACTUAL.TXT EXPECTED.TXT
+FC ACTUAL.TXT EXPECTED.TXT
\ No newline at end of file
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
old mode 100644
new mode 100755