diff --git a/README.md b/README.md
index 90aa7f092..831573b25 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Duke project template
+# Quinn 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.
@@ -13,12 +13,15 @@ Prerequisites: JDK 17, update Intellij to the most recent version.
1. If there are any further prompts, accept the defaults.
1. Configure the project to use **JDK 17** (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/quinn/Quinn.java` file, right-click it, and choose `Run Quinn.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
- ____ _
- | _ \ _ _| | _____
- | | | | | | | |/ / _ \
- | |_| | |_| | < __/
- |____/ \__,_|_|\_\___|
+
+ QQQ U U III N N N N
+ Q Q U U I NN N NN N
+ Q Q U U I N N N N N N
+ Q Q U U I N NN N NN
+ QQQ UUU III N N N N
+ Q
+ QQ
```
diff --git a/docs/README.md b/docs/README.md
index 47b9f984f..8ab79f9df 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,30 +1,157 @@
-# Duke User Guide
+# Quinn Task Manager User Guide
-// Update the title above to match the actual product name
+Quinn is a personal assistant chatbot that helps in Task Management.
+It is a desktop application optimized to use from a Command Line Interface (CLI).
+Quinn helps to keep track of your to-dos, deadlines, and events.
+With Quinn, you can easily add, list, mark, and delete tasks using simple commands.
+This User Guide(UG) is also found at this link : [Quinn User Guide](https://kaboomzxc.github.io/ip/)
-// Product screenshot goes here
+## Table of Contents
+1. [Quick Start](#quick-start)
+2. [Features](#features)
+3. [Command Reference](#command-reference)
+4. [Understanding Task Types](#understanding-task-types)
+5. [Error Handling](#error-handling)
+6. [Data Storage](#data-storage)
+7. [Quinn Command Summary](#quinn-command-summary)
-// Product intro goes here
+## Quick Start
-## Adding deadlines
+1. Ensure you have Java 17 installed on your computer.
+2. Download the latest `Quinn.jar` file from [here](https://github.com/kaboomzxc/ip/releases).
+3. Copy the file to your desired folder.
+4. Open a command prompt or terminal, navigate to the folder containing Quinn.jar, and run: **java -jar Quinn.jar**
-// Describe the action and its outcome.
-// Give examples of usage
+## Features
-Example: `keyword (optional arguments)`
+### Adding Tasks
-// A description of the expected outcome goes here
+Quinn supports three types of tasks: ToDos, Deadlines, and Events.
-```
-expected output
-```
+#### Adding a ToDo
+Command: `todo `
+Example: `todo Buy groceries`
+
+#### Adding a Deadline
+Command: `deadline /by `
+Example: `deadline Submit report /by 2023-05-15 1400`
+* For proper recognition of `date` and `time`,
+ * `date` has to be entered first, followed by `time`.
+ * `date` has to be in the format `yyyy-MM-dd` (e.g. `2024-01-21`).
+ * `time` has to be in the format `HHmm` (e.g. `1400`) and can be optional.
+ * Nonetheless, any other string e.g. 13121995 will still be accepted, albeit not formatted
+ * (i.e. the same exact string "13121995" would be displayed.)
+
+#### Adding an Event
+Command: `event /from /to `
+Example: `event Team meeting /from 2023-06-20 0900 /to 2023-06-20 1100`
+* For proper recognition of `date` and `time`,
+ * `date` has to be entered first, followed by `time`.
+ * `date` has to be in the format `yyyy-MM-dd` (e.g. `2024-01-21`).
+ * `time` has to be in the format `HHmm` (e.g. `1800`) and can be optional.
+ * Nonetheless, any other string e.g. 13121995 will still be accepted, albeit not formatted
+ * (i.e. the same exact string "13121995" would be displayed.)
+
+### Examples of Tasks Display
+
+After using these commands, tasks will be displayed in the following format:
+
+1. ToDo: `[T][ ] Buy groceries`
+2. Deadline: `[D][ ] Submit report (by: Jun 30 2023 02:00 PM)`
+3. Event: `[E][ ] Team meeting (from: Jun 15 2023 09:00 AM to: Jun 15 2023 11:00 AM)`
+
+### Listing Tasks
+Command: `list`
+
+### Marking Tasks as Done
+Command: `mark `
+Example: `mark 1`
+
+### Unmarking Tasks
+Command: `unmark `
+Example: `unmark 2`
+
+### Deleting Tasks
+Command: `delete `
+Example: `delete 3`
+
+### Finding Tasks
+Command: `find `
+Example: `find meeting`
+
+### Exiting the Application
+Command: `bye`
+
+
+## Command Reference
+Here is a quick reference for all available commands:
+
+• todo [description]: Add a ToDo task
+• deadline [description] /by [date] [time]: Add a Deadline task
+• event [description] /from [start date] [start time] /to [end date] [end time]: Add an Event task
+• list: Display all tasks
+• mark [task number]: Mark a task as done
+• unmark [task number]: Mark a task as not done
+• delete [task number]: Remove a task
+• find [keyword]: Search for tasks containing the keyword
+• bye: Exit the application
+
+## Understanding Task Types
+Quinn supports three types of tasks:
+
+• ToDo: A simple task without any date/time constraint.
+
+Displayed as: [T][ ]
+
+
+• Deadline: A task with a specific due date and time.
+
+Displayed as: [D][ ] (by: Date Time)
+
+
+• Event: A task with a start and end date/time.
+
+Displayed as: [E][ ] (from: Start Date Time to: End Date Time)
+
+The square brackets next to the task type indicator (T/D/E) show the task's completion status:
+[ ]: Task is not done
+[X]: Task is done
+
+## Error Handling
+ Quinn will display error messages if it encounters issues. Here are some common errors and their meanings:
+
+"INVALID COMMAND. Please try again!": The command entered is not recognized.
+"The description of a todo cannot be empty!": You need to provide a description for the ToDo task.
+"INCOMPLETE COMMAND": Some required information is missing from the command.
+"Task not found. Please try again!": The task number provided doesn't exist in the list.
+"Please enter a valid task number to be marked as done!": The mark/unmark command requires a valid task number.
+
+If you encounter an error, read the error message carefully and adjust your command accordingly.
+
+
+## Data Storage
+ Quinn automatically saves your tasks to a file named "tasks.txt" in a "data" folder in the same directory as the Quinn.jar file. This ensures that your tasks persist between sessions.
+ Note: Do not modify the "tasks.txt" file manually, as this may cause Quinn to malfunction.
+
+
+
+
+## Quinn Command Summary
+
+| Command | Description | Example |
+|---------|-------------|---------|
+| `todo [description]` | Add a ToDo task | `todo Buy groceries` |
+| `deadline [description] /by [date] [time]` | Add a Deadline task | `deadline Submit report /by 2023-06-30 1400` |
+| `event [description] /from [start date] [start time] /to [end date] [end time]` | Add an Event task | `event Team meeting /from 2023-06-15 0900 /to 2023-06-15 1100` |
+| `list` | Display all tasks | `list` |
+| `mark [task number]` | Mark a task as done | `mark 1` |
+| `unmark [task number]` | Mark a task as not done | `unmark 2` |
+| `delete [task number]` | Remove a task | `delete 3` |
+| `find [keyword]` | Search for tasks containing the keyword | `find meeting` |
+| `bye` | Exit the application | `bye` |
-## Feature ABC
-// Feature details
-## Feature XYZ
-// Feature details
\ No newline at end of file
diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java
deleted file mode 100644
index 5d313334c..000000000
--- 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/quinn/Quinn.java b/src/main/java/quinn/Quinn.java
new file mode 100644
index 000000000..08e6a7294
--- /dev/null
+++ b/src/main/java/quinn/Quinn.java
@@ -0,0 +1,127 @@
+package quinn;
+
+import quinn.command.Command;
+import quinn.command.CommandType;
+import quinn.command.FindCommand;
+import quinn.exception.QuinnException;
+import quinn.parser.Parser;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+import quinn.storage.Storage;
+
+import java.io.IOException;
+
+/**
+ * The Quinn class represents the main controller for the Quinn task management application.
+ * It initializes the application components, manages the command execution loop, and
+ * coordinates interactions between the user interface, task list, storage, and parser.
+ *
+ * This class is responsible for:
+ * Initializing the UI, parser, storage, and task list
+ * Running the main command execution loop
+ * Handling exceptions during command execution
+ * Managing the display of filtered tasks
+ */
+public class Quinn {
+
+ /** The user interface component for handling input/output. */
+ private final Ui ui;
+
+ /** The parser for interpreting user commands. */
+ private final Parser parser;
+
+ /** The storage component for persisting task data. */
+ private Storage storage;
+
+ /** The list of tasks managed by the application. */
+ private TaskList taskList;
+
+ /**
+ * Constructs a new Quinn application instance.
+ * Initializes the UI, parser, storage, and task list components.
+ *
+ * @param folderName the name of the folder to store task data
+ * @param fileName the name of the file to store task data
+ */
+ public Quinn(String folderName, String fileName) {
+ ui = new Ui();
+ parser = new Parser();
+
+ Command command = null;
+
+ try {
+ storage = new Storage(folderName, fileName);
+ taskList = storage.loadTasksFromFile();
+ } catch (QuinnException | IOException e) {
+ ui.displayError(e.getMessage());
+ taskList = new TaskList();
+ }
+ }
+
+
+ /**
+ * Checks if there are filtered tasks and displays them if necessary.
+ * This method is called after each command execution to maintain
+ * the display of filtered tasks when appropriate.
+ *
+ * @param command the last executed command
+ */
+ public void checkAndDisplayFilteredTasks(Command command) {
+ if (taskList.hasFilter()) {
+ String filteredTasksMessage;
+
+ if (taskList.getFilterCommandType() == CommandType.FIND && !(command instanceof FindCommand)) {
+ filteredTasksMessage = ui.tasksWithKeywordMessage(taskList, taskList.getFilterInfo());
+ } else {
+ filteredTasksMessage = "";
+ }
+
+ if (!filteredTasksMessage.isEmpty()) {
+ ui.displayFilteredTasks(filteredTasksMessage);
+ }
+ }
+ }
+
+ /**
+ * The main entry point of the Quinn application.
+ * Creates a new Quinn instance and starts its execution.
+ *
+ * @param args command-line arguments (not used)
+ */
+ public static void main(String[] args) {
+ new Quinn("data", "tasks.txt").run();
+ }
+
+ /**
+ * Runs the main execution loop of the Quinn application.
+ * This method continuously reads user input, executes commands,
+ * and displays results until an exit command is received.
+ *
+ * The execution loop performs the following steps:
+ * Read a command from the user
+ * Parse the command
+ * Execute the command
+ * Check and display filtered tasks if necessary
+ * Handle any exceptions that occur during execution
+ */
+ public void run() {
+ ui.displayWelcome();
+ boolean isExit = false;
+
+ Command command = null;
+
+ while (!isExit) {
+ try {
+ String commandLine = ui.readCommand();
+ command = parser.parse(commandLine);
+ command.execute(taskList, ui, storage);
+ isExit = command.isExit();
+ } catch (QuinnException | IOException e) {
+ ui.displayError(e.getMessage());
+ } finally {
+ checkAndDisplayFilteredTasks(command);
+ ui.displayLine();
+ }
+ }
+ }
+}
diff --git a/src/main/java/quinn/command/AddCommand.java b/src/main/java/quinn/command/AddCommand.java
new file mode 100644
index 000000000..d66b77095
--- /dev/null
+++ b/src/main/java/quinn/command/AddCommand.java
@@ -0,0 +1,61 @@
+package quinn.command;
+
+import quinn.storage.Storage;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+
+import java.io.IOException;
+
+/**
+ * Represents an abstract base class for all add commands in the task management system.
+ * This class implements the Command interface and provides common functionality
+ * for adding tasks to the task list.
+ *
+ * Subclasses must implement the {@link #execute(TaskList, Ui, Storage)} method
+ * to define the specific behavior for adding different types of tasks.
+ *
+ */
+public abstract class AddCommand implements Command {
+ /** The description of the task to be added. */
+ private final String taskDescription;
+
+ /**
+ * Constructs an AddCommand with the specified task description.
+ *
+ * @param taskDescription the description of the task to be added
+ */
+ public AddCommand(String taskDescription) {
+ this.taskDescription = taskDescription;
+ }
+
+ /**
+ * Returns the description of the task to be added.
+ *
+ * @return the task description
+ */
+ public String getTaskDescription() {
+ return taskDescription;
+ }
+
+ /**
+ * Indicates whether this command should cause the program to exit.
+ *
+ * @return false, as add commands do not cause the program to exit
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ /**
+ * Executes the add command. This method must be implemented by subclasses
+ * to define the specific behavior for adding different types of tasks.
+ *
+ * @param taskList the task list to which the task will be added
+ * @param ui the user interface for displaying messages
+ * @param storage the storage for saving tasks
+ * @throws IOException if there's an error saving the updated task list
+ */
+ @Override
+ public abstract void execute(TaskList taskList, Ui ui, Storage storage) throws IOException;
+}
diff --git a/src/main/java/quinn/command/AddDeadlineCommand.java b/src/main/java/quinn/command/AddDeadlineCommand.java
new file mode 100644
index 000000000..2332530b7
--- /dev/null
+++ b/src/main/java/quinn/command/AddDeadlineCommand.java
@@ -0,0 +1,57 @@
+package quinn.command;
+
+import quinn.storage.Storage;
+import quinn.task.Task;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+
+import java.io.IOException;
+
+/**
+ * Represents a command to add a deadline task to the task list.
+ * This class extends the AddCommand abstract class and implements
+ * the specific behavior for adding a deadline task.
+ *
+ */
+public class AddDeadlineCommand extends AddCommand {
+ /** The due date and time of the deadline task. */
+ private final String taskDueDateTime;
+
+ /**
+ * Constructs an AddDeadlineCommand with the specified task information.
+ *
+ * @param taskInfo a string containing the task description and due date/time,
+ * separated by "/by"
+ */
+ public AddDeadlineCommand(String taskInfo) {
+ // Task description
+ super(taskInfo.split("/by", 2)[0].trim());
+
+ // Task due datetime
+ taskDueDateTime = taskInfo.split("/by", 2)[1].trim();
+ }
+
+ /**
+ * Executes the add deadline command. This method adds a new deadline task
+ * to the task list, updates the UI, and saves the changes to storage.
+ *
+ * @param taskList the task list to which the deadline task will be added
+ * @param ui the user interface for displaying messages
+ * @param storage the storage for saving tasks
+ * @throws IOException if there's an error saving the updated task list
+ */
+ @Override
+ public void execute(TaskList taskList, Ui ui, Storage storage) throws IOException {
+ Task newDeadlineTask = taskList.addDeadlineTask(super.getTaskDescription(), taskDueDateTime);
+
+ // Reset the List of filteredTasks when AddCommand is executed
+ // This will clear the List of filteredTasks
+ taskList.resetFilteredTasks();
+
+ String response = ui.taskAddedMessage(newDeadlineTask)
+ + System.lineSeparator() + ui.numOfTasksInListMessage(taskList);
+ ui.displayResponse(response);
+
+ storage.saveTasksToFile(taskList);
+ }
+}
diff --git a/src/main/java/quinn/command/AddEventCommand.java b/src/main/java/quinn/command/AddEventCommand.java
new file mode 100644
index 000000000..71661b04d
--- /dev/null
+++ b/src/main/java/quinn/command/AddEventCommand.java
@@ -0,0 +1,61 @@
+package quinn.command;
+
+import quinn.storage.Storage;
+import quinn.task.Task;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+
+import java.io.IOException;
+
+/**
+ * Represents a command to add an event task to the task list.
+ * This class extends the AddCommand abstract class and implements
+ * the specific behavior for adding an event task.
+ *
+ */
+public class AddEventCommand extends AddCommand {
+ /** The start date and time of the event task. */
+ private final String taskStartDateTime;
+
+ /** The end date and time of the event task. */
+ private final String taskEndDateTime;
+
+ /**
+ * Constructs an AddEventCommand with the specified task information.
+ *
+ * @param taskInfo a string containing the task description, start date/time,
+ * and end date/time, separated by "/from" and "/to"
+ */
+ public AddEventCommand(String taskInfo) {
+ // Task description
+ super(taskInfo.split("/from|/to", 3)[0].trim());
+
+ // Task start and end datetime
+ taskStartDateTime = taskInfo.split("/from|/to", 3)[1].trim();
+ taskEndDateTime = taskInfo.split("/from|/to", 3)[2].trim();
+ }
+
+ /**
+ * Executes the add event command. This method adds a new event task
+ * to the task list, updates the UI, and saves the changes to storage.
+ *
+ * @param taskList the task list to which the event task will be added
+ * @param ui the user interface for displaying messages
+ * @param storage the storage for saving tasks
+ * @throws IOException if there's an error saving the updated task list
+ */
+ @Override
+ public void execute(TaskList taskList, Ui ui, Storage storage) throws IOException {
+ Task newEventTask = taskList.addEventTask(super.getTaskDescription(), taskStartDateTime, taskEndDateTime);
+
+ // Reset the List of filteredTasks when AddCommand is executed
+ // This will clear the List of filteredTasks
+ taskList.resetFilteredTasks();
+
+ String response = ui.taskAddedMessage(newEventTask)
+ + System.lineSeparator() + ui.numOfTasksInListMessage(taskList);
+ ui.displayResponse(response);
+
+ storage.saveTasksToFile(taskList);
+ }
+}
diff --git a/src/main/java/quinn/command/AddToDoCommand.java b/src/main/java/quinn/command/AddToDoCommand.java
new file mode 100644
index 000000000..ea298c1a1
--- /dev/null
+++ b/src/main/java/quinn/command/AddToDoCommand.java
@@ -0,0 +1,49 @@
+package quinn.command;
+
+import quinn.storage.Storage;
+import quinn.task.Task;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+
+import java.io.IOException;
+
+/**
+ * Represents a command to add a to-do task to the task list.
+ * This class extends the AddCommand abstract class and implements
+ * the specific behavior for adding a to-do task.
+ *
+ */
+public class AddToDoCommand extends AddCommand {
+ /**
+ * Constructs an AddToDoCommand with the specified task description.
+ *
+ * @param taskDescription the description of the to-do task
+ */
+ public AddToDoCommand(String taskDescription) {
+ super(taskDescription);
+ }
+
+ /**
+ * Executes the add to-do command. This method adds a new to-do task
+ * to the task list, updates the UI, and saves the changes to storage.
+ *
+ * @param taskList the task list to which the to-do task will be added
+ * @param ui the user interface for displaying messages
+ * @param storage the storage for saving tasks
+ * @throws IOException if there's an error saving the updated task list
+ */
+ @Override
+ public void execute(TaskList taskList, Ui ui, Storage storage) throws IOException {
+ Task newToDoTask = taskList.addToDoTask(super.getTaskDescription());
+
+ // Reset the List of filteredTasks when AddCommand is executed
+ // This will clear the List of filteredTasks
+ taskList.resetFilteredTasks();
+
+ String response = ui.taskAddedMessage(newToDoTask)
+ + System.lineSeparator() + ui.numOfTasksInListMessage(taskList);
+ ui.displayResponse(response);
+
+ storage.saveTasksToFile(taskList);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/quinn/command/Command.java b/src/main/java/quinn/command/Command.java
new file mode 100644
index 000000000..2b10699c8
--- /dev/null
+++ b/src/main/java/quinn/command/Command.java
@@ -0,0 +1,32 @@
+package quinn.command;
+
+import quinn.exception.QuinnException;
+import quinn.storage.Storage;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+
+import java.io.IOException;
+
+/**
+ * Represents a command in the task management system.
+ * This interface defines the basic structure for all commands.
+ */
+public interface Command {
+ /**
+ * Checks if this command should cause the program to exit.
+ *
+ * @return true if this command should cause the program to exit, false otherwise
+ */
+ boolean isExit();
+
+ /**
+ * Executes the command.
+ *
+ * @param taskList the list of tasks to operate on
+ * @param ui the user interface to display results
+ * @param storage the storage to save updates
+ * @throws QuinnException if there's an error specific to the application
+ * @throws IOException if there's an I/O error during execution
+ */
+ void execute(TaskList taskList, Ui ui, Storage storage) throws QuinnException, IOException;
+}
diff --git a/src/main/java/quinn/command/CommandType.java b/src/main/java/quinn/command/CommandType.java
new file mode 100644
index 000000000..75709d512
--- /dev/null
+++ b/src/main/java/quinn/command/CommandType.java
@@ -0,0 +1,32 @@
+package quinn.command;
+
+/**
+ * Enumerates the types of commands available in the task management system.
+ */
+public enum CommandType {
+ BYE("bye"),
+ LIST("list"),
+ MARK("mark"),
+ UNMARK("unmark"),
+ DELETE("delete"),
+ TODO("todo"),
+ DEADLINE("deadline"),
+ EVENT("event"),
+ FIND("find");
+
+ private final String label;
+
+
+ /**
+ * Constructs a CommandType with the specified label.
+ *
+ * @param label the string representation of the command
+ */
+ CommandType(String label) {
+ this.label = label;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+}
diff --git a/src/main/java/quinn/command/DeleteCommand.java b/src/main/java/quinn/command/DeleteCommand.java
new file mode 100644
index 000000000..2c0552041
--- /dev/null
+++ b/src/main/java/quinn/command/DeleteCommand.java
@@ -0,0 +1,80 @@
+package quinn.command;
+
+import quinn.exception.QuinnException;
+import quinn.storage.Storage;
+import quinn.task.Task;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+
+import java.io.IOException;
+
+/**
+ * Represents a command to delete a task from the task list.
+ */
+public class DeleteCommand implements Command {
+ private final int taskNum;
+
+ /**
+ * Constructs a DeleteCommand with the specified task number.
+ *
+ * @param taskNum the number of the task to be deleted
+ */
+ public DeleteCommand(int taskNum) {
+ this.taskNum = taskNum;
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ @Override
+ public void execute(TaskList taskList, Ui ui, Storage storage) throws QuinnException, IOException {
+ Task taskDeleted = !taskList.hasFilter()
+ ? deleteTaskBasedOnAllTasks(taskList)
+ : deleteTaskBasedOnFilteredTasks(taskList);
+
+ if (taskList.getNumOfFilteredTasks() == 0) {
+ // Reset the List of filteredTasks when the List is empty
+ // This will clear the List of filteredTasks
+ taskList.resetFilteredTasks();
+ }
+
+ String response = ui.taskDeletedMessage(taskDeleted)
+ + System.lineSeparator()
+ + ui.numOfTasksInListMessage(taskList);
+ ui.displayResponse(response);
+
+ storage.saveTasksToFile(taskList);
+ }
+
+ /**
+ * Deletes a task based on all tasks in the list.
+ *
+ * @param taskList the list of all tasks
+ * @return the deleted task
+ * @throws QuinnException if the task number is invalid
+ */
+ public Task deleteTaskBasedOnAllTasks(TaskList taskList) throws QuinnException {
+ if (taskNum > 0 && taskNum <= taskList.getNumOfTasks()) {
+ return taskList.deleteTask(taskNum);
+ } else {
+ throw new QuinnException("Task not found. Please try again!");
+ }
+ }
+
+ /**
+ * Deletes a task based on filtered tasks in the list.
+ *
+ * @param taskList the list of filtered tasks
+ * @return the deleted task
+ * @throws QuinnException if the task number is invalid
+ */
+ public Task deleteTaskBasedOnFilteredTasks(TaskList taskList) throws QuinnException {
+ if (taskNum > 0 && taskNum <= taskList.getNumOfFilteredTasks()) {
+ return taskList.deleteTask(taskNum);
+ } else {
+ throw new QuinnException("Task not found. Please try again!");
+ }
+ }
+}
diff --git a/src/main/java/quinn/command/ExitCommand.java b/src/main/java/quinn/command/ExitCommand.java
new file mode 100644
index 000000000..50091444c
--- /dev/null
+++ b/src/main/java/quinn/command/ExitCommand.java
@@ -0,0 +1,31 @@
+package quinn.command;
+
+import quinn.storage.Storage;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+
+/**
+ * Represents a command to exit the task management system.
+ */
+public class ExitCommand implements Command {
+ /**
+ * Constructs an ExitCommand.
+ */
+ public ExitCommand() {
+ }
+
+ @Override
+ public boolean isExit() {
+ return true;
+ }
+
+ @Override
+ public void execute(TaskList taskList, Ui ui, Storage storage) {
+ // Reset the List of filteredTasks when ExitCommand is executed
+ // This will clear the List of filteredTasks
+ taskList.resetFilteredTasks();
+
+ ui.displayExit();
+ }
+}
+
diff --git a/src/main/java/quinn/command/FindCommand.java b/src/main/java/quinn/command/FindCommand.java
new file mode 100644
index 000000000..b3622df87
--- /dev/null
+++ b/src/main/java/quinn/command/FindCommand.java
@@ -0,0 +1,48 @@
+package quinn.command;
+
+import quinn.exception.QuinnException;
+import quinn.storage.Storage;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+
+/**
+ * Represents a command to find tasks based on a keyword.
+ */
+public class FindCommand implements Command {
+ private final String keyword;
+
+ /**
+ * Constructs a FindCommand with the specified keyword.
+ *
+ * @param keyword the keyword to search for in tasks
+ */
+ public FindCommand(String keyword) {
+ this.keyword = keyword;
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ @Override
+ public void execute(TaskList taskList, Ui ui, Storage storage) throws QuinnException {
+ if (taskList.getNumOfTasks() != 0) {
+ taskList.setFilteredTasksByKeyword(keyword);
+
+ if (taskList.getNumOfFilteredTasks() != 0) {
+ String response = ui.tasksWithKeywordMessage(taskList, keyword);
+ ui.displayResponse(response);
+ } else {
+ // Reset the List of filteredTasks when FindCommand is executed
+ // and there are no matching tasks
+ // This will clear the List of filteredTasks
+ taskList.resetFilteredTasks();
+
+ throw new QuinnException("There are no matching tasks!");
+ }
+ } else {
+ throw new QuinnException("There are no tasks in your list!");
+ }
+ }
+}
diff --git a/src/main/java/quinn/command/ListCommand.java b/src/main/java/quinn/command/ListCommand.java
new file mode 100644
index 000000000..fc6576568
--- /dev/null
+++ b/src/main/java/quinn/command/ListCommand.java
@@ -0,0 +1,36 @@
+package quinn.command;
+
+import quinn.exception.QuinnException;
+import quinn.storage.Storage;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+
+/**
+ * Represents a command to list all tasks.
+ */
+public class ListCommand implements Command {
+ /**
+ * Constructs a ListCommand.
+ */
+ public ListCommand() {
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ @Override
+ public void execute(TaskList taskList, Ui ui, Storage storage) throws QuinnException {
+ if (taskList.getNumOfTasks() != 0) {
+ // Reset the List of filteredTasks when ListCommand is executed
+ // This will clear the List of filteredTasks
+ taskList.resetFilteredTasks();
+
+ String response = ui.tasksInListMessage(taskList);
+ ui.displayResponse(response);
+ } else {
+ throw new QuinnException("There are no tasks in your list!");
+ }
+ }
+}
diff --git a/src/main/java/quinn/command/MarkCommand.java b/src/main/java/quinn/command/MarkCommand.java
new file mode 100644
index 000000000..9d92d6cec
--- /dev/null
+++ b/src/main/java/quinn/command/MarkCommand.java
@@ -0,0 +1,72 @@
+package quinn.command;
+
+import quinn.exception.QuinnException;
+import quinn.storage.Storage;
+import quinn.task.Task;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+
+import java.io.IOException;
+
+/**
+ * Represents a command to mark a task as done.
+ */
+public class MarkCommand implements Command {
+ private final int taskNum;
+
+ /**
+ * Constructs a MarkCommand with the specified task number.
+ *
+ * @param taskNum the number of the task to be marked as done
+ */
+ public MarkCommand(int taskNum) {
+ this.taskNum = taskNum;
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ @Override
+ public void execute(TaskList taskList, Ui ui, Storage storage) throws QuinnException, IOException {
+ Task taskDone = !taskList.hasFilter()
+ ? markTaskDoneBasedOnAllTasks(taskList)
+ : markTaskDoneBasedOnFilteredTasks(taskList);
+
+ String response = ui.taskDoneMessage(taskDone);
+ ui.displayResponse(response);
+
+ storage.saveTasksToFile(taskList);
+ }
+
+ /**
+ * Marks a task as done based on all tasks in the list.
+ *
+ * @param taskList the list of all tasks
+ * @return the task marked as done
+ * @throws QuinnException if the task number is invalid
+ */
+ public Task markTaskDoneBasedOnAllTasks(TaskList taskList) throws QuinnException {
+ if (taskNum > 0 && taskNum <= taskList.getNumOfTasks()) {
+ return taskList.markDone(taskNum);
+ } else {
+ throw new QuinnException("Task not found. Please try again!");
+ }
+ }
+
+ /**
+ * Marks a task as done based on filtered tasks in the list.
+ *
+ * @param taskList the list of filtered tasks
+ * @return the task marked as done
+ * @throws QuinnException if the task number is invalid
+ */
+ public Task markTaskDoneBasedOnFilteredTasks(TaskList taskList) throws QuinnException {
+ if (taskNum > 0 && taskNum <= taskList.getNumOfFilteredTasks()) {
+ return taskList.markDone(taskNum);
+ } else {
+ throw new QuinnException("Task not found. Please try again!");
+ }
+ }
+}
diff --git a/src/main/java/quinn/command/UnmarkCommand.java b/src/main/java/quinn/command/UnmarkCommand.java
new file mode 100644
index 000000000..b2a585ba2
--- /dev/null
+++ b/src/main/java/quinn/command/UnmarkCommand.java
@@ -0,0 +1,72 @@
+package quinn.command;
+
+import quinn.exception.QuinnException;
+import quinn.storage.Storage;
+import quinn.task.Task;
+import quinn.task.TaskList;
+import quinn.ui.Ui;
+
+import java.io.IOException;
+
+/**
+ * Represents a command to unmark a task (mark it as not done).
+ */
+public class UnmarkCommand implements Command {
+ private final int taskNum;
+
+ /**
+ * Constructs an UnmarkCommand with the specified task number.
+ *
+ * @param taskNum the number of the task to be unmarked
+ */
+ public UnmarkCommand(int taskNum) {
+ this.taskNum = taskNum;
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ @Override
+ public void execute(TaskList taskList, Ui ui, Storage storage) throws QuinnException, IOException {
+ Task taskNotDone = !taskList.hasFilter()
+ ? markTaskNotDoneBasedOnAllTasks(taskList)
+ : markTaskNotDoneBasedOnFilteredTasks(taskList);
+
+ String response = ui.taskNotDoneMessage(taskNotDone);
+ ui.displayResponse(response);
+
+ storage.saveTasksToFile(taskList);
+ }
+
+ /**
+ * Marks a task as not done based on all tasks in the list.
+ *
+ * @param taskList the list of all tasks
+ * @return the task marked as not done
+ * @throws QuinnException if the task number is invalid
+ */
+ public Task markTaskNotDoneBasedOnAllTasks(TaskList taskList) throws QuinnException {
+ if (taskNum > 0 && taskNum <= taskList.getNumOfTasks()) {
+ return taskList.markNotDone(taskNum);
+ } else {
+ throw new QuinnException("Task not found. Please try again!");
+ }
+ }
+
+ /**
+ * Marks a task as not done based on filtered tasks in the list.
+ *
+ * @param taskList the list of filtered tasks
+ * @return the task marked as not done
+ * @throws QuinnException if the task number is invalid
+ */
+ public Task markTaskNotDoneBasedOnFilteredTasks(TaskList taskList) throws QuinnException {
+ if (taskNum > 0 && taskNum <= taskList.getNumOfFilteredTasks()) {
+ return taskList.markNotDone(taskNum);
+ } else {
+ throw new QuinnException("Task not found. Please try again!");
+ }
+ }
+}
diff --git a/src/main/java/quinn/exception/QuinnException.java b/src/main/java/quinn/exception/QuinnException.java
new file mode 100644
index 000000000..c4760742d
--- /dev/null
+++ b/src/main/java/quinn/exception/QuinnException.java
@@ -0,0 +1,32 @@
+package quinn.exception;
+
+
+/**
+ * Represents a custom exception specific to the Quinn application.
+ * This exception is thrown when application-specific errors occur during execution.
+ *
+ * QuinnException extends the standard Java {@link Exception} class,
+ * allowing it to be caught and handled like any other exception in Java.
+ *
+ * This exception is typically used to encapsulate and report errors that are
+ * specific to the logic or operations of the Quinn application,
+ * providing more contextual information about what went wrong.
+ *
+ * Common scenarios where this exception might be thrown include:
+ * Attempting to access or modify a non-existent task
+ * Invalid user input that cannot be processed by Quinn
+ */
+public class QuinnException extends Exception {
+ /**
+ * Constructs a new QuinnException with the specified error message.
+ *
+ * This constructor creates a QuinnException with a detailed message
+ * describing the specific error condition that occurred in the Quinn application.
+ *
+ * @param errorMessage a detailed message describing the error condition
+ * that led to this exception being thrown.
+ */
+ public QuinnException(String errorMessage) {
+ super(errorMessage);
+ }
+}
diff --git a/src/main/java/quinn/parser/Parser.java b/src/main/java/quinn/parser/Parser.java
new file mode 100644
index 000000000..51938e005
--- /dev/null
+++ b/src/main/java/quinn/parser/Parser.java
@@ -0,0 +1,332 @@
+package quinn.parser;
+
+import quinn.command.AddDeadlineCommand;
+import quinn.command.AddEventCommand;
+import quinn.command.AddToDoCommand;
+import quinn.command.Command;
+import quinn.command.CommandType;
+import quinn.command.DeleteCommand;
+import quinn.command.ExitCommand;
+import quinn.command.FindCommand;
+import quinn.command.ListCommand;
+import quinn.command.MarkCommand;
+import quinn.command.UnmarkCommand;
+import quinn.exception.QuinnException;
+
+
+/**
+ * The Parser class is responsible for interpreting user input and converting it into
+ * appropriate Command objects in the Quinn task management application.
+ *
+ * This class handles the parsing of various command types, including task creation,
+ * listing, marking, unmarking, deletion, and searching. It validates user input and
+ * throws QuinnExceptions for invalid or incomplete commands.
+ *
+ */
+public class Parser {
+ /**
+ * Parses a command line input and returns the corresponding Command object.
+ *
+ * @param commandLine the full command line input from the user
+ * @return a Command object representing the parsed command
+ * @throws QuinnException if the command is invalid or incomplete
+ */
+ public Command parse(String commandLine) throws QuinnException {
+ String[] commandLineParts = commandLine.split("\\s+", 2);
+
+ CommandType commandType = validateCommand(commandLineParts[0].toLowerCase());
+
+ if (commandType == null) {
+ throw new QuinnException("INVALID COMMAND. Please try again!");
+ }
+
+ String commandInfo = getCommandInfo(commandLineParts, commandType);
+
+ return initialiseCommand(commandType, commandInfo);
+ }
+
+ /**
+ * Validates the command type against known command types.
+ *
+ * @param commandType the command type string to validate
+ * @return the corresponding CommandType enum value, or null if invalid
+ */
+ private CommandType validateCommand(String commandType) {
+ for (CommandType c : CommandType.values()) {
+ if (c.getLabel().equals(commandType)) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Extracts command information from the command line parts.
+ *
+ * @param commandLineParts the split parts of the command line
+ * @param commandType the type of the command
+ * @return the command information string
+ * @throws QuinnException if the command format is invalid
+ */
+ private String getCommandInfo(String[] commandLineParts, CommandType commandType) throws QuinnException {
+ String commandInfo;
+
+ if (commandLineParts.length == 2) {
+ commandInfo = commandLineParts[1];
+
+ // If user inputs extra information for "bye" or "list" commands,
+ // then it is not a valid command
+ if ((commandType == CommandType.BYE) || (commandType == CommandType.LIST)) {
+ throw new QuinnException("INVALID COMMAND. Please try again!");
+ }
+ } else {
+ // For "bye" and "list" commands
+ commandInfo = "";
+ }
+
+ return commandInfo;
+ }
+
+ /**
+ * Initializes and returns the appropriate Command object based on the command type and information.
+ *
+ * @param commandType the type of the command
+ * @param commandInfo additional information for the command
+ * @return the initialized Command object
+ * @throws QuinnException if there's an error in command initialization
+ */
+ private Command initialiseCommand(CommandType commandType, String commandInfo) throws QuinnException {
+ Command command;
+ int taskNum;
+ String taskDescription;
+ String taskInfo;
+ String keyword;
+
+ // Enhanced 'switch' (https://dev.to/nikhilxd/exploring-enhanced-switch-in-java-44fh)
+ command = switch (commandType) {
+ case BYE -> new ExitCommand();
+ case LIST -> new ListCommand();
+ case MARK -> {
+ taskNum = getTaskNumFromMarkCommand(commandInfo);
+ yield new MarkCommand(taskNum);
+ }
+ case UNMARK -> {
+ taskNum = getTaskNumFromUnmarkCommand(commandInfo);
+ yield new UnmarkCommand(taskNum);
+ }
+ case DELETE -> {
+ taskNum = getTaskNumFromDeleteCommand(commandInfo);
+ yield new DeleteCommand(taskNum);
+ }
+ case TODO -> {
+ taskDescription = getTaskDescriptionFromToDoCommand(commandInfo);
+ yield new AddToDoCommand(taskDescription);
+ }
+ case DEADLINE -> {
+ taskInfo = processTaskInfoFromDeadlineCommand(commandInfo);
+ yield new AddDeadlineCommand(taskInfo);
+ }
+ case EVENT -> {
+ taskInfo = processTaskInfoFromEventCommand(commandInfo);
+ yield new AddEventCommand(taskInfo);
+ }
+ case FIND -> {
+ keyword = getKeywordFromFindCommand(commandInfo);
+ yield new FindCommand(keyword);
+ }
+ };
+
+ return command;
+ }
+
+ /**
+ * Checks if the command information is present and non-empty.
+ *
+ * @param commandInfo the command information string to check
+ * @return true if command information is present, false otherwise
+ */
+ private boolean isCommandInfoPresent(String commandInfo) {
+ return !commandInfo.trim().isEmpty();
+ }
+
+ /**
+ * Extracts the task number from a mark command.
+ *
+ * @param commandInfo the command information string
+ * @return the task number to be marked
+ * @throws QuinnException if the task number is invalid or missing
+ */
+ private int getTaskNumFromMarkCommand(String commandInfo) throws QuinnException {
+ if (isCommandInfoPresent(commandInfo)) {
+ try {
+ return Integer.parseInt(commandInfo);
+ } catch (NumberFormatException e) {
+ throw new QuinnException("Please enter a valid task number to be marked as done!");
+ }
+ } else {
+ throw new QuinnException("Please enter a task number to be marked as done!");
+ }
+ }
+
+ /**
+ * Extracts the task number from an unmark command.
+ *
+ * @param commandInfo the command information string
+ * @return the task number to be unmarked
+ * @throws QuinnException if the task number is invalid or missing
+ */
+ private int getTaskNumFromUnmarkCommand(String commandInfo) throws QuinnException {
+ if (isCommandInfoPresent(commandInfo)) {
+ try {
+ return Integer.parseInt(commandInfo);
+ } catch (NumberFormatException e) {
+ throw new QuinnException("Please enter a valid task number to be marked as not done yet!");
+ }
+ } else {
+ throw new QuinnException("Please enter a task number to be marked as not done yet!");
+ }
+ }
+
+ /**
+ * Extracts the task number from a delete command.
+ *
+ * @param commandInfo the command information string
+ * @return the task number to be deleted
+ * @throws QuinnException if the task number is invalid or missing
+ */
+ private int getTaskNumFromDeleteCommand(String commandInfo) throws QuinnException {
+ if (isCommandInfoPresent(commandInfo)) {
+ try {
+ return Integer.parseInt(commandInfo);
+ } catch (NumberFormatException e) {
+ throw new QuinnException("Please enter a valid task number to be deleted!");
+ }
+ } else {
+ throw new QuinnException("Please enter a task number to be deleted!");
+ }
+ }
+
+ /**
+ * Extracts the task description from a todo command.
+ *
+ * @param commandInfo the command information string
+ * @return the task description
+ * @throws QuinnException if the description is empty
+ */
+ private String getTaskDescriptionFromToDoCommand(String commandInfo) throws QuinnException {
+ if (isCommandInfoPresent(commandInfo)) {
+ return commandInfo;
+ } else {
+ throw new QuinnException("The description of a todo cannot be empty!");
+ }
+ }
+
+ /**
+ * Processes and validates the information for a deadline command.
+ *
+ * @param commandInfo the command information string
+ * @return the processed deadline task information
+ * @throws QuinnException if the deadline information is invalid or incomplete
+ */
+ private String processTaskInfoFromDeadlineCommand(String commandInfo) throws QuinnException {
+ if (!isCommandInfoPresent(commandInfo)) {
+ throw new QuinnException("INCOMPLETE COMMAND"
+ + System.lineSeparator() + "\t"
+ + "The description and date/time of a deadline cannot be empty!"
+ + System.lineSeparator() + "\t"
+ + "[Note: Enter /by before specifying the date/time]");
+ } else {
+ String[] deadlineInfoParts = commandInfo.split("/by", 2);
+
+ if (deadlineInfoParts.length != 2) {
+ throw new QuinnException("INVALID COMMAND"
+ + System.lineSeparator() + "\t"
+ + "Please check that the description and date/time of a deadline is present!"
+ + System.lineSeparator() + "\t"
+ + "[Note: Enter /by before specifying the date/time]");
+ }
+
+ String deadlineDescription = deadlineInfoParts[0].trim();
+ String deadlineByDateTime = deadlineInfoParts[1].trim();
+
+ if (deadlineDescription.isEmpty()) {
+ throw new QuinnException("INCOMPLETE COMMAND"
+ + System.lineSeparator() + "\t"
+ + "The description of a deadline cannot be empty!");
+ }
+
+ if (deadlineByDateTime.isEmpty()) {
+ throw new QuinnException("INCOMPLETE COMMAND"
+ + System.lineSeparator() + "\t"
+ + "The date/time of a deadline cannot be empty!"
+ + System.lineSeparator() + "\t"
+ + "[Note: Enter /by before specifying the date/time]");
+ }
+
+ return commandInfo;
+ }
+ }
+
+ /**
+ * Processes and validates the information for an event command.
+ *
+ * @param commandInfo the command information string
+ * @return the processed event task information
+ * @throws QuinnException if the event information is invalid or incomplete
+ */
+ private String processTaskInfoFromEventCommand(String commandInfo) throws QuinnException {
+ if (!isCommandInfoPresent(commandInfo)) {
+ throw new QuinnException("INCOMPLETE COMMAND"
+ + System.lineSeparator() + "\t"
+ + "The description and date/time of an event cannot be empty!"
+ + System.lineSeparator() + "\t"
+ + "[Note: Specify the date/time with '/from /to']");
+ } else {
+ String[] eventInfoParts = commandInfo.split("/from|/to", 3);
+
+ if (eventInfoParts.length != 3) {
+ throw new QuinnException("INVALID COMMAND"
+ + System.lineSeparator() + "\t"
+ + "Please check that the description and date/time of an event is present!"
+ + System.lineSeparator() + "\t"
+ + "[Note: Specify the date/time with '/from /to']");
+ }
+
+ String eventDescription = eventInfoParts[0].trim();
+ String eventFromDateTime = eventInfoParts[1].trim();
+ String eventToDateTime = eventInfoParts[2].trim();
+
+ if (eventDescription.isEmpty()) {
+ throw new QuinnException("INCOMPLETE COMMAND"
+ + System.lineSeparator() + "\t"
+ + "The description of an event cannot be empty!");
+ }
+
+ if (eventFromDateTime.isEmpty() || eventToDateTime.isEmpty()) {
+ throw new QuinnException("INCOMPLETE COMMAND"
+ + System.lineSeparator() + "\t"
+ + "The date/time of an event cannot be empty!"
+ + System.lineSeparator() + "\t"
+ + "[Note: Specify the date/time with '/from /to']");
+ }
+
+ return commandInfo;
+ }
+ }
+
+ /**
+ * Extracts the keyword from a find command.
+ *
+ * @param commandInfo the command information string
+ * @return the search keyword
+ * @throws QuinnException if the keyword is missing
+ */
+ private String getKeywordFromFindCommand(String commandInfo) throws QuinnException {
+ if (isCommandInfoPresent(commandInfo)) {
+ return commandInfo;
+ } else {
+ throw new QuinnException("Please enter the keyword to search for matching tasks!");
+ }
+ }
+}
diff --git a/src/main/java/quinn/storage/Storage.java b/src/main/java/quinn/storage/Storage.java
new file mode 100644
index 000000000..5131f1676
--- /dev/null
+++ b/src/main/java/quinn/storage/Storage.java
@@ -0,0 +1,139 @@
+package quinn.storage;
+
+import quinn.exception.QuinnException;
+import quinn.task.Deadline;
+import quinn.task.Event;
+import quinn.task.Task;
+import quinn.task.TaskList;
+import quinn.task.TaskType;
+import quinn.task.ToDo;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * The Storage class manages the persistence of task data to and from a file.
+ * It handles the initialization of the storage directory and file, as well as
+ * reading from and writing to the storage file.
+ */
+public class Storage {
+ /** The File object representing the data storage file. */
+ private final File dataFile;
+
+ /**
+ * Constructs a new Storage object with the specified folder and file name.
+ *
+ * @param folderName the name of the folder to store the data file
+ * @param fileName the name of the data file
+ * @throws QuinnException if unable to initialize the directory
+ * @throws IOException if unable to initialize the file
+ */
+ public Storage(String folderName, String fileName) throws QuinnException, IOException {
+ File directory = initialiseDirectory(folderName);
+ dataFile = initialiseFile(directory, fileName);
+ }
+
+ /**
+ * Initializes the directory for data storage.
+ *
+ * @param folderName the name of the folder to be initialized
+ * @return the File object representing the initialized directory
+ * @throws QuinnException if unable to initialize the directory
+ */
+ private File initialiseDirectory(String folderName) throws QuinnException {
+ File directory = new File(folderName);
+ boolean hasDirectory = directory.exists();
+
+ if (!hasDirectory) {
+ hasDirectory = directory.mkdir();
+ }
+
+ if (hasDirectory) {
+ return directory;
+ } else {
+ throw new QuinnException("\t" + "Unable to initialise directory");
+ }
+ }
+
+ private File initialiseFile(File directory, String fileName) throws IOException {
+ File file = new File(directory + "/" + fileName);
+ boolean hasFile = file.exists();
+
+ if (!hasFile) {
+ hasFile = file.createNewFile();
+ }
+
+ if (hasFile) {
+ return file;
+ } else {
+ throw new IOException("\t" + "Unable to initialise file");
+ }
+ }
+
+ /**
+ * Loads tasks from the data file and returns them as a TaskList.
+ *
+ * @return a TaskList containing all tasks loaded from the file
+ * @throws QuinnException if an invalid task type is encountered
+ * @throws IOException if an I/O error occurs while reading the file
+ */
+ public TaskList loadTasksFromFile() throws QuinnException, IOException {
+ TaskList taskList = new TaskList();
+
+ FileReader fileReader = new FileReader(dataFile);
+ BufferedReader bufferedReader = new BufferedReader(fileReader);
+ String line;
+
+ while ((line = bufferedReader.readLine()) != null) {
+ String[] taskDetails = line.trim().split("\\|");
+ String type = taskDetails[0].trim();
+ boolean isDone = (Integer.parseInt(taskDetails[1].trim()) == 1);
+ String description = taskDetails[2].trim();
+
+ if (type.equals(TaskType.TODO.getAbbreviation())) {
+ Task todoTask = new ToDo(description, isDone);
+ taskList.addTask(todoTask);
+ } else if (type.equals(TaskType.DEADLINE.getAbbreviation())) {
+ String dueDateTime = taskDetails[3].trim();
+ Task deadlineTask = new Deadline(description, dueDateTime, isDone);
+ taskList.addTask(deadlineTask);
+ } else if (type.equals(TaskType.EVENT.getAbbreviation())) {
+ String startDateTime = taskDetails[3].trim();
+ String endDateTime = taskDetails[4].trim();
+ Task eventTask = new Event(description, startDateTime, endDateTime, isDone);
+ taskList.addTask(eventTask);
+ } else {
+ // Error detection for any invalid type of tasks found in the
+ // storage file. This should not happen since the user is only
+ // allowed to create todo [T], deadline [D] and event [E] tasks.
+ throw new QuinnException("INVALID TYPE OF TASK FOUND");
+ }
+ }
+
+ bufferedReader.close();
+
+ return taskList;
+ }
+
+ /**
+ * Saves the given TaskList to the data file.
+ *
+ * @param taskList the TaskList to be saved to the file
+ * @throws IOException if an I/O error occurs while writing to the file
+ */
+ public void saveTasksToFile(TaskList taskList) throws IOException {
+ FileWriter fileWriter = new FileWriter(dataFile,false);
+ BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
+
+ for (int i = 0; i < taskList.getNumOfTasks(); i++) {
+ Task task = taskList.getTask(i);
+ bufferedWriter.write(task.saveFormat() + System.lineSeparator());
+ }
+
+ bufferedWriter.close();
+ }
+}
diff --git a/src/main/java/quinn/task/Deadline.java b/src/main/java/quinn/task/Deadline.java
new file mode 100644
index 000000000..0435a2800
--- /dev/null
+++ b/src/main/java/quinn/task/Deadline.java
@@ -0,0 +1,93 @@
+package quinn.task;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+/**
+ * Represents a deadline task in the Quinn task management application.
+ * A deadline task is a task with a specific due date and time.
+ */
+public class Deadline extends Task {
+ /** The due date and time of the deadline task as a string. */
+ private final String dueDateTime;
+
+ /** The parsed LocalDateTime object of the due date and time. */
+ private final LocalDateTime parsedDueDateTime;
+
+ /**
+ * Constructs a new Deadline task with the given description and due date/time.
+ *
+ * @param description the description of the task
+ * @param dueDateTime the due date and time of the task in the format "yyyy-MM-dd HHmm"
+ */
+ public Deadline(String description, String dueDateTime) {
+ this(description, dueDateTime, false);
+ }
+
+ /**
+ * Constructs a new Deadline task with the given description, due date/time, and completion status.
+ *
+ * @param description the description of the task
+ * @param dueDateTime the due date and time of the task in the format "yyyy-MM-dd HHmm"
+ * @param isDone the completion status of the task
+ */
+ public Deadline(String description, String dueDateTime, boolean isDone) {
+ super(TaskType.DEADLINE, description, isDone);
+ this.dueDateTime = dueDateTime;
+ this.parsedDueDateTime = parseDateTime(dueDateTime); // parse dueDateTime into a LocalDateTime object
+ }
+
+ /**
+ * Parses the input date/time string into a LocalDateTime object.
+ *
+ * @param inputDateTime the input date/time string in the format "yyyy-MM-dd HHmm"
+ * @return the parsed LocalDateTime object, or null if parsing fails
+ */
+ private LocalDateTime parseDateTime(String inputDateTime) {
+ try {
+ DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm");
+ return LocalDateTime.parse(inputDateTime, inputFormatter);
+ } catch (DateTimeParseException e) {
+ return null; // if inputDateTime is not in "yyyy-MM-dd HHmm" format
+ }
+ }
+
+ /**
+ * Formats a LocalDateTime object into a readable string.
+ *
+ * @param parsedDateTime the LocalDateTime object to format
+ * @return a formatted string representation of the date/time
+ */
+ private String formatDateTime(LocalDateTime parsedDateTime) {
+ DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("MMM d yyyy hh:mm a");
+ return parsedDateTime.format(outputFormatter);
+ }
+
+ /**
+ * Returns a string representation of the Deadline task.
+ *
+ * @return a string representation of the task, including its description and due date/time
+ */
+ @Override
+ public String toString() {
+ if (parsedDueDateTime != null) {
+ return super.toString() + " (by: " + formatDateTime(parsedDueDateTime) + ")";
+ } else {
+ return super.toString() + " (by: " + dueDateTime + ")";
+ }
+ }
+
+ /**
+ * Returns a string representation of the Deadline task suitable for saving to a file.
+ *
+ * @return a string representation of the task for file storage
+ */
+ public String saveFormat() {
+ if (parsedDueDateTime != null) {
+ return super.saveFormat() + " | " + formatDateTime(parsedDueDateTime);
+ } else {
+ return super.saveFormat() + " | " + dueDateTime;
+ }
+ }
+}
diff --git a/src/main/java/quinn/task/Event.java b/src/main/java/quinn/task/Event.java
new file mode 100644
index 000000000..b32ba90c6
--- /dev/null
+++ b/src/main/java/quinn/task/Event.java
@@ -0,0 +1,109 @@
+package quinn.task;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+/**
+ * Represents an event task in the Quinn task management application.
+ * An event task is a task with a specific start and end date/time.
+ */
+public class Event extends Task {
+ /** The start date and time of the event task as a string. */
+ private final String startDateTime;
+
+ /** The end date and time of the event task as a string. */
+ private final String endDateTime;
+
+ /** The parsed LocalDateTime object of the start date and time. */
+ private final LocalDateTime parsedStartDateTime;
+
+ /** The parsed LocalDateTime object of the end date and time. */
+ private final LocalDateTime parsedEndDateTime;
+
+
+ /**
+ * Constructs a new Event task with the given description, start date/time, and end date/time.
+ *
+ * @param description the description of the task
+ * @param startDateTime the start date and time of the event in the format "yyyy-MM-dd HHmm"
+ * @param endDateTime the end date and time of the event in the format "yyyy-MM-dd HHmm"
+ */
+ public Event(String description, String startDateTime, String endDateTime) {
+ this(description, startDateTime, endDateTime, false);
+ }
+
+
+ /**
+ * Constructs a new Event task with the given description, start date/time, end date/time, and completion status.
+ *
+ * @param description the description of the task
+ * @param startDateTime the start date and time of the event in the format "yyyy-MM-dd HHmm"
+ * @param endDateTime the end date and time of the event in the format "yyyy-MM-dd HHmm"
+ * @param isDone the completion status of the task
+ */
+ public Event(String description, String startDateTime, String endDateTime, boolean isDone) {
+ super(TaskType.EVENT, description, isDone);
+ this.startDateTime = startDateTime;
+ this.endDateTime = endDateTime;
+ this.parsedStartDateTime = parseDateTime(startDateTime); // parse startDateTime into a LocalDateTime object
+ this.parsedEndDateTime = parseDateTime(endDateTime); // parse endDateTime into a LocalDateTime object
+ }
+
+ /**
+ * Parses the input date/time string into a LocalDateTime object.
+ *
+ * @param inputDateTime the input date/time string in the format "yyyy-MM-dd HHmm"
+ * @return the parsed LocalDateTime object, or null if parsing fails
+ */
+ private LocalDateTime parseDateTime(String inputDateTime) {
+ try {
+ DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm");
+ return LocalDateTime.parse(inputDateTime, inputFormatter);
+ } catch (DateTimeParseException e) {
+ return null; // if inputDateTime is not in "yyyy-MM-dd HHmm" format
+ }
+ }
+
+ /**
+ * Formats a LocalDateTime object into a readable string.
+ *
+ * @param parsedDateTime the LocalDateTime object to format
+ * @return a formatted string representation of the date/time
+ */
+ private String formatDateTime(LocalDateTime parsedDateTime) {
+ DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("MMM d yyyy hh:mm a");
+ return parsedDateTime.format(outputFormatter);
+ }
+
+ /**
+ * Returns a string representation of the Event task.
+ *
+ * @return a string representation of the task, including its description, start date/time, and end date/time
+ */
+ @Override
+ public String toString() {
+ if (parsedStartDateTime != null && parsedEndDateTime != null) {
+ return super.toString()
+ + " (from: " + formatDateTime(parsedStartDateTime)
+ + " to: " + formatDateTime(parsedEndDateTime) + ")";
+ } else {
+ return super.toString() + " (from: " + startDateTime + " to: " + endDateTime + ")";
+ }
+ }
+
+ /**
+ * Returns a string representation of the Event task suitable for saving to a file.
+ *
+ * @return a string representation of the task for file storage
+ */
+ public String saveFormat() {
+ if (parsedStartDateTime != null && parsedEndDateTime != null) {
+ return super.saveFormat()
+ + " | " + formatDateTime(parsedStartDateTime)
+ + " | " + formatDateTime(parsedEndDateTime);
+ } else {
+ return super.saveFormat() + " | " + startDateTime + " | " + endDateTime;
+ }
+ }
+}
diff --git a/src/main/java/quinn/task/Task.java b/src/main/java/quinn/task/Task.java
new file mode 100644
index 000000000..8f3fef314
--- /dev/null
+++ b/src/main/java/quinn/task/Task.java
@@ -0,0 +1,90 @@
+package quinn.task;
+
+/**
+ * Represents an abstract base class for all types of tasks in the Quinn task management application.
+ * This class defines common properties and behaviors for all tasks.
+ *
+ */
+public abstract class Task {
+ private final TaskType type;
+ private final String description;
+
+ /** The completion status of the task. */
+ private boolean isDone;
+
+ /**
+ * Constructs a new Task with the specified type and description.
+ *
+ * @param type the type of the task
+ * @param description the description of the task
+ */
+ public Task(TaskType type, String description) {
+ // By default, the task is not done
+ this(type, description, false);
+ }
+
+ /**
+ * Constructs a new Task with the specified type, description, and completion status.
+ *
+ * @param type the type of the task
+ * @param description the description of the task
+ * @param isDone the initial completion status of the task
+ */
+ public Task(TaskType type, String description, boolean isDone) {
+ this.type = type;
+ this.description = description;
+ this.isDone = isDone;
+ }
+
+ /**
+ * Returns the status icon of the task.
+ *
+ * @return "[X]" if the task is done, "[ ]" otherwise
+ */
+ public String getStatusIcon() {
+ if (isDone) {
+ return "[X]"; // mark done task with X
+ } else {
+ return "[ ]";
+ }
+ }
+
+ /** Marks the task as done. */
+ public void setDone() {
+ this.isDone = true;
+ }
+
+ /**Marks the task as not done.*/
+ public void setNotDone() {
+ this.isDone = false;
+ }
+
+ /**
+ * Checks if the task description contains the given keyword (case-insensitive).
+ *
+ * @param keyword the keyword to search for in the task description
+ * @return true if the task description contains the keyword, false otherwise
+ */
+ public boolean hasKeyword(String keyword) {
+ return description.toLowerCase().contains(keyword.toLowerCase());
+ }
+
+ /**
+ * Returns a string representation of the task.
+ *
+ * @return a string representation of the task, including its type, status, and description
+ */
+ @Override
+ public String toString() {
+ return "[" + type.getAbbreviation() + "] " + getStatusIcon() + " " + description;
+ }
+
+ /**
+ * Returns a string representation of the task suitable for saving to a file.
+ *
+ * @return a string representation of the task for file storage
+ */
+ public String saveFormat() {
+ return type.getAbbreviation() + " | " + (isDone ? "1" : "0") + " | " + description;
+ }
+}
diff --git a/src/main/java/quinn/task/TaskList.java b/src/main/java/quinn/task/TaskList.java
new file mode 100644
index 000000000..dd7cc7774
--- /dev/null
+++ b/src/main/java/quinn/task/TaskList.java
@@ -0,0 +1,280 @@
+package quinn.task;
+
+import quinn.command.CommandType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a list of tasks in the Quinn task management application.
+ * This class manages the collection of tasks, including filtering and various operations on tasks.
+ * The TaskList maintains both a main list of all tasks and a filtered list of tasks.
+ * The filter is applied when searching for tasks, and is reset in the following scenarios:
+ * When a new task is added
+ * When the list command is executed
+ * When the exit command is executed
+ * When a delete command is executed and the filtered list becomes empty
+ */
+public class TaskList {
+ private final List tasks;
+ private List filteredTasks;
+ private CommandType filterCommandType;
+ private String filterInfo;
+
+ /**
+ * Constructs an empty TaskList.
+ * Initializes the main task list and the filtered task list.
+ */
+ public TaskList() {
+ this.tasks = new ArrayList();
+ filteredTasks = new ArrayList<>();
+ filterCommandType = null;
+ filterInfo = null;
+ }
+
+ /**
+ * Returns the total number of tasks in the list.
+ *
+ * @return the number of tasks in the main task list.
+ */
+ public int getNumOfTasks() {
+ return tasks.size();
+ }
+
+ /**
+ * Returns the number of tasks in the filtered list.
+ *
+ * @return the number of tasks in the filtered task list
+ */
+ public int getNumOfFilteredTasks() {
+ return filteredTasks.size();
+ }
+
+ /**
+ * Retrieves a task from the main task list by its index.
+ *
+ * @param index the index of the task to retrieve (0-based)
+ * @return the Task at the specified index
+ * @throws IndexOutOfBoundsException if the index is out of range
+ */
+ public Task getTask(int index) {
+ return tasks.get(index);
+ }
+
+ /**
+ * Retrieves a task from the filtered task list by its index.
+ *
+ * @param index the index of the task to retrieve from the filtered list (0-based)
+ * @return the Task at the specified index in the filtered list
+ * @throws IndexOutOfBoundsException if the index is out of range
+ */
+ public Task getFilteredTask(int index) {
+ return filteredTasks.get(index);
+ }
+
+ /**
+ * Checks if there is an active filter applied to the task list.
+ *
+ * @return true if there are tasks in the filtered list, false otherwise
+ */
+ public boolean hasFilter() {
+ return !filteredTasks.isEmpty();
+ }
+
+ /**
+ * Retrieves the type of command that was used to apply the current filter.
+ *
+ * @return the CommandType of the current filter, or null if no filter is applied
+ */
+ public CommandType getFilterCommandType() {
+ return filterCommandType;
+ }
+
+ /**
+ * Retrieves the information used for the current filter.
+ *
+ * @return the filter information (e.g., keyword for search), or null if no filter is applied
+ */
+ public String getFilterInfo() {
+ return filterInfo;
+ }
+
+ /**
+ * Resets the filtered task list and clears filter information.
+ * This method is typically called when a new operation that affects all tasks is performed.
+ * This method is called in the following scenarios:
+ * When a new task is added
+ * When the list command is executed
+ * When the exit command is executed
+ * When a delete command is executed and the filtered list becomes empty
+ */
+ public void resetFilteredTasks() {
+ filteredTasks = new ArrayList<>();
+ filterCommandType = null;
+ filterInfo = null;
+ }
+
+ /**
+ * Filters the tasks based on a given keyword and updates the filtered task list.
+ *
+ * @param keyword the keyword to search for in task descriptions
+ */
+ public void setFilteredTasksByKeyword(String keyword) {
+ filteredTasks = new ArrayList<>();
+
+ // Iterate through each task and filter those that contain the keyword
+ for (Task task : tasks) {
+ if (task.hasKeyword(keyword)) {
+ filteredTasks.add(task);
+ }
+ }
+
+ filterCommandType = CommandType.FIND;
+ filterInfo = keyword;
+ }
+
+ /**
+ * Adds a new ToDo task to the task list.
+ * also resets any active filter.
+ * @param description the description of the ToDo task
+ * @return the newly created ToDo task
+ */
+ public Task addToDoTask(String description) {
+ Task toDoTask = new ToDo(description);
+ addTask(toDoTask);
+ return toDoTask;
+ }
+
+ /**
+ * Adds a new Deadline task to the task list.
+ * also resets any filter
+ * @param description the description of the Deadline task
+ * @param dueDateTime the due date and time of the Deadline task. This should be in the format "yyyy-MM-dd HHmm".
+ * @return the newly created Deadline task
+ */
+ public Task addDeadlineTask(String description, String dueDateTime) {
+ Task deadlineTask = new Deadline(description, dueDateTime);
+ addTask(deadlineTask);
+ return deadlineTask;
+ }
+
+ /**
+ * Adds a new Event task to the task list.
+ * also resets any filter
+ * @param description the description of the Event task
+ * @param startDateTime the start date and time of the Event task. This should be in the format "yyyy-MM-dd HHmm".
+ * @param endDateTime the end date and time of the Event task. This should be in the format "yyyy-MM-dd HHmm".
+ * @return the newly created Event task
+ */
+ public Task addEventTask(String description, String startDateTime, String endDateTime) {
+ Task eventTask = new Event(description, startDateTime,endDateTime);
+ addTask(eventTask);
+ return eventTask;
+ }
+
+ /**
+ * Adds a task to the main task list.
+ *
+ * @param task the task to be added
+ */
+ public void addTask(Task task) {
+ tasks.add(task);
+ }
+
+
+ /**
+ * Marks a task as done.
+ *
+ * @param taskNum the number of the task to be marked as done (1-based index)
+ * @return the task that was marked as done
+ * @throws IndexOutOfBoundsException if the task number is out of range
+ */
+ public Task markDone(int taskNum) {
+ Task task = !hasFilter() ? getTask(taskNum - 1) : getFilteredTask(taskNum - 1);
+ task.setDone();
+ return task;
+ }
+
+ /**
+ * Marks a task as not done.
+ *
+ * @param taskNum the number of the task to be marked as not done (1-based index)
+ * @return the task that was marked as not done
+ * @throws IndexOutOfBoundsException if the task number is out of range
+ */
+ public Task markNotDone(int taskNum) {
+ Task task = !hasFilter() ? getTask(taskNum - 1) : getFilteredTask(taskNum - 1);
+ task.setNotDone();
+ return task;
+ }
+
+ /**
+ * Deletes a task from the task list.
+ *
+ * @param taskNum the number of the task to be deleted (1-based index)
+ * @return the task that was deleted
+ * @throws IndexOutOfBoundsException if the task number is out of range
+ */
+ public Task deleteTask(int taskNum) {
+ Task task = !hasFilter() ? getTask(taskNum - 1) : getFilteredTask(taskNum - 1);
+ tasks.remove(task);
+
+ if (hasFilter()) {
+ filteredTasks.remove(task);
+ }
+
+ return task;
+ }
+
+ /**
+ * Generates a string representation of all tasks in the main task list.
+ *
+ * @return a formatted string containing all tasks, each on a new line
+ */
+ public String listOfTasksString() {
+ StringBuilder listBuilder = new StringBuilder();
+
+ for (int i = 0; i < getNumOfTasks(); i++) {
+ String listItem = (i + 1) + "." + "\t" + getTask(i);
+
+ if (i != 0) {
+ listBuilder.append(System.lineSeparator());
+ }
+
+ listBuilder.append("\t").append(listItem);
+ }
+
+ return listBuilder.toString();
+ }
+
+ /**
+ * Generates a string representation of all tasks in the filtered task list.
+ *
+ * @return a formatted string containing all filtered tasks, each on a new line
+ */
+ public String listOfFilteredTasksString() {
+ StringBuilder listBuilder = new StringBuilder();
+
+ for (int i = 0; i < getNumOfFilteredTasks(); i++) {
+ String listItem = (i + 1) + "." + "\t" + getFilteredTask(i);
+
+ if (i != 0) {
+ listBuilder.append(System.lineSeparator());
+ }
+
+ listBuilder.append("\t").append(listItem);
+ }
+
+ return listBuilder.toString();
+ }
+
+ /**
+ * Returns a string representation of the task list.
+ *
+ * @return a string representing all tasks or filtered tasks if a filter is applied
+ */
+ @Override
+ public String toString() {
+ return !hasFilter() ? listOfTasksString() : listOfFilteredTasksString();
+ }
+}
diff --git a/src/main/java/quinn/task/TaskType.java b/src/main/java/quinn/task/TaskType.java
new file mode 100644
index 000000000..1f290345a
--- /dev/null
+++ b/src/main/java/quinn/task/TaskType.java
@@ -0,0 +1,45 @@
+package quinn.task;
+
+/**
+ * Enumerates the types of tasks available in the Quinn task management application.
+ * This enum defines the different categories of tasks that can be created and managed within the system.
+ *
+ */
+public enum TaskType {
+ /**
+ * Represents a ToDo task.
+ * A ToDo task is a simple task without any specific date or time constraints.
+ */
+ TODO("T"),
+
+ /**
+ * A Deadline task is associated with a specific due date and time.
+ */
+ DEADLINE("D"),
+
+ /**
+ * An Event task is associated with both a start and end date and time.
+ */
+ EVENT("E");
+
+ /** The single-character abbreviation used to represent the task type. */
+ private final String abbreviation;
+
+ /**
+ * Constructs a TaskType with the specified abbreviation.
+ *
+ * @param abbreviation the single-character string used to represent this task type
+ */
+ TaskType(String abbreviation) {
+ this.abbreviation = abbreviation;
+ }
+
+ /**
+ * Retrieves the abbreviation associated with this task type.
+ *
+ * @return the single-character string abbreviation of this task type
+ */
+ public String getAbbreviation() {
+ return abbreviation;
+ }
+}
diff --git a/src/main/java/quinn/task/ToDo.java b/src/main/java/quinn/task/ToDo.java
new file mode 100644
index 000000000..41cec904f
--- /dev/null
+++ b/src/main/java/quinn/task/ToDo.java
@@ -0,0 +1,31 @@
+package quinn.task;
+
+/**
+ * Represents a ToDo task in the Quinn task management application.
+ * A ToDo task is the simplest form of task, consisting of a description without any specific date or time constraints.
+ *
+ * This class extends the abstract {@link Task} class and inherits its basic properties and methods.
+ * It does not add any additional fields or methods beyond those provided by the parent class.
+ */
+public class ToDo extends Task {
+ /**
+ * Constructs a new ToDo task with the given description.
+ * The task is initially marked as not done.
+ *
+ * @param description the description of the ToDo task
+ */
+ public ToDo(String description) {
+ super(TaskType.TODO, description);
+ }
+
+ /**
+ * Constructs a new ToDo task with the given description and completion status.
+ * This constructor is primarily used when loading tasks from storage.
+ *
+ * @param description the description of the ToDo task
+ * @param isDone the initial completion status of the task
+ */
+ public ToDo(String description, boolean isDone) {
+ super(TaskType.TODO, description, isDone);
+ }
+}
diff --git a/src/main/java/quinn/ui/Ui.java b/src/main/java/quinn/ui/Ui.java
new file mode 100644
index 000000000..07cbf3891
--- /dev/null
+++ b/src/main/java/quinn/ui/Ui.java
@@ -0,0 +1,203 @@
+package quinn.ui;
+
+import quinn.task.Task;
+import quinn.task.TaskList;
+
+import java.util.Scanner;
+
+/**
+ * The Ui (User Interface) class handles all interactions between the Quinn task management application
+ * and the user. It is responsible for displaying information to the user and reading user input.
+ * This class provides methods for reading commands, displaying various messages and task information,
+ * and formatting the output for better readability.
+ */
+public class Ui {
+
+ /**
+ * Constructs a new Ui object.
+ * This constructor doesn't initialize any fields as the Ui class currently has no instance variables.
+ */
+ public Ui() {
+ }
+
+ /**
+ * Reads a command from the user via the console.
+ *
+ * @return the user's input command as a trimmed string
+ */
+ public String readCommand() {
+ Scanner sc = new Scanner(System.in);
+ System.out.println("Enter Command:");
+ System.out.print("\t");
+ return sc.nextLine().trim();
+ }
+
+ /**
+ * Displays a welcome message and the Quinn logo when the application starts.
+ * This method prints the welcome message and logo to the console.
+ */
+ public void displayWelcome() {
+ String logo = "\t" + " QQQ U U III N N N N " + System.lineSeparator()
+ + "\t" + " Q Q U U I NN N NN N " + System.lineSeparator()
+ + "\t" + " Q Q U U I N N N N N N " + System.lineSeparator()
+ + "\t" + " Q Q U U I N NN N NN " + System.lineSeparator()
+ + "\t" + " QQQ UUU III N N N N " + System.lineSeparator()
+ + "\t" + " Q " + System.lineSeparator()
+ + "\t" + " QQ " + System.lineSeparator();
+
+ String welcomeMessage = "\t" + "Hello! I'm Quinn, your Personal Assistant ChatBot."
+ + System.lineSeparator()
+ + System.lineSeparator()
+ + logo
+ + System.lineSeparator()
+ + "\t" + "How can I help you?";
+
+ displayResponse(welcomeMessage);
+ displayLine();
+ }
+
+ /**
+ * Displays an exit message when the user chooses to exit the application.
+ * This method prints the farewell message to the console.
+ */
+ public void displayExit() {
+ String exitMessage = "\t" + "Farewell. Hope to see you again soon!";
+ displayResponse(exitMessage);
+ }
+
+
+ /**
+ * Displays a given message to the user, prefixed with a horizontal line for better readability.
+ *
+ * @param message the message to be displayed
+ */
+ public void displayResponse(String message) {
+ displayLine();
+ System.out.println(message);
+ }
+
+
+ /**
+ * Displays an error message to the user
+ *
+ * @param message the error message to be displayed
+ */
+ public void displayError(String message) {
+ displayLine();
+ System.out.println("Error Message:");
+ System.out.println("\t" + "('-')? " + message);
+ }
+
+ /**
+ * Displays a list of filtered tasks to the user.
+ *
+ * @param message the message containing the filtered task information
+ */
+ public void displayFilteredTasks(String message) {
+ displayLine();
+ System.out.println("[FILTERED TASKS]" + System.lineSeparator());
+ System.out.println(message);
+ }
+
+ /**
+ * Displays a horizontal line for separating different sections of output.
+ */
+ public void displayLine() {
+ String horizontalLine = "____________________________________________________________";
+ System.out.println(horizontalLine);
+ }
+
+
+ /**
+ * Generates a message indicating that a task has been added to the list.
+ *
+ * @param task the Task that was added
+ * @return a formatted string confirming the addition of the task
+ */
+ public String taskAddedMessage(Task task) {
+ return "\t" + "Got it. I've added this task:"
+ + System.lineSeparator() + "\t\t" + task;
+ }
+
+ /**
+ * Generates a message indicating that a task has been marked as done.
+ *
+ * @param task the Task that was marked as done
+ * @return a formatted string confirming the task has been marked as done
+ */
+ public String taskDoneMessage(Task task) {
+ return "\t" + "Roger! I've marked this task as done:"
+ + System.lineSeparator() + "\t\t" + task;
+ }
+
+ /**
+ * Generates a message indicating that a task has been marked as not done.
+ *
+ * @param task the Task that was marked as not done
+ * @return a formatted string confirming the task has been marked as not done
+ */
+ public String taskNotDoneMessage(Task task) {
+ return "\t" + "OK, I've marked this task as not done yet:"
+ + System.lineSeparator() + "\t\t" + task;
+ }
+
+ /**
+ * Generates a message indicating that a task has been deleted from the list.
+ *
+ * @param task the Task that was deleted
+ * @return a formatted string confirming the deletion of the task
+ */
+ public String taskDeletedMessage(Task task) {
+ return "\t" + "Roger. I've removed this task:"
+ + System.lineSeparator() + "\t\t" + task;
+ }
+
+ /**
+ * Generates a message indicating the current number of tasks in the list.
+ *
+ * @param taskList the TaskList containing the tasks
+ * @return a formatted string stating the number of tasks in the list
+ */
+ public String numOfTasksInListMessage(TaskList taskList) {
+ return "\t" + "Now you have " + taskList.getNumOfTasks()
+ + (taskList.getNumOfTasks() > 1 ? " tasks" : " task")
+ + " in the list.";
+ }
+
+ /**
+ * Generates a message listing all tasks currently in the task list.
+ *
+ * @param taskList the TaskList containing the tasks to be listed
+ * @return a formatted string containing all tasks in the list
+ */
+ public String tasksInListMessage(TaskList taskList) {
+ return "\t" + "Here"
+ + (taskList.getNumOfTasks() > 1 ? " are the tasks " : " is the task ")
+ + "in your list:"
+ + System.lineSeparator()
+ + "\t" + "[Legend: T = todo, D = deadline, E = event]"
+ + System.lineSeparator()
+ + System.lineSeparator()
+ + taskList;
+ }
+
+ /**
+ * Generates a message listing all tasks that match a given keyword.
+ *
+ * @param taskList the TaskList containing the filtered tasks
+ * @param keyword the keyword used for filtering the tasks
+ * @return a formatted string containing all tasks that match the keyword
+ */
+ public String tasksWithKeywordMessage(TaskList taskList, String keyword) {
+ return "\t" + "Here"
+ + (taskList.getNumOfFilteredTasks() > 1 ? " are the matching tasks " : " is the matching task ")
+ + "in your list:"
+ + System.lineSeparator()
+ + "\t" + "[Keyword Search: " + keyword + "]"
+ + System.lineSeparator()
+ + "\t" + "[Legend: T = todo, D = deadline, E = event]"
+ + System.lineSeparator()
+ + System.lineSeparator()
+ + taskList;
+ }
+}
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 087374464..a71f37185 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 quinn.Quinn < input.txt > ACTUAL.TXT
REM compare the output to the expected output
FC ACTUAL.TXT EXPECTED.TXT