diff --git a/README.md b/README.md
index 8715d4d915..74a398fa33 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,43 @@
-# Duke project template
+# Duke
-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.
+Duke is a interactive chatbot with UI.
+> "Your mind is for having ideas, not holding them" - David Allen
-## Setting up in Intellij
+Duke is a task tracker that can track three categories of tasks:
+- Todo
+- Deadline
+- Event
-Prerequisites: JDK 11, update Intellij to the most recent version.
+## Setting up
-1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project first)
-1. Open the project into Intellij as follows:
- 1. Click `Open`.
- 1. Select the project directory, and click `OK`.
- 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:
+Setting up Duke is extremely easy. All you need to do is:
+1) Download JDK 11 and above from [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk)
+2) Download the latest JAR file release from the right side of this repository
+3) Run the command java -jar ip.jar
+
+It's that simple! :)
+
+## Commands
+There are a few commands you can run in Duke. These include:
+- [x] todo
+- [X] deadline
+- [X] event
+- [X] delete
+- [X] list
+- [X] find
+- [X] bye
+
+## Using an IDE
+You can further edit/modify Duke by navigating to the `src` folder which contains all the `java` files
+
+You can run it from Launcher.java by using __main__:
+
+public class Launcher {
+ public static void main(String[] args) {
+ Application.launch(Main.class,args);
+ }
+}
+
```
Hello from
____ _
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000000..133badba50
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,61 @@
+plugins {
+ id 'java'
+ id 'application'
+ id 'checkstyle'
+ id 'com.github.johnrengelman.shadow' version '5.1.0'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ 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'
+ 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'
+}
+
+test {
+ useJUnitPlatform()
+
+ testLogging {
+ events "passed", "skipped", "failed"
+
+ showExceptions true
+ exceptionFormat "full"
+ showCauses true
+ showStackTraces true
+ showStandardStreams = false
+ }
+}
+
+application {
+ mainClassName = "duke.Duke"
+}
+
+shadowJar {
+ archiveBaseName = "duke"
+ archiveClassifier = null
+}
+
+checkstyle {
+ toolVersion = '8.29'
+}
+
+run{
+ standardInput = System.in
+ enableAssertions = true;
+}
diff --git a/docs/README.md b/docs/README.md
index 8077118ebe..61cb7553bd 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,29 +1,135 @@
# User Guide
+Duke is a interactive chatbot with UI.
+> “Software and cathedrals are much the same; first we build them, then we pray.”
+
+Duke is a task tracker that can track three categories of tasks:
+- Todo
+- Deadline
+- Event
## Features
+1. `todo`
+2. `deadline`
+3. `event`
+4. `list`
+5. `delete`
+6. `mark`
+7. `unmark`
+8. `snooze`
+9. `bye`
-### Feature-ABC
+## Usage
-Description of the feature.
+## `todo NAME` - creates a Todo task with name NAME
-### Feature-XYZ
+Example of usage:
-Description of the feature.
+`todo work`
-## Usage
+Expected outcome:
-### `Keyword` - Describe action
+Expected outcome:
+Creates a new Todo object called work
+*Take note of the space between todo and NAME!
-Describe the action and its outcome.
-Example of usage:
+## `deadline NAME /by DATE TIME(optional)` - creates a Deadline task with name NAME at DATE and TIME
+
+Example of usage:
+```
+deadline report submission /by 2022-02-14 23:59
+deadline report submission /by 2022-02-14
+```
+
+Expected outcome:
+Creates a new Deadline object with DATE and TIME(optional)
+*Format of date: yyyy-mm-dd
+*Format of time: hh:mm
+
+
+## `event NAME /at DATE TIME(optional)` - creates a Event task with name NAME at DATE and TIME
+
+Example of usage:
+```
+event meeting /at 2022-02-12 01:00
+event meeting /at 2022-02-12
+```
+
+Expected outcome:
+Creates a new Event object with DATE and TIME(optional)
+*Format of date: yyyy-mm-dd
+*Format of time: hh:mm
+
+
+## `list` - lists out all tasks
+
+Example of usage:
+```
+list
+```
+
+Expected outcome:
+A list of all tasks in their respective formats
+
+
+## `delete INDEX` - deletes task at specific INDEX
-`keyword (optional arguments)`
+Example of usage:
+```
+delete 1
+```
+
+Expected outcome:
+Delete task from tasklist and prints the deleted task
+*Recommended to use alongside `list` command so that you get the right index
+*Important to delete a valid index - Do not go out of bounds!
+
+
+## `mark INDEX` - marks task as done
+
+Example of usage:
+```
+mark 0
+```
+
+Expected outcome:
+Marks task and prints the marked task
+*Recommended to use alongside `list` command so that you get the right index
+
+
+## `unmark INDEX` - marks task as undone
+
+Example of usage:
+```
+unmark 0
+```
+
+Expected outcome:
+Marks task and prints the marked task
+*Recommended to use alongisde `list` command so that you get the right index
+
+
+## `snooze NAME DATE /t NEWDATE NEWTIME` - changes the date & time of task
+
+Example of usage:
+```
+snooze meeting 2022-02-12 /t 2022-03-20 23:59
+```
Expected outcome:
+Sets new task date and time and prints new date and time for task
+
+_ _Keeping track of the exact time is difficult_ _, which is why all you need to input is the date of the task and not the time.
+*Getting the exact name and date of the task is important
+
-Description of the outcome.
+## `bye` - ends the program
+Example of usage:
```
-expected output
+bye
```
+
+Expected outcome:
+Duke sends a goodbye message and exits the program, closing the UI
+
diff --git a/docs/Ui.png b/docs/Ui.png
new file mode 100644
index 0000000000..342abd51f1
Binary files /dev/null and b/docs/Ui.png differ
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..b7c8c5dbf5
--- /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
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000000..2fe81a7d95
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,183 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000000..62bd9b9cce
--- /dev/null
+++ b/gradlew.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
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/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..918fec1efb
--- /dev/null
+++ b/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: duke.duke.Duke
+
diff --git a/src/main/java/duke/duke/Duke.java b/src/main/java/duke/duke/Duke.java
new file mode 100644
index 0000000000..6944549bd7
--- /dev/null
+++ b/src/main/java/duke/duke/Duke.java
@@ -0,0 +1,176 @@
+package duke.duke;
+
+import duke.ui.DukeException;
+import duke.ui.InputHandler;
+import duke.ui.DialogBox;
+
+
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.control.Button;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+import javafx.scene.layout.Region;
+import javafx.scene.image.Image;
+
+import java.io.IOException;
+import java.util.Scanner;
+
+public class Duke extends Application {
+
+ //For GUI
+ private ScrollPane scrollPane;
+ private VBox dialogBox;
+ private TextField userInput;
+ private Button sendButton;
+ private Scene scene;
+
+ //Images for Duke & user
+ private Image user = new Image(this.getClass().getResourceAsStream("/images/human.jpg"));
+ private Image duke = new Image(this.getClass().getResourceAsStream("/images/bear.jpg"));
+
+
+ public static void main(String[] args) throws IOException {
+ String dukeGreeting = "Hello! I'm Duke \nWhat can I do for you?";
+ String endMessage = "Bye. Hope to see you again soon!";
+
+ System.out.println(dukeGreeting);
+ Scanner sc = new Scanner(System.in);
+ InputHandler inputHandler = new InputHandler();
+ String response = "";
+ while (!response.equals(endMessage)) {
+ try {
+ String input = sc.nextLine();
+ System.out.println(response);
+ response = inputHandler.handleInput(input);
+ } catch (DukeException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+ }
+
+
+ /**
+ * Start the application
+ * @param stage Stage object
+ */
+ @Override
+ public void start(Stage stage) {
+ //Setting up required components
+ Label dukeLabel = new Label("Duke");
+
+ //Creating container for the chat to scroll
+ scrollPane = new ScrollPane();
+ dialogBox = new VBox();
+ scrollPane.setContent(dialogBox);
+
+ userInput = new TextField();
+ sendButton = new Button("Send");
+
+ AnchorPane mainLayout = new AnchorPane();
+ mainLayout.getChildren().addAll(scrollPane, userInput, sendButton);
+
+ Scene scene = new Scene(mainLayout);
+ stage.setScene(scene);
+ stage.show();
+
+ //Formatting window
+ stage.setTitle("Duke");
+ stage.setResizable(false);
+ stage.setMinHeight(600.0);
+ stage.setMinWidth(400.0);
+
+ mainLayout.setPrefSize(400.0, 600.0);
+ scrollPane.setPrefSize(385, 535);
+ scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
+ scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
+
+ scrollPane.setVvalue(1.0);
+ scrollPane.setFitToWidth(true);
+
+ dialogBox.setPrefHeight(Region.USE_COMPUTED_SIZE);
+
+ userInput.setPrefWidth(325.0);
+ userInput.setPrefHeight(55.0);
+
+ AnchorPane.setTopAnchor(scrollPane, 1.0);
+ AnchorPane.setBottomAnchor(sendButton, 1.0);
+ AnchorPane.setRightAnchor(sendButton, 1.0);
+ AnchorPane.setLeftAnchor(userInput, 1.0);
+ AnchorPane.setBottomAnchor(userInput, 1.0);
+
+ //Set functionality on User Input
+ sendButton.setOnMouseClicked((event) -> {
+ handleUserInput();
+ });
+
+ userInput.setOnAction((event) -> {
+ handleUserInput();
+ });
+
+ //scroll down if dialogBox's height changes
+ dialogBox.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0));
+
+ }
+
+ /**
+ * Creates the dialogboxes as well as handles user and duke's inputs
+ */
+ private void handleUserInput() {
+
+ String userText = userInput.getText();
+ String dukeText = getResponse(userInput.getText());
+ dialogBox.getChildren().addAll(
+ DialogBox.getUserDialog(userText, user),
+ DialogBox.getDukeDialog(dukeText, duke)
+ );
+ userInput.clear();
+ }
+
+ /**
+ * Returns the Response of Duke to user input
+ *
+ * @param input user's input
+ * @return Duke's reply to user's input
+ */
+ public String getResponse(String input) {
+ String output = "";
+ try {
+ InputHandler inputHandler = new InputHandler();
+ output = inputHandler.handleInput(input);
+ } catch (DukeException e) {
+ return e.getMessage();
+ } catch (IOException e) {
+ return e.getMessage();
+ }
+
+ return output;
+ }
+
+ /**
+ * Returns a Label around the text
+ *
+ * @param text text for the dialog
+ * @return Label object with text
+ */
+ private Label getDialogLabel(String text) {
+ Label textToAdd = new Label(text);
+ textToAdd.setWrapText(true);
+ return textToAdd;
+ }
+
+
+}
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/duke/duke/Launcher.java b/src/main/java/duke/duke/Launcher.java
new file mode 100644
index 0000000000..c4f9be716e
--- /dev/null
+++ b/src/main/java/duke/duke/Launcher.java
@@ -0,0 +1,12 @@
+package duke.duke;
+
+import javafx.application.Application;
+
+/**
+ * Launches the Application for Duke
+ */
+public class Launcher {
+ public static void main(String[] args) {
+ Application.launch(Main.class,args);
+ }
+}
diff --git a/src/main/java/duke/duke/Main.java b/src/main/java/duke/duke/Main.java
new file mode 100644
index 0000000000..ee17ea41d4
--- /dev/null
+++ b/src/main/java/duke/duke/Main.java
@@ -0,0 +1,38 @@
+package duke.duke;
+
+import duke.ui.MainWindow;
+
+import java.io.IOException;
+
+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();
+
+ /**
+ * Starts the application
+ *
+ * @param stage stage object passed in
+ */
+ @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);
+ fxmlLoader.getController().setDuke(duke);
+ stage.show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java
new file mode 100644
index 0000000000..7b1c48b8f7
--- /dev/null
+++ b/src/main/java/duke/storage/Storage.java
@@ -0,0 +1,229 @@
+package duke.storage;
+
+import duke.ui.DukeException;
+import duke.task.Event;
+import duke.task.Task;
+import duke.task.Todo;
+import duke.task.Deadline;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.io.FileWriter;
+
+import java.util.Scanner;
+
+public class Storage {
+
+ private TaskList taskList;
+
+ final String symbolForMarked = "[1]";
+ final String symbolForUnMarked = "[0]";
+ final String symbolForTask = "[T]";
+ final String symbolForDeadline = "[D]";
+ final String symbolForEvent = "[E]";
+ final String FILEPATH = "data/data.txt";
+ final String FILEDIRECTORY = "data";
+
+ /**
+ * Constructs a Storage object from data in data.txt file.
+ *
+ * @throws IOException Issue when writing or reading from data.txt
+ */
+ public Storage() throws IOException {
+ try {
+ File dataFile = new File(FILEPATH);
+ Scanner sc = new Scanner(dataFile);
+ TaskList newTaskList = new TaskList();
+ while (sc.hasNextLine()) {
+ String nextLine = sc.nextLine();
+ String[] taskLineSplitBySpace = nextLine.split(" ");
+ String taskType = taskLineSplitBySpace[0];
+ String[] taskLineSplitBySlash = nextLine.split(" / ");
+
+ newTaskList.add(convertToTask(taskType, taskLineSplitBySlash, taskLineSplitBySpace));
+ }
+ this.taskList = newTaskList;
+ } catch (IOException e){
+ Path filePath = Paths.get(FILEDIRECTORY);
+ boolean dataDirectoryExists = Files.exists(filePath);
+ if (!dataDirectoryExists) {
+ new File(FILEDIRECTORY).mkdir();
+ }
+ new File(FILEPATH).createNewFile();
+ this.taskList = new TaskList();
+ } catch (DukeException e) {
+ //Wrong format of tasks in data.txt
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Comprehends and converts input from data.txt into Task object
+ *
+ * @param taskType Type of Task: Deadline, Event or Todo
+ * @param taskLineSplitBySlash Format of the line in a String array format split by slashes
+ * @param taskLineSplitBySpace Format of the line in a String array format split by spaces
+ * @return Task object to be put into TaskList
+ * @throws DukeException If tasks are stored in wrong format in data.txt
+ */
+ public Task convertToTask(String taskType, String[] taskLineSplitBySlash, String[] taskLineSplitBySpace) throws DukeException {
+
+ String wrongFormatError = "Tasks stored in wrong format";
+
+ switch (taskType) {
+ case symbolForTask:
+ Todo newTodo = new Todo(taskLineSplitBySlash[1]);
+ if (taskLineSplitBySpace[1].equals(symbolForMarked)) {
+ newTodo.setMarkedTask();
+ }
+ return newTodo;
+
+ case symbolForDeadline:
+ //Checks whether there is a time component for the stored Deadline
+ Deadline newDeadline = (taskLineSplitBySlash[3].equals("null"))
+ ? new Deadline(taskLineSplitBySlash[1], taskLineSplitBySlash[2])
+ : new Deadline(taskLineSplitBySlash[1], taskLineSplitBySlash[2], taskLineSplitBySlash[3]);
+
+ if (taskLineSplitBySpace[1].equals(symbolForMarked)) {
+ newDeadline.setMarkedTask();
+ }
+ return newDeadline;
+
+ case symbolForEvent:
+ //Checks whether there is a time component for the stored Event
+ Event newEvent = (taskLineSplitBySlash[3].equals("null"))
+ ? new Event(taskLineSplitBySlash[1], taskLineSplitBySlash[2])
+ : new Event(taskLineSplitBySlash[1], taskLineSplitBySlash[2], taskLineSplitBySlash[3]);
+
+ if (taskLineSplitBySpace[1].equals(symbolForMarked)) {
+ newEvent.setMarkedTask();
+ }
+ return newEvent;
+
+ default:
+ throw new DukeException(wrongFormatError);
+ }
+ }
+
+ /**
+ * Converts task to string format for storage in the data.txt file for writing into the txt file
+ *
+ * @param task Task to be converted and written
+ * @return String format of the task eg: [D] [1] / deadline / duedate / duetime
+ */
+ public String taskToStringConverter(Task task) {
+ String output = "";
+ if (task instanceof Todo) {
+ String mark = (task.hasBeenMarked()) ? symbolForMarked : symbolForUnMarked;
+ output = symbolForTask + " " + mark + " / " + task.name + "\n";
+ } else if (task instanceof Deadline) {
+ Deadline deadline = (Deadline) task;
+ String mark = (deadline.hasBeenMarked()) ? symbolForMarked : symbolForUnMarked;
+ output = symbolForDeadline + " " + mark + " / " + deadline.name + " / " + deadline.getDueDate() + " / "
+ + deadline.getDueTime() + "\n";
+ } else if (task instanceof Event) {
+ Event event = (Event) task;
+ String mark = (event.hasBeenMarked()) ? symbolForMarked : symbolForUnMarked;
+ output = symbolForEvent + " " + mark + " / " + event.name + " / " + event.getDueDate() + " / " + event.getDueTime() + "\n";
+ }
+ return output;
+ }
+
+ /**
+ * Rewrites entire storage based on current taskList
+ *
+ * @throws IOException If there is an error with writing to data.txt
+ */
+ public void rewriteData() throws IOException {
+ FileWriter fw = new FileWriter(this.FILEPATH);
+ for (int i = 0; i < this.taskListSize(); i++) {
+ Task task = this.taskList.get(i);
+ fw.write(taskToStringConverter(task));
+ }
+ fw.close();
+ }
+
+ /**
+ * Appends a single task to the file
+ *
+ * @param task task to be added to the data.txt file
+ * @throws IOException if there is an error appending the task to data.txt
+ */
+ public void writeData(Task task) throws IOException {
+ this.taskList.add(task);
+ FileWriter fw = new FileWriter(this.FILEPATH, true);
+ System.out.println(new File(this.FILEPATH).getAbsolutePath());
+ fw.write(taskToStringConverter(task));
+ fw.close();
+ }
+
+ /**
+ * Deletes the entire file and rewrites it based on the new taskList.
+ * Amends the current stored TaskList as well.
+ *
+ * @param idx index of task to be deleted
+ * @throws IOException if there is an error rewriting data.txt
+ */
+ public void deleteData(int idx) throws IOException {
+ taskList.remove(idx);
+ FileWriter fw = new FileWriter(this.FILEPATH);
+ for (int i = 0; i < this.taskList.size(); i++) {
+ Task task = this.taskList.get(i);
+ fw.write(taskToStringConverter(task));
+ }
+ fw.close();
+ }
+
+ /**
+ * Obtains list of tasks from this.taskList and returns it
+ *
+ * @return String that lists out the tasks currently
+ */
+ public String list() {
+ String listOfTasks = "";
+ for (int i = 1; i <= this.taskList.size(); i++) {
+ Task task = this.taskList.get(i - 1);
+ if (task.hasBeenMarked()) {
+ listOfTasks += i + ". " + task + "\n";
+ } else {
+ listOfTasks += i + ". " + task + "\n";
+ }
+ }
+ if (listOfTasks.equals("")) {
+ return "You currently have no tasks. Yay! :)";
+ }
+ return listOfTasks;
+ }
+
+ /**
+ * Gets task from Storage
+ *
+ * @param idx index of task to be gotten
+ * @return task that is requested
+ */
+ public Task get(int idx) {
+ return this.taskList.get(idx);
+ }
+
+ /**
+ * Size of TaskList
+ *
+ * @return size of task list
+ */
+ public int taskListSize() {
+ return this.taskList.size();
+ }
+
+ /**
+ * Allows external access to TaskList
+ *
+ * @return TaskList
+ */
+ public TaskList accessTaskList() {
+ return this.taskList;
+ }
+
+}
diff --git a/src/main/java/duke/storage/TaskList.java b/src/main/java/duke/storage/TaskList.java
new file mode 100644
index 0000000000..fc80253fab
--- /dev/null
+++ b/src/main/java/duke/storage/TaskList.java
@@ -0,0 +1,53 @@
+package duke.storage;
+
+import duke.task.Task;
+
+import java.util.ArrayList;
+public class TaskList {
+
+ public ArrayList list;
+
+ /**
+ * Constructor for TaskList. Initialises an empty ArrayList
+ */
+ public TaskList() {
+ this.list = new ArrayList<>();
+ }
+
+ /**
+ * Adds Task to TaskList
+ *
+ * @param task Task to be added
+ */
+ public void add(Task task) {
+ this.list.add(task);
+ }
+
+ /**
+ * Removes Task from TaskList
+ *
+ * @param index index of Task to be removed
+ */
+ public void remove(int index) {
+ this.list.remove(index);
+ }
+
+ /**
+ * Gets task by index
+ *
+ * @param index index of Task to be retrieved
+ * @return Retrieved Task
+ */
+ public Task get(int index) {
+ return this.list.get(index);
+ }
+
+ /**
+ * Returns Size of TaskList
+ *
+ * @return integer size of TaskList
+ */
+ public int size() {
+ return this.list.size();
+ }
+}
diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java
new file mode 100644
index 0000000000..1b54c44e9f
--- /dev/null
+++ b/src/main/java/duke/task/Deadline.java
@@ -0,0 +1,90 @@
+package duke.task;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeParseException;
+
+/**
+ * Represents a Deadline which is a subclass of Task
+ * Includes a dueDate attribute. Overrides toString() from Task
+ */
+public class Deadline extends Task {
+ private LocalDate dueDate;
+ private LocalTime dueTime;
+
+ /**
+ * Constructor for Deadline with date
+ *
+ * @param name Name of Deadline
+ * @param date Date of deadline in yyyy-mm-dd format
+ * @throws DateTimeParseException If date is not in yyyy-mm-dd format
+ */
+ public Deadline(String name, String date) throws DateTimeParseException {
+ super(name);
+ this.dueDate = LocalDate.parse(date);
+ this.dueTime = null;
+ }
+
+ /**
+ * Constructor for Deadline with date and time
+ *
+ * @param name Name of Deadline
+ * @param date Date of deadline in yyyy-mm-dd format
+ * @param time Time of deadline in hh:mm format
+ * @throws DateTimeParseException If date is not in yyyy-mm-dd format AND/OR time is not in hh:mm format
+ */
+ public Deadline(String name, String date, String time) throws DateTimeParseException {
+ super(name);
+ this.dueDate = LocalDate.parse(date);
+ this.dueTime = LocalTime.parse(time);
+ }
+
+ /**
+ * Returns the dueDate of this Deadline object
+ *
+ * @return LocalDate object of dueDate
+ */
+ public LocalDate getDueDate() {
+ return this.dueDate;
+ }
+
+ /**
+ * Returns the dueTime of this Deadline object
+ *
+ * @return LocalTime object of dueTime
+ */
+ public LocalTime getDueTime() {
+ return this.dueTime;
+ }
+
+ /**
+ * Changes dueDate to new LocalDate
+ *
+ * @param date LocalDate object of new date
+ */
+ public void changeDueDate(LocalDate date) {
+ this.dueDate = date;
+ }
+
+ /**
+ * Changes dueTime to new LocalTime
+ *
+ * @param time LocalTIme object of new time
+ */
+ public void changeDueTime(LocalTime time) {
+ this.dueTime = time;
+ }
+
+ /**
+ * Returns String representation of Deadline
+ *
+ * @return String of Deadline task, eg [D][0] Deadline (by: 22 Feb 2022 2:22pm) vs [D][1] Deadline (by: 22 Feb 2022 2:22pm)
+ */
+ @Override
+ public String toString() {
+ String dueDateAndTime = (this.dueTime == null)
+ ? dateConverterToString(this.dueDate)
+ : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime);
+ return "[D]" + super.toString() + " (by: " + dueDateAndTime + ")";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java
new file mode 100644
index 0000000000..5e5e040535
--- /dev/null
+++ b/src/main/java/duke/task/Event.java
@@ -0,0 +1,90 @@
+package duke.task;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeParseException;
+
+/**
+ * Represents a Event which is a subclass of Task
+ * Overrides toString() from Task
+ */
+public class Event extends Task {
+ private LocalDate dueDate;
+ private LocalTime dueTime;
+
+ /**
+ * Constructor for Event. Takes in name and date but no time.
+ *
+ * @param name name of the Event
+ * @param date date in yyyy-mm-dd format only
+ * @throws DateTimeParseException If date format is not in yyyy-mm-dd format
+ */
+ public Event (String name, String date) throws DateTimeParseException {
+ super(name);
+ this.dueDate = LocalDate.parse(date);
+ this.dueTime = null;
+ }
+
+ /**
+ * Constructor for Event. Includes name, date and time
+ *
+ * @param name name of the Event
+ * @param date date in yyyy-mm-dd format only
+ * @param time time in hh:mm format
+ * @throws DateTimeParseException if date format is not in yyyy-mm-dd format AND/OR time is not in hh:mm format
+ */
+ public Event (String name, String date, String time) throws DateTimeParseException {
+ super(name);
+ this.dueDate = LocalDate.parse(date);
+ this.dueTime = LocalTime.parse(time);
+ }
+
+ /**
+ * Returns the LocalDate associated with the Event object
+ *
+ * @return LocalDate object of the dueDate
+ */
+ public LocalDate getDueDate() {
+ return this.dueDate;
+ }
+
+ /**
+ * Returns the dueTime of this Event object
+ *
+ * @return LocalTime object of dueTime
+ */
+ public LocalTime getDueTime() {
+ return this.dueTime;
+ }
+
+ /**
+ * Changes dueDate to new LocalDate
+ *
+ * @param date LocalDate object of new date
+ */
+ public void changeDueDate(LocalDate date) {
+ this.dueDate = date;
+ }
+
+ /**
+ * Changes dueTime to new LocalTime
+ *
+ * @param time LocalTIme object of new time
+ */
+ public void changeDueTime(LocalTime time) {
+ this.dueTime = time;
+ }
+
+ /**
+ * Returns String representation of Event
+ *
+ * @return String of Event task, eg: [E][0] Event (at: 22 Feb 2022 2:22pm) vs [E][1] Event (at: 22 Feb 2022 2:22pm)
+ */
+ @Override
+ public String toString() {
+ String dueDateAndTime = (this.dueTime == null)
+ ? dateConverterToString(this.dueDate)
+ : dateConverterToString(this.dueDate) + " " + timeConverterToString(this.dueTime);
+ return "[E]" + super.toString() + " (at: " + dueDateAndTime + ")";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java
new file mode 100644
index 0000000000..6c71c5f86c
--- /dev/null
+++ b/src/main/java/duke/task/Task.java
@@ -0,0 +1,83 @@
+package duke.task;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
+
+/**
+ * Represents a Task. Contains a Task constructor, two methods to mark and unmark tasks, toString() method as well as a isMark() method to check if Task is marked
+ */
+public class Task {
+ private boolean isMarked;
+ public String name;
+
+ /**
+ * Constructor for Task
+ *
+ * @param name name of the task
+ */
+ public Task (String name) {
+ this.name = name;
+ this.isMarked = false;
+ }
+
+ /**
+ * Marks current Task object as done
+ */
+ public void setMarkedTask () {
+ this.isMarked = true;
+ }
+
+ /**
+ * Unmarks current Task object
+ */
+ public void setUnmarkedTask() {
+ this.isMarked = false;
+ }
+
+ /**
+ * Returns whether current Task object has been marked
+ *
+ * @return boolean on whether task is marked
+ */
+ public boolean hasBeenMarked() {
+ return this.isMarked;
+ }
+
+ /**
+ * Converts date to String format for display
+ *
+ * @param date LocalDate for Deadline/Event tasks
+ * @return String format: converts from yyyy-mm-dd format to mmm dd yyyy format, eg: Aug 21 2022
+ */
+ public String dateConverterToString(LocalDate date) {
+ return DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(date);
+ }
+
+ /**
+ * Converts time to String format for display
+ *
+ * @param time LocalTime for Deadline/Event tasks
+ * @return String format: converts hh:mm format to hh:mm am/pm format eg: 1:30pm
+ */
+ public String timeConverterToString(LocalTime time) {
+ return DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(time);
+ }
+
+ /**
+ * Returns String representation of Task
+ *
+ * @return String version of task, with marked and name. E.g. [0] Task vs [1] Task
+ */
+ @Override
+ public String toString() {
+ if (this.isMarked) {
+ String marked = "[1] ";
+ return marked + this.name;
+ } else {
+ String unmarked = "[0] ";
+ return unmarked + this.name;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/duke/task/Todo.java b/src/main/java/duke/task/Todo.java
new file mode 100644
index 0000000000..55cc2e600b
--- /dev/null
+++ b/src/main/java/duke/task/Todo.java
@@ -0,0 +1,22 @@
+package duke.task;
+
+/**
+ * Represents a Todo which is a subclass of Task
+ * Includes a dueDate attribute. Overrides toString() from Task
+ */
+public class Todo extends Task {
+ public Todo (String name) {
+ super(name);
+ }
+
+ /**
+ * Returns String representation of Todo
+ *
+ * @return String of Todo task, eg: [T][1] Todo
+ */
+ @Override
+ public String toString() {
+ return "[T]" + super.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/duke/ui/DialogBox.java b/src/main/java/duke/ui/DialogBox.java
new file mode 100644
index 0000000000..e43bc2c8af
--- /dev/null
+++ b/src/main/java/duke/ui/DialogBox.java
@@ -0,0 +1,80 @@
+package duke.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;
+
+public class DialogBox extends HBox {
+
+ @FXML
+ private Label dialog;
+ @FXML
+ private ImageView displayPicture;
+
+ final String dialogBoxFxmlLocation = "/view/DialogBox.fxml";
+
+ /**
+ * Constructor for DialogBox. Contains text and an image for Duke/User
+ *
+ * @param text Text to be displayed
+ * @param img Image to be displayed
+ */
+ private DialogBox(String text, Image img) {
+ try {
+ FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource(dialogBoxFxmlLocation));
+ 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);
+ }
+
+ /**
+ * Returns new DialogBox for user with text contained
+ *
+ * @param text User's text
+ * @param img Image for the user
+ * @return DialogBox to be displayed
+ */
+ public static DialogBox getUserDialog(String text, Image img) {
+ return new DialogBox(text, img);
+ }
+
+ /**
+ * Returns new DialogBox for Duke with response contained
+ *
+ * @param text User's text
+ * @param img Image for Duke
+ * @return DialogBox to be displayed
+ */
+ public static DialogBox getDukeDialog(String text, Image img) {
+ var db = new DialogBox(text, img);
+ db.flip();
+ return db;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/duke/ui/DukeException.java b/src/main/java/duke/ui/DukeException.java
new file mode 100644
index 0000000000..cc9a84cc04
--- /dev/null
+++ b/src/main/java/duke/ui/DukeException.java
@@ -0,0 +1,38 @@
+package duke.ui;
+import duke.duke.Duke;
+import duke.ui.Parser;
+import duke.ui.DukeException;
+import duke.ui.InputHandler;
+import duke.storage.Storage;
+import duke.storage.TaskList;
+import duke.task.Event;
+import duke.task.Task;
+import duke.task.Todo;
+import duke.task.Deadline;
+
+/**
+ * Custom DukeException to be handled by InputHandler
+ */
+public class DukeException extends Exception{
+
+ private String errorMessage;
+
+ /**
+ * Constructs a DukeException. DukeException handles wrong inputs by user
+ *
+ * @param errorMessage Error message to be printed
+ */
+ public DukeException (String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ /**
+ * Gets error message
+ *
+ * @override Returns customised error message for DukeException when input is incorrect
+ * @return String errorMessage
+ */
+ public String getMessage() {
+ return this.errorMessage;
+ }
+}
diff --git a/src/main/java/duke/ui/InputHandler.java b/src/main/java/duke/ui/InputHandler.java
new file mode 100644
index 0000000000..6264dacc0b
--- /dev/null
+++ b/src/main/java/duke/ui/InputHandler.java
@@ -0,0 +1,184 @@
+package duke.ui;
+
+import duke.storage.Storage;
+
+import duke.task.Event;
+import duke.task.Task;
+import duke.task.Todo;
+import duke.task.Deadline;
+
+import java.io.IOException;
+
+/**
+ * Handles input from user
+ * Processes the input into 7 categories: Todo, Event, Deadline, list, mark, unmark, bye
+ */
+public class InputHandler {
+ private Storage storage;
+ private Parser parser;
+
+ //Error messages
+ final String unableToSnoozeErrorMessage = "Wrong usage of snooze! Correct usage: snooze [name]"
+ + " [time](required if deadline/event";
+ final String unableToFindErrorMessage = "Uh oh! It seems like you did not specify what to find";
+ final String unableToDeleteErrorMessage = "Wrong usage of delete! Correct usage: delete [index]";
+ final String unableToUnmarkErrorMessage = "Wrong usage of unmark! Correct usage: unmark [index]";
+ final String unableToMarkErrorMessage = "Wrong usage of mark! Correct usage: mark [index]";
+ final String unableToListErrorMessage = "Wrong usage of list! Correct usage: list";
+
+ /**
+ * Constructs an InputHandler and loads data into Storage object
+ *
+ * @throws IOException If Storage class fails to initialise
+ */
+ public InputHandler() throws IOException {
+ this.storage = new Storage();
+ this.parser = new Parser();
+ }
+
+
+ /**
+ * Handles input from Duke.java. Breaks up the String input into proper parts and parses them.
+ *
+ * @param input String input from user input.
+ * @return String output from Duke as response to user.
+ * @throws DukeException For invalid input types, or unrecognisable commands.
+ */
+ public String handleInput(String input) throws DukeException, IOException {
+ String endMessage = "Bye. Hope to see you again soon!";
+
+ String[] splitInput = input.split(" ");
+ String inputCommand = splitInput[0];
+
+ switch (inputCommand) {
+ case "todo":
+ return taskCaseHandler(CommandType.TODO, splitInput );
+ case "event":
+ return taskCaseHandler(CommandType.EVENT, splitInput);
+ case "deadline":
+ return taskCaseHandler(CommandType.DEADLINE, splitInput);
+ case "list":
+ //Confirms that input command is simply "list"
+ if (splitInput.length == 1) {
+ return parser.parse(CommandType.LIST, this.storage, splitInput);
+ } else {
+ throw new DukeException(unableToListErrorMessage);
+ }
+ case "mark":
+ //Confirms that input is in the format mark [index]
+ if (splitInput.length == 2) {
+ return this.parser.parse(CommandType.MARK, this.storage, splitInput);
+ } else {
+ throw new DukeException(unableToMarkErrorMessage);
+ }
+ case "unmark":
+ //Confirms that input is in the format mark [index]
+ if (splitInput.length == 2) {
+ return this.parser.parse(CommandType.UNMARK, this.storage, splitInput);
+ } else {
+ throw new DukeException(unableToUnmarkErrorMessage);
+ }
+ case "delete":
+ //Confirms that input is in the format mark [index]
+ if (splitInput.length == 2) {
+ return this.parser.parse(CommandType.DELETE, this.storage, splitInput);
+ } else {
+ throw new DukeException(unableToDeleteErrorMessage);
+ }
+ case "find":
+ if (splitInput.length > 1) {
+ return this.parser.parse(CommandType.FIND, this.storage, splitInput);
+ } else {
+ throw new DukeException(unableToFindErrorMessage);
+ }
+ case "snooze":
+ String splitInputBySlash[] = input.split(" /t ");
+ if (splitInputBySlash.length > 1 && splitInput.length > 5) {
+ return parser.parse(CommandType.SNOOZE, this.storage, splitInputBySlash);
+ } else {
+ throw new DukeException(unableToSnoozeErrorMessage);
+ }
+ case "bye":
+ return endMessage;
+ default:
+ throw new DukeException(":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: " +
+ "todo [task], event [task] /at [time]," +
+ " deadline [task] /by [time], mark [index], unmark [index], delete [index], bye");
+ }
+
+ }
+
+ /**
+ * Handles Todo, Event and Deadline CommandTypes. Writes the task to storage and returns Duke's reply.
+ *
+ * @param typeOfTask Type of Task, Todo, Event or Deadline.
+ * @param splitInput String array of user input, split by spaces.
+ * @return String format of Duke's reply.
+ * @throws DukeException If CommandType passed in is wrong.
+ * @throws IOException If there is an issue writing to Storage.
+ */
+ public String taskCaseHandler(CommandType typeOfTask, String[] splitInput) throws DukeException, IOException {
+ String emptyDescription = ":( OOPS!!! The description of a todo cannot be empty. Correct usage: ";
+ String todoFormat = "todo [name]";
+ String eventFormat = "event [name] /at [date] [time(optional)]";
+ String deadlineFormat = "deadline [name] /by /by [date] [time(optional)]";
+ switch(typeOfTask) {
+ case TODO:
+ //Confirms that input is in the format: todo [task]
+ if (splitInput.length > 1) {
+ Todo newTodo = (Todo) parser.parse(CommandType.TODO, splitInput);
+ this.storage.writeData(newTodo);
+ return addTaskMessage(newTodo);
+ } else {
+ throw new DukeException(emptyDescription + todoFormat);
+ }
+ case DEADLINE:
+ //Confirms that input is in the format: deadline [task] /by [date] [time(optional)]
+ if (splitInput.length > 3) {
+ Deadline newDeadline = (Deadline) parser.parse(CommandType.DEADLINE, splitInput);
+ this.storage.writeData(newDeadline);
+ return addTaskMessage(newDeadline);
+ } else {
+ throw new DukeException(emptyDescription + deadlineFormat);
+ }
+ case EVENT:
+ //Confirms that input is in the format: event [task] /at [date] [time(optional)]
+ if (splitInput.length > 3) {
+ Event newEvent = (Event) parser.parse(CommandType.EVENT, splitInput);
+ this.storage.writeData(newEvent);
+ return addTaskMessage(newEvent);
+
+ } else {
+ throw new DukeException(emptyDescription + eventFormat);
+ }
+ default:
+ throw new DukeException("Incorrect format. Should never reach this stage");
+ }
+ }
+
+ /**
+ * Prints out the task name that has been added as well as the number of tasks in the list.
+ *
+ * @param task The task that has been added
+ */
+ public String addTaskMessage(Task task) {
+ return "Got it. I've added this task:\n" + task + "\nNow you have " + this.storage.taskListSize() +
+ " tasks in the list." ;
+ }
+
+ /**
+ * Types of commands accepted by Duke
+ */
+ enum CommandType {
+ TODO,
+ EVENT,
+ DEADLINE,
+ LIST,
+ MARK,
+ UNMARK,
+ DELETE,
+ FIND,
+ SNOOZE
+ }
+}
+
diff --git a/src/main/java/duke/ui/MainWindow.java b/src/main/java/duke/ui/MainWindow.java
new file mode 100644
index 0000000000..abd97d7df3
--- /dev/null
+++ b/src/main/java/duke/ui/MainWindow.java
@@ -0,0 +1,71 @@
+package duke.ui;
+
+import duke.duke.Duke;
+
+import javafx.application.Platform;
+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;
+
+ //setting the images
+ private Image userImage = new Image(this.getClass().getResourceAsStream("/images/human.jpg"));
+ private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/bear.jpg"));
+
+ /**
+ * Initialises scrollpane
+ */
+ @FXML
+ public void initialize() {
+ String dukeGreeting = "Hello! I'm Duke ^^ \nWhat can I do for you?";
+ scrollPane.vvalueProperty().bind(dialogContainer.heightProperty());
+ dialogContainer.getChildren().addAll(DialogBox.getDukeDialog(dukeGreeting, dukeImage));
+ }
+
+ /**
+ * Sets duke
+ *
+ * @param d Duke object passed in
+ */
+ 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 endMessage = "Bye. Hope to see you again soon!";
+ String sleepErrorMessage = "Issue shutting down Duke! Force shutting down...";
+
+ String input = userInput.getText();
+ String response = duke.getResponse(input);
+ dialogContainer.getChildren().addAll(DialogBox.getUserDialog(input, userImage),
+ DialogBox.getDukeDialog(response, dukeImage));
+
+ if (response.equals(endMessage)) {
+ Platform.exit();
+ }
+
+ userInput.clear();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/duke/ui/Parser.java b/src/main/java/duke/ui/Parser.java
new file mode 100644
index 0000000000..25fdff2afb
--- /dev/null
+++ b/src/main/java/duke/ui/Parser.java
@@ -0,0 +1,290 @@
+package duke.ui;
+
+import duke.storage.Storage;
+import duke.task.Event;
+import duke.task.Task;
+import duke.task.Todo;
+import duke.task.Deadline;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeParseException;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+
+import java.io.IOException;
+
+public class Parser {
+
+ String defaultErrorMessage = ":( OOPS!!! I'm sorry, but I don't know what that means! Possible commands: todo [task]," +
+ " event [task] /at [time], deadline [task] /by [time], mark [index], unmark [index], delete [index], bye";
+ String dateAndTimeErrorMessage = ":( OOPS!!! The correct format for date and time is yyyy-mm-dd and hh:mm";
+ String snoozeErrorMessage = "Unable to find a matching task with that name and date :( Check again?";
+
+ /**
+ * Parses input from InputHandler and returns a new Task to be added to TaskList. Handles event, deadline, todo commands
+ *
+ * @param type CommandType of input, including (TODO, DEADLINE, EVENT)
+ * @param splitInput SplitInput from InputHandler is user's input, split by empty spaces for processing
+ * @return Task object of new task to be added to TaskList
+ * @throws DukeException If format is wrong
+ */
+ public Task parse(InputHandler.CommandType type, String[] splitInput) throws DukeException {
+ switch (type) {
+ case TODO:
+ //Removes the todo command word: i.e. todo task -> task
+ String[] nameArray = Arrays.copyOfRange(splitInput, 1, splitInput.length);
+ String todoName = String.join(" ", nameArray);
+ return new Todo(todoName);
+ case EVENT:
+ return parseEvent(splitInput);
+ case DEADLINE:
+ return parseDeadline(splitInput);
+ default:
+ throw new DukeException(defaultErrorMessage);
+ }
+ }
+
+ /**
+ * Parses input from InputHandler and writes/deletes/prints from storage accordingly. Handles list, mark, unmark,
+ * delete commands
+ *
+ * @param type CommandType of input, including (LIST, MARK, UNMARK, DELETE)
+ * @param storage Storage object in InputHandler to write/delete/get data from
+ * @param splitInput SplitInput from InputHandler is user's input, split by empty spaces for processing
+ * @throws DukeException Handles unrecognised commands
+ * @throws IOException Handles IO Errors
+ */
+ public String parse(InputHandler.CommandType type, Storage storage, String[] splitInput) throws DukeException, IOException {
+
+ String listCommandStringIntro = "Here are the tasks in your list:\n";
+
+ switch (type) {
+ case LIST:
+ return listCommandStringIntro + storage.list();
+ case MARK:
+ return parseMarkAndUnmark(InputHandler.CommandType.MARK, storage, splitInput);
+ case UNMARK:
+ return parseMarkAndUnmark(InputHandler.CommandType.UNMARK, storage, splitInput);
+ case DELETE:
+ return parseDelete(storage, splitInput);
+ case FIND:
+ return parseFind(storage, splitInput);
+ case SNOOZE:
+ return parseSnooze(storage, splitInput);
+ default:
+ throw new DukeException(defaultErrorMessage);
+ }
+ }
+
+ /**
+ * Parses the EVENT CommandType and user input to create the Event object using the relevant data.
+ *
+ * @param splitInput User's input, split by spaces
+ * @return Event object to be added to storage.
+ * @throws DukeException if input is in the wrong datetime format.
+ */
+ private Event parseEvent(String[] splitInput) throws DukeException {
+ //Removes the event command word and separates into date and time (optional)
+ String[] stringArrayExcludingEvent = Arrays.copyOfRange(splitInput, 1, splitInput.length);
+ String stringExcludingEvent = String.join(" ", stringArrayExcludingEvent);
+ String[] eventNameAndTimeArray = stringExcludingEvent.split("/at ");
+ String eventNameWithExtraSpace = eventNameAndTimeArray[0];
+ String eventName = eventNameWithExtraSpace.substring(0, eventNameWithExtraSpace.length() -1);
+ String eventTime = eventNameAndTimeArray[1];
+ String[] eventTimeArray = eventTime.split(" ");
+
+ try {
+ Event newEvent = (eventTimeArray.length > 1) ? new Event(eventName, eventTimeArray[0], eventTimeArray[1]) : new Event(eventName, eventTimeArray[0]);
+ return newEvent;
+ } catch (DateTimeParseException e) {
+ //Datetime unable to be parsed
+ throw new DukeException(dateAndTimeErrorMessage);
+ }
+ }
+
+ /**
+ * Parses the DEADLINE CommandType and user input to create the Deadline object using the relevant data.
+ *
+ * @param splitInput User's input, split by spaces.
+ * @return Deadline object to be added to storage.
+ * @throws DukeException If input is in the wrong datetime format/
+ */
+ private Deadline parseDeadline(String[] splitInput) throws DukeException {
+ //Removes the deadline command word and separates into date and time (optional)
+ String[] stringArrayExcludingDeadline = Arrays.copyOfRange(splitInput, 1, splitInput.length);
+ String stringExcludingDeadline = String.join(" ", stringArrayExcludingDeadline);
+ String[] deadlineNameAndTimeArray = stringExcludingDeadline.split("/by ");
+ String deadlineNameWithSpace = deadlineNameAndTimeArray[0];
+ String deadlineName = deadlineNameWithSpace.substring(0, deadlineNameWithSpace.length() - 1);
+ String deadlineTime = deadlineNameAndTimeArray[1];
+ String[] deadlineTimeArray = deadlineTime.split(" ");
+
+ try {
+ Deadline newDeadline = (deadlineTimeArray.length > 1) ? new Deadline(deadlineName, deadlineTimeArray[0], deadlineTimeArray[1]) : new Deadline(deadlineName, deadlineTimeArray[0]);
+ return newDeadline;
+ } catch (DateTimeParseException e) {
+ //Datetime unable to be parsed
+ throw new DukeException(dateAndTimeErrorMessage);
+ }
+ }
+
+ /**
+ * Parses the FIND CommandType and user input to find a list of tasks with matching descriptions
+ *
+ * @param storage Storage contains the task. This storage is iterated through to search.
+ * @param splitInput User's input split by spaces.
+ * @return String format of the task.
+ * @throws DukeException Unable to find the task.
+ */
+ private String parseFind(Storage storage, String[] splitInput) throws DukeException{
+ //Removes the find command and iterates through the TaskList to find a task name that contains the keyword
+ String cannotFindTaskMessage = "Uh oh! No task matches the description you've given :(";
+ String[] stringArrayExcludingFind = Arrays.copyOfRange(splitInput, 1, splitInput.length);
+ String nameOfKeyWord = String.join(" ", stringArrayExcludingFind);
+ ArrayList arrayOfTasks = storage.accessTaskList().list;
+ ArrayList indexOfFoundObjects = new ArrayList<>();
+
+ for (int i = 0; i < arrayOfTasks.size(); i++) {
+ Task currentTask = arrayOfTasks.get(i);
+ if (currentTask.name.contains(nameOfKeyWord)) {
+ indexOfFoundObjects.add(i);
+ }
+ }
+
+ String outputString = "";
+ if (!indexOfFoundObjects.isEmpty()) {
+ //Task found and print
+ for (int j = 0; j < indexOfFoundObjects.size(); j++) {
+ outputString += (j + 1) + "." + storage.get(indexOfFoundObjects.get(j)) + "\n";
+ }
+ return outputString;
+ } else {
+ //Unable to find
+ throw new DukeException(cannotFindTaskMessage);
+ }
+ }
+
+ /**
+ * Parses the CommandType DELETE and deletes the task from storage.
+ *
+ * @param storage Storage contains the task to be deleted.
+ * @param splitInput User's input split by spaces.
+ * @return String of Duke's reply that task has been deleted.
+ * @throws DukeException If there is a NumberFormatException with the index.
+ * @throws IOException If there is error by Storage in reading/writing to data.txt.
+ */
+ private String parseDelete(Storage storage, String[] splitInput) throws DukeException, IOException {
+ //Delete task by index
+ String wrongDeleteFormatErrorMessage = "Make sure delete is in the format delete [index]!";
+ String indexTooLarge = "Index too large!";
+
+ try {
+ int idxOfTaskToBeDeleted = Integer.parseInt(splitInput[1]) - 1;
+ System.out.println(idxOfTaskToBeDeleted);
+ assert idxOfTaskToBeDeleted < storage.taskListSize() : indexTooLarge;
+ Task taskToBeDeleted = storage.get(idxOfTaskToBeDeleted);
+ storage.deleteData(idxOfTaskToBeDeleted);
+ return "Noted. I've removed this task:\n" + taskToBeDeleted + "\nNow you have " + storage.taskListSize() +
+ " tasks in the list";
+ } catch (NumberFormatException e) {
+ //Addresses the issue of a non-integer being passed in
+ throw new DukeException(wrongDeleteFormatErrorMessage);
+ }
+ }
+
+ /**
+ * Marks or Unmarks task.
+ *
+ * @param type CommandType of task
+ * @param storage Storage to be iterated through for task
+ * @param splitInput User's input split by spaces except for snooze which is split by /t
+ * @return String of Duke's reply that task has been marked/unmarked
+ * @throws DukeException NumberFormatError due to index or invalid CommandType
+ * @throws IOException If there is an issue reading/writing from data by Storage
+ */
+ private String parseMarkAndUnmark(InputHandler.CommandType type, Storage storage, String[] splitInput) throws DukeException, IOException {
+ String markedMessage = "Nice! I've marked this task as done:\n";
+ String unmarkedMessage = "OK, I've marked this task as not done yet:\n";
+ String wrongMarkFormatErrorMessage = "Make sure mark is in the format: mark [index]!";
+ String wrongUnmarkFormatErrorMessage = "Make sure unmark is in the format: unmark [index]!";
+ String invalidCommandTypeErrorMessage = "Invalid CommandType. Should not reach here";
+
+ switch (type) {
+ case UNMARK:
+ try {
+ int taskToBeUnmarkedIndex = Integer.parseInt(splitInput[1]) - 1;
+ Task taskToBeUnmarked = storage.get(taskToBeUnmarkedIndex);
+ taskToBeUnmarked.setUnmarkedTask();
+ storage.rewriteData();
+ return unmarkedMessage + taskToBeUnmarked;
+ } catch (NumberFormatException e) {
+ //Addresses the issue of a non-integer being passed in
+ throw new DukeException(wrongUnmarkFormatErrorMessage);
+ }
+ case MARK:
+ //Marks task by index
+ try {
+ int taskToBeMarkedIndex = Integer.parseInt(splitInput[1]) - 1;
+ Task taskToBeMarked = storage.get(taskToBeMarkedIndex);
+ taskToBeMarked.setMarkedTask();
+ storage.rewriteData();
+ return markedMessage + taskToBeMarked;
+ } catch (NumberFormatException e) {
+ //Addresses the error of a non-integer being passed in
+ throw new DukeException(wrongMarkFormatErrorMessage);
+ }
+ default:
+ throw new DukeException(invalidCommandTypeErrorMessage);
+ }
+ }
+
+ private String parseSnooze(Storage storage, String[] splitInput) throws DukeException, IOException {
+ String[] nameAndPrevTimeArr = splitInput[0].split(" ");
+ String[] newDateAndTimeArr = splitInput[1].split(" ");
+
+ String previousDateString = nameAndPrevTimeArr[nameAndPrevTimeArr.length - 1];
+ LocalDate previousDate = LocalDate.parse(previousDateString);
+ String newDateString = newDateAndTimeArr[newDateAndTimeArr.length - 2];
+ LocalDate newDate = LocalDate.parse(newDateString);
+ String newTimeString = newDateAndTimeArr[newDateAndTimeArr.length - 1];
+ LocalTime newTime = LocalTime.parse(newTimeString);
+
+ String taskName = "";
+ for (int i = 1; i < nameAndPrevTimeArr.length - 1; i++) {
+ if (i == nameAndPrevTimeArr.length - 2) {
+ taskName += nameAndPrevTimeArr[i];
+ } else {
+ taskName += nameAndPrevTimeArr[i] + " ";
+ }
+ }
+
+ for (int j = 0; j < storage.taskListSize(); j++) {
+ if (storage.get(j).name.equals(taskName) && storage.get(j) instanceof Deadline) {
+ Deadline deadlineToBeChanged = (Deadline) storage.get(j);
+ if (deadlineToBeChanged.getDueDate().equals(previousDate)) {
+ deadlineToBeChanged.changeDueDate(newDate);
+ deadlineToBeChanged.changeDueTime(newTime);
+ storage.rewriteData();
+ return "Changed Deadline " + taskName + " from " + deadlineToBeChanged.dateConverterToString(previousDate)
+ + " " + deadlineToBeChanged.dateConverterToString(newDate)
+ + " " + deadlineToBeChanged.timeConverterToString(newTime);
+ }
+ }
+ if (storage.get(j).name.equals(taskName) && storage.get(j) instanceof Event) {
+ Event eventToBeChanged = (Event) storage.get(j);
+ if (eventToBeChanged.getDueDate().equals(previousDate)) {
+ eventToBeChanged.changeDueDate(newDate);
+ eventToBeChanged.changeDueTime(newTime);
+ storage.rewriteData();
+ return "Changed Event " + taskName + " from " + eventToBeChanged.dateConverterToString(previousDate)
+ + " " + eventToBeChanged.dateConverterToString(newDate)
+ + " " + eventToBeChanged.timeConverterToString(newTime);
+ }
+ }
+ }
+ throw new DukeException(snoozeErrorMessage);
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/images/bear.jpg b/src/main/resources/images/bear.jpg
new file mode 100644
index 0000000000..306f938454
Binary files /dev/null and b/src/main/resources/images/bear.jpg differ
diff --git a/src/main/resources/images/human.jpg b/src/main/resources/images/human.jpg
new file mode 100644
index 0000000000..a7c2e69ebf
Binary files /dev/null and b/src/main/resources/images/human.jpg differ
diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml
new file mode 100644
index 0000000000..ede775d4f9
--- /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..83fddd291c
--- /dev/null
+++ b/src/main/resources/view/MainWindow.fxml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/InputHandlerTest.java b/src/test/java/InputHandlerTest.java
new file mode 100644
index 0000000000..d40b991c48
--- /dev/null
+++ b/src/test/java/InputHandlerTest.java
@@ -0,0 +1,30 @@
+import duke.ui.InputHandler;
+import duke.ui.DukeException;
+
+import java.io.IOException;
+import java.io.File;
+
+import java.util.Scanner;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.DisplayName;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class InputHandlerTest {
+
+ @Test
+ @DisplayName("test tasks")
+ void AddTodoEventDeadline() throws IOException, DukeException {
+ InputHandler ui = new InputHandler();
+ ui.handleInput("todo test todo");
+ ui.handleInput("event test event /at 2022-02-09 12:00");
+ ui.handleInput("deadline test deadline /by 2022-02-11 03:59");
+ String FILEPATH = "data/data.txt";
+ File dataFile = new File(FILEPATH);
+ Scanner sc = new Scanner(dataFile);
+ assertEquals(sc.nextLine(), "[T] [X] / test todo");
+ assertEquals(sc.nextLine(), "[E] [X] / test event / 2022-02-09 / 12:00");
+ assertEquals(sc.nextLine(), "[D] [X] / test deadline / 2022-02-11 / 03:59");
+ }
+}
+
diff --git a/src/test/java/TaskListTest.java b/src/test/java/TaskListTest.java
new file mode 100644
index 0000000000..07aed79401
--- /dev/null
+++ b/src/test/java/TaskListTest.java
@@ -0,0 +1,22 @@
+import duke.storage.TaskList;
+import duke.task.Task;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.DisplayName;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TaskListTest {
+
+ @Test
+ @DisplayName("test task list")
+ void testTaskList() {
+ TaskList tl = new TaskList();
+ int taskListSize = tl.size();
+ Task newTask = new Task("test task");
+ tl.add(newTask);
+ assertEquals(tl.size(), taskListSize + 1);
+ assertEquals(tl.get(tl.size() - 1), newTask);
+ tl.remove(tl.size() - 1);
+ assertEquals(tl.size(), taskListSize);
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e7..db3a588d2f 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,7 +1,19 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
+Hello! I'm Duke
+What can I do for you?
+Got it. I've added this task:
+ [T][X] test1
+Now you have 1 tasks in the list.
+Got it. I've added this task:
+ [D][X] test2 (by:21 December 2022 1:00 am)
+Now you have 2 tasks in the list.
+Got it. I've added this task:
+ [E][X] test3 (at:5 March 2022 1:00 pm)
+Now you have 3 tasks in the list.
+Nice! I've marked this task as done:
+ [D][✓] test2 (by:21 December 2022 1:00 am)
+Here are the tasks in your list:
+1. [T][X] test1
+2. [D][✓] test2 (by:21 December 2022 1:00 am)
+3. [E][X] test3 (at:5 March 2022 1:00 pm)
+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..851a579e6e 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -0,0 +1,6 @@
+todo test1
+deadline test2 /by 2022-12-21 01:00
+event test3 /at 2022-03-05 13:00
+mark 2
+list
+bye
\ No newline at end of file
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 0873744649..35045a388a 100644
--- a/text-ui-test/runtest.bat
+++ b/text-ui-test/runtest.bat
@@ -7,7 +7,8 @@ REM delete output from previous run
if exist ACTUAL.TXT del ACTUAL.TXT
REM compile the code into the bin folder
-javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java
+javac -cp ..\src\main\java\ -Xlint:none -d ..\bin ..\src\main\java\duke\duke\*.java
+
IF ERRORLEVEL 1 (
echo ********** BUILD FAILURE **********
exit /b 1
@@ -15,7 +16,8 @@ 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\duke\duke Duke < input.txt > ACTUAL.TXT
+pause
REM compare the output to the expected output
FC ACTUAL.TXT EXPECTED.TXT
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
old mode 100644
new mode 100755