diff --git a/README.md b/README.md
index af0309a9e..7ef723f5e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Duke project template
+# WallE 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,7 +13,7 @@ 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.
-1. 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:
+1. After that, locate the `src/main/java/WallE.java` file, right-click it, and choose `Run WallE.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
____ _
diff --git a/data.txt b/data.txt
new file mode 100644
index 000000000..87bd9db86
--- /dev/null
+++ b/data.txt
@@ -0,0 +1,6 @@
+1,T,some stuff
+0,E,coldplay concert,evening,night
+1,D,submit stuff,2025-05-05,18:00
+1,D,assignment submission,2025-03-03,23:59
+0,E,CCA,evening,night
+0,D,submit JAR release,2023-03-15,23:59
diff --git a/docs/README.md b/docs/README.md
index 47b9f984f..d78c425e6 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,30 +1,153 @@
-# Duke User Guide
-// Update the title above to match the actual product name
+# Wall-E User Guide
-// Product screenshot goes here
+
-// Product intro goes here
+**Product Overview**
+Wall-E is a task management application designed to help you organize and keep track of your tasks with ease.
+You can add todo items, set deadlines, events, and mark tasks as done or not done. The app will save your tasks in a file and retrieve them when you restart it.
-## Adding deadlines
+---
+## Adding Todo Tasks
-// Describe the action and its outcome.
+To add a todo task, simply use the `todo` command followed by the task description.
-// Give examples of usage
+### Example: `todo mop the floor`
-Example: `keyword (optional arguments)`
+**Expected Outcome:**
+This will add a task called "mop the floor" and initialize it as incomplete. It has a "T" type to indicate it is a todo task.
-// A description of the expected outcome goes here
+```
+added: [T][ ] mop the floor
+```
+
+---
+## Adding Deadlines
+
+To add a task with a deadline, simply use the `deadline` command followed by the task description and the due date and time.
+The task command *expects* a valid date and time of the format yyyy-MM-dd HHmm in 24 hour format.
+
+### Example: `deadline finish project /by 2025-03-10 1600`
+
+**Expected Outcome:**
+This will add a task named "finish project" with the deadline of March 10, 2025, 4.00pm. It will be displayed in your task list with the type "D" for Deadline.
+
+```
+added: [D][ ] finish project (by: 2025-03-10 4.00 pm)
+```
+
+---
+## Adding Events
+
+You can add an event using the `event` command, providing the task description and the start and end dates.
+
+### Example: `event team meeting /from 9am /to around 12pm`
+
+**Expected Outcome:**
+This adds an event for "team meeting" that spans from "9am" to "around 12pm". Event does not differentiate between
+real dates and text input.
+
+```
+added: [E][ ] team meeting (from: 2025-03-01, to: 2025-03-02)
+```
+
+---
+
+## Marking Tasks as Done
+
+You can mark any task as completed by using the `mark` command followed by the task number.
+
+### Example: `mark 1`
+
+**Expected Outcome:**
+The task with the specified number will be marked as done.
+
+```
+Nice! I've marked this task as done:
+ [D][X] finish project (by: 2025-03-10)
+```
+
+---
+
+## Unmarking Tasks
+
+If you wish to unmark a task that was previously marked as done, use the `unmark` command followed by the task number.
+
+### Example: `unmark 1`
+
+**Expected Outcome:**
+The task will be unmarked as done.
```
-expected output
+OK, I've marked this task as not done yet:
+ [D][ ] finish project (by: 2025-03-10)
```
-## Feature ABC
+---
-// Feature details
+## Deleting Tasks
+If you no longer need a task, you can delete it using the `delete` command followed by the task number.
+
+### Example: `delete 2`
+
+**Expected Outcome:**
+The task with the specified index will be removed from the task list.
+
+```
+deleted task: [T][ ] Buy groceries
+```
+
+---
+
+## Listing All Tasks
+
+To view all the tasks currently in your list, use the `list` command.
+
+### Example: `list`
+
+**Expected Outcome:**
+This will display all the tasks currently stored in your list.
+
+```
+Here are the tasks in your list:
+1. [D][ ] finish project (by: 2025-03-10)
+2. [E][ ] team meeting (from: 2025-03-01, to: 2025-03-02)
+```
+
+---
+
+## Find By Keyword or Phrase
+
+To find tasks that contain some keyword or phrase, use the `find` command.
+
+### Example: `find homework`
+
+**Expected Outcome:**
+This will display tasks that contain the provided search term(s).
+
+```
+Results of find command:
+1. [D][ ] do homework (by: 2025-03-10)
+2. [E][ ] submit cs2113 homework (from: 2025-03-01, to: 2025-03-02)
+```
+
+---
+
+## Exit the Application
+
+To close the Wall-E application and save your tasks, use the `bye` command.
+
+### Example: `bye`
+
+**Expected Outcome:**
+Wall-E will save the tasks to the file and print an exit message.
+
+```
+Saved data to data.txt
+Bye. Hope to see you again soon!
+```
-## Feature XYZ
+---
-// Feature details
\ No newline at end of file
+This is your guide to using the Wall-E application. Enjoy organizing your tasks with ease!
diff --git a/docs/assets/WallE-Screenshot.png b/docs/assets/WallE-Screenshot.png
new file mode 100644
index 000000000..885a40474
Binary files /dev/null and b/docs/assets/WallE-Screenshot.png differ
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/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..7d7f196bb
--- /dev/null
+++ b/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: walle.WallE
+
diff --git a/src/main/java/walle/Storage.java b/src/main/java/walle/Storage.java
new file mode 100644
index 000000000..dec807274
--- /dev/null
+++ b/src/main/java/walle/Storage.java
@@ -0,0 +1,119 @@
+package walle;
+
+import walle.task.Deadline;
+import walle.task.Event;
+import walle.task.Task;
+import walle.task.Todo;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+/**
+ * Handles reading from and writing to a storage file for tasks.
+ * Provides methods to load tasks from a file and save tasks to a file.
+ */
+public class Storage {
+ private File file;
+
+ /**
+ * Reads the contents of the specified file and loads tasks into the provided list.
+ *
+ * @param filePath The path to the file to read from.
+ * @param tasks The list to store the loaded tasks.
+ * @return The number of tasks read from the file.
+ * @throws FileNotFoundException If the file cannot be found.
+ */
+ public int readFileContents(String filePath, ArrayList tasks) throws FileNotFoundException {
+ Scanner scanner = new Scanner(file);
+ int listSize = 0;
+ while (scanner.hasNext()) {
+ String[] taskString = scanner.nextLine().split(",");
+ boolean isDone = taskString[0].equals("1") ? true : false;
+ String taskType = taskString[1];
+ String taskDescription = taskString[2];
+
+ if (taskType.equals("T")) {
+ tasks.add(new Todo(taskDescription));
+ } else if (taskType.equals("D")) {
+ String dueDate = taskString[3];
+ String dueTime = taskString[4];
+ tasks.add(new Deadline(taskDescription, dueDate, dueTime));
+ } else if (taskType.equals("E")) {
+ String fromDate = taskString[3];
+ String toDate = taskString[4];
+ tasks.add(new Event(taskDescription, fromDate, toDate));
+ }
+
+ if (isDone) {
+ tasks.get(listSize).markAsDone();
+ }
+
+ listSize++;
+ }
+ scanner.close();
+ return listSize;
+ }
+
+ private void createFile(String filePath) {
+ try {
+ File tempFile = new File(filePath);
+ if (tempFile.createNewFile()) {
+ System.out.println("File created: " + tempFile.getName());
+ FileWriter writer = new FileWriter(tempFile);
+ writer.close();
+ } else {
+ System.out.println("File already exists.");
+ }
+ } catch (IOException e) {
+ System.out.println("An error occurred while creating the file.");
+ }
+ }
+
+ /**
+ * Instantiate storage functionality by providing relative file path to stored data.
+ * Creates a new file if file does not exist.
+ *
+ * @param filePath Relative path to the data file to read from.
+ */
+ public Storage(String filePath) {
+ file = new File(filePath);
+ if (!file.exists()) {
+ createFile(filePath);
+ }
+ }
+
+ /**
+ * Saves the tasks to the file at the specified path.
+ *
+ * @param filePath The path to the file to save to.
+ * @param tasks The list of tasks to save.
+ * @param listSize The number of tasks to save.
+ */
+ public void saveToFile(String filePath, ArrayList tasks, int listSize) {
+ try {
+ FileWriter writer = new FileWriter(file);
+ for (int i = 0; i < listSize; i++) {
+ Task t = tasks.get(i);
+ String status = t.isDone() ? "1" : "0";
+ writer.write(status + "," + t.getTypeIcon() + "," + t.getDescription());
+ if (t.getTypeIcon() == "T") {
+ writer.write("\n");
+ } else if (t.getTypeIcon() == "D") {
+ Deadline d = (Deadline) t;
+ writer.write("," + d.getDueDate() + "," + d.getDueTime() + "\n");
+ } else if (t.getTypeIcon() == "E") {
+ Event e = (Event) t;
+ writer.write("," + e.getStartDate() + "," + e.getEndDate() + "\n");
+ }
+ }
+ writer.close();
+ } catch (Exception e) {
+ System.out.println("An error occurred while saving the file.");
+ System.out.println(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/walle/TaskList.java b/src/main/java/walle/TaskList.java
new file mode 100644
index 000000000..94078d455
--- /dev/null
+++ b/src/main/java/walle/TaskList.java
@@ -0,0 +1,136 @@
+package walle;
+
+import walle.task.Deadline;
+import walle.task.Event;
+import walle.task.Task;
+import walle.task.Todo;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
+
+/**
+ * Represents a list of tasks, with methods for managing tasks such as adding, removing,
+ * marking/unmarking, and displaying them.
+ */
+public class TaskList {
+ private int listSize;
+ private ArrayList tasks;
+ private UserInterface ui;
+
+ /**
+ * Prints the list of tasks to the user interface.
+ */
+ public void printTaskList() {
+ ui.printLineBreak();
+ ui.printWithoutLineBreak("Here are the tasks in your list: ");
+ for (int i = 0; i < listSize; i++) {
+ ui.printWithoutLineBreak(Integer.toString(i + 1) + ". " + tasks.get(i).toString());
+ }
+ ui.printLineBreak();
+ }
+
+ /**
+ * Deletes a task from the list by its index.
+ *
+ * @param taskIndex The index of the task to delete.
+ */
+ public void deleteTask(int taskIndex) {
+ Task deletedTask = tasks.remove(taskIndex - 1);
+ listSize = getListSize() - 1;
+ ui.printWithLineBreak("Deleted task: " + deletedTask.toString());
+ }
+
+ /**
+ * Adds an event task to the task list.
+ *
+ * @param eventName The name/description of the event.
+ * @param startDate The start date of the event.
+ * @param endDate The end date of the event.
+ */
+ public void addEvent(String eventName, String startDate, String endDate) {
+ tasks.add(new Event(eventName, startDate, endDate));
+ listSize = getListSize() + 1;
+ ui.printWithLineBreak("Added: " + tasks.get(getListSize() - 1).toString());
+ }
+
+ /**
+ * Adds a deadline task to the task list.
+ *
+ * @param taskName The name/description of the task.
+ * @param dueDate The due date of the task.
+ */
+ public void addDeadline(String taskName, LocalDate dueDate, LocalTime deadlineTime) {
+ tasks.add(new Deadline(taskName, dueDate, deadlineTime));
+ listSize = getListSize() + 1;
+ ui.printWithLineBreak("Added: " + tasks.get(getListSize() - 1).toString());
+ }
+
+ /**
+ * Adds a to-do task to the task list.
+ *
+ * @param todoTask The name/description of the to-do task.
+ */
+ public void addTodo(String todoTask) {
+ tasks.add(new Todo(todoTask));
+ listSize = getListSize() + 1;
+ ui.printWithLineBreak("Added: " + tasks.get(getListSize() - 1).toString());
+ }
+
+ /**
+ * Unmarks a task as done.
+ *
+ * @param taskIndex The index of the task to unmark.
+ */
+ public void unmarkTask(int taskIndex) {
+ tasks.get(taskIndex - 1).unmarkAsDone();
+ ui.printWithLineBreak("OK, I've marked this task as not done yet:\n"
+ + "\t" + tasks.get(taskIndex - 1).toString());
+ }
+
+ /**
+ * Marks a task as done.
+ *
+ * @param taskIndex The index of the task to mark.
+ */
+ public void markTask(int taskIndex) {
+ tasks.get(taskIndex - 1).markAsDone();
+ ui.printWithLineBreak("Nice! I've marked this task as done:\n" + "\t" + tasks.get(taskIndex - 1));
+ }
+
+ /**
+ * Constructs an empty TaskList with no {@link Task} stored.
+ */
+ public TaskList() {
+ listSize = 0;
+ tasks = new ArrayList<>();
+ ui = new UserInterface();
+ }
+
+ /**
+ * Returns the current size of the task list.
+ *
+ * @return The number of tasks in the list.
+ */
+ public int getListSize() {
+ return listSize;
+ }
+
+ /**
+ * Returns the list of tasks.
+ *
+ * @return An ArrayList of tasks.
+ */
+ public ArrayList getTasks() {
+ return tasks;
+ }
+
+ /**
+ * Sets the size of the task list.
+ *
+ * @param listSize The new size of the task list.
+ */
+ public void setListSize(int listSize) {
+ this.listSize = listSize;
+ }
+}
diff --git a/src/main/java/walle/UserInterface.java b/src/main/java/walle/UserInterface.java
new file mode 100644
index 000000000..6ed0ea66f
--- /dev/null
+++ b/src/main/java/walle/UserInterface.java
@@ -0,0 +1,77 @@
+package walle;
+
+import java.util.Scanner;
+
+/**
+ * Represents the user interface of the Wall-E application. Provides methods to print messages
+ * with specific formatting like line breaks and greetings.
+ */
+public class UserInterface {
+ private static final String EXIT_MESSAGE = "Bye. Hope to see you again soon!";
+ private static final String GREETING = "Hello! I'm Wall-E!\n" + "\tWhat can I do for you?\n\n";
+ private Scanner scanner;
+
+ /**
+ * Prints a line break for formatting purposes.
+ */
+ public void printLineBreak() {
+ String lineBreak = "\t_________________________________\n";
+ System.out.print(lineBreak);
+ }
+
+ /**
+ * Prints a message with line breaks before and after it.
+ *
+ * @param response The message to be printed.
+ */
+ public void printWithLineBreak(String response) {
+ printLineBreak();
+ System.out.println("\t" + response);
+ printLineBreak();
+ }
+
+ /**
+ * Prints a message without a line break after it.
+ *
+ * @param response The message to be printed.
+ */
+ public void printWithoutLineBreak(String response) {
+ System.out.println("\t" + response);
+ }
+
+ /**
+ * Prints a greeting message to the user.
+ */
+ public void printGreeting() {
+ printWithLineBreak(GREETING);
+ }
+
+ /**
+ * Prints the exit message when the user decides to exit the program.
+ */
+ public void printExitMessage() {
+ printWithLineBreak(EXIT_MESSAGE);
+ }
+
+ /**
+ * Gets next line of input from user.
+ * @return Input from user in {@link String} format
+ */
+ public String getInput() {
+ return scanner.nextLine();
+ }
+
+ /**
+ * Closes standard input to prevent memory leakage.
+ */
+ public void closeInput() {
+ scanner.close();
+ }
+
+ /**
+ * Constructs a new UserInterface object and instantiates standard input.
+ */
+ public UserInterface() {
+ scanner = new Scanner(System.in);
+ }
+}
diff --git a/src/main/java/walle/WallE.java b/src/main/java/walle/WallE.java
new file mode 100644
index 000000000..8b857b0e9
--- /dev/null
+++ b/src/main/java/walle/WallE.java
@@ -0,0 +1,88 @@
+package walle;
+
+import walle.command.CommandParser;
+import walle.command.CommandType;
+
+import walle.exception.InvalidCommandException;
+import walle.exception.InvalidCommandParameterException;
+import walle.exception.WallEException;
+
+import java.io.FileNotFoundException;
+import java.util.Scanner;
+
+/**
+ * Represents the Wall-E application, which handles user input, task management, and file storage.
+ */
+public class WallE {
+ private static final String FILE_NAME = "data.txt";
+ private UserInterface ui;
+ private TaskList tasks;
+ private Storage storage;
+ private CommandParser parser;
+
+ private void handleUserInput(String[] params) {
+ CommandType commandType = null;
+ try {
+ commandType = parser.getCommandType(params);
+ parser.handleCommand(commandType, params, tasks, ui);
+ } catch (InvalidCommandException e) {
+ ui.printWithLineBreak(e.getMessage());
+ } catch (InvalidCommandParameterException e) {
+ ui.printWithLineBreak(e.getMessage());
+ }
+ }
+
+ private void initialiseWallE() throws WallEException {
+ ui.printGreeting();
+ storage = new Storage(FILE_NAME);
+ try {
+ tasks.setListSize(storage.readFileContents(FILE_NAME, tasks.getTasks()));
+ } catch (FileNotFoundException e) {
+ throw new WallEException("Error: Unable to create storage file.");
+ }
+ }
+
+ private void exitWallE() {
+ storage.saveToFile("data.txt", tasks.getTasks(), tasks.getListSize());
+ ui.printWithoutLineBreak("Saved data to " + FILE_NAME);
+ ui.printExitMessage();
+ }
+
+ private void run() throws WallEException {
+ initialiseWallE();
+
+ ui.printWithLineBreak("Stored list size: " + tasks.getListSize());
+ String userInput = ui.getInput();
+ while (!userInput.equals("bye")) {
+ String[] params = userInput.split(" ");
+ handleUserInput(params);
+ userInput = ui.getInput();
+ }
+
+ exitWallE();
+ ui.closeInput();
+ }
+
+ /**
+ * Constructs a new WallE object and initializes the user interface, task list, and command parser.
+ */
+ public WallE() {
+ ui = new UserInterface();
+ tasks = new TaskList();
+ parser = new CommandParser();
+ }
+
+ /**
+ * The main method that starts the Wall-E application.
+ *
+ * @param args Command-line arguments (not used).
+ */
+ public static void main(String[] args) {
+ WallE wallE = new WallE();
+ try {
+ wallE.run();
+ } catch (WallEException e) {
+ System.out.println("\t" + e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/walle/command/CommandParser.java b/src/main/java/walle/command/CommandParser.java
new file mode 100644
index 000000000..e5497df06
--- /dev/null
+++ b/src/main/java/walle/command/CommandParser.java
@@ -0,0 +1,290 @@
+package walle.command;
+
+import walle.TaskList;
+import walle.UserInterface;
+import walle.exception.InvalidCommandException;
+import walle.exception.InvalidCommandParameterException;
+import walle.task.Task;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * The CommandParser class is responsible for parsing user input commands,
+ * determining their type, and delegating them to appropriate handlers.
+ */
+ public class CommandParser {
+ private static final int LIST_COMMAND_LENGTH = 1;
+ private static final int INDEX_COMMAND_LENGTH = 2;
+ private static final int MIN_TODO_LENGTH = 2;
+ private static final int MIN_DEADLINE_LENGTH = 4;
+ private static final int MIN_EVENT_LENGTH = 6;
+
+ /**
+ * Parses the command array to determine the command type.
+ *
+ * @param params A string array where the first element specifies the command type.
+ * @return The corresponding {@link CommandType}.
+ * @throws InvalidCommandException If the command is invalid.
+ */
+ public CommandType getCommandType(String[] params) throws InvalidCommandException {
+ CommandType commandType;
+ switch (params[0]) {
+ case "list":
+ commandType = CommandType.LIST;
+ break;
+
+ case "mark":
+ commandType = CommandType.MARK;
+ break;
+
+ case "unmark":
+ commandType = CommandType.UNMARK;
+ break;
+
+ case "todo":
+ commandType = CommandType.TODO;
+ break;
+
+ case "deadline":
+ commandType = CommandType.DEADLINE;
+ break;
+
+ case "event":
+ commandType = CommandType.EVENT;
+ break;
+
+ case "delete":
+ commandType = CommandType.DELETE;
+ break;
+
+ case "find":
+ commandType = CommandType.FIND;
+ break;
+
+ default:
+ commandType = null;
+ }
+
+ if (commandType == null) {
+ throw new InvalidCommandException("Invalid command type");
+ }
+ return commandType;
+ }
+
+ /**
+ * Calls the appropriate handler based on the commandType.
+ *
+ * @param commandType The type of the command to execute.
+ * @param params The parameters associated with the command.
+ * @param tasks The list of tasks to modify based on the command.
+ * @throws InvalidCommandParameterException If the command parameters are invalid.
+ * @throws InvalidCommandException If the command type is unrecognized.
+ */
+ public void handleCommand(CommandType commandType, String[] params, TaskList tasks, UserInterface ui) throws InvalidCommandParameterException, InvalidCommandException {
+ switch (commandType) {
+ case LIST:
+ handleListCommand(params, tasks);
+ break;
+ case MARK:
+ handleMarkCommand(params, tasks);
+ break;
+ case UNMARK:
+ handleUnmarkCommand(params, tasks);
+ break;
+ case TODO:
+ handleTodoCommand(params, tasks);
+ break;
+ case DELETE:
+ handleDeleteCommand(params, tasks);
+ break;
+ case DEADLINE:
+ handleDeadlineCommand(params, tasks);
+ break;
+ case EVENT:
+ handleEventCommand(params, tasks);
+ break;
+ case FIND:
+ handleFindCommand(params, tasks, ui);
+ break;
+ default:
+ throw new InvalidCommandException("Unknown command type");
+ }
+ }
+
+ private void handleListCommand(String[] params, TaskList tasks) throws InvalidCommandParameterException {
+ if (params.length != LIST_COMMAND_LENGTH) {
+ throw new InvalidCommandParameterException("Invalid list command. List does not take any arguments.");
+ }
+ tasks.printTaskList();
+ }
+
+ private void handleMarkCommand(String[] params, TaskList tasks) throws InvalidCommandParameterException {
+ if (params.length != INDEX_COMMAND_LENGTH) {
+ throw new InvalidCommandParameterException("Invalid mark command. Mark takes index as argument.");
+ }
+ if (!isValidInteger(params[1])) {
+ throw new InvalidCommandParameterException("Mark command expects an integer.");
+ }
+ if (!isValidIndex(params[1], tasks)) {
+ throw new InvalidCommandParameterException("Mark command expects a valid index.");
+ }
+
+ int index = Integer.parseInt(params[1]);
+ tasks.markTask(index);
+ }
+
+ private void handleUnmarkCommand(String[] params, TaskList tasks) throws InvalidCommandParameterException {
+ if (params.length != INDEX_COMMAND_LENGTH) {
+ throw new InvalidCommandParameterException("Invalid unmark command. Unmark takes index as argument.");
+ }
+ if (!isValidInteger(params[1])) {
+ throw new InvalidCommandParameterException("Unmark command expects a number.");
+ }
+ if (!isValidIndex(params[1], tasks)) {
+ throw new InvalidCommandParameterException("Unmark command expects a valid index.");
+ }
+
+ int index = Integer.parseInt(params[1]);
+ tasks.unmarkTask(index);
+ }
+
+ private void handleTodoCommand(String[] params, TaskList tasks) throws InvalidCommandParameterException {
+ if (params.length < MIN_TODO_LENGTH) {
+ throw new InvalidCommandParameterException("Enter a todo task.");
+ }
+
+ String todoTask = String.join(" ", Arrays.copyOfRange(params, 1, params.length));
+ tasks.addTodo(todoTask);
+ }
+
+ private void handleDeleteCommand(String[] params, TaskList tasks) throws InvalidCommandParameterException {
+ if (params.length != INDEX_COMMAND_LENGTH) {
+ throw new InvalidCommandParameterException("Delete takes index as argument.");
+ }
+ if (!isValidInteger(params[1])) {
+ throw new InvalidCommandParameterException("Delete command expects a number.");
+ }
+ if (tasks.getListSize() == 0) {
+ throw new InvalidCommandParameterException("Nothing to delete in task list.");
+ }
+ if (!isValidIndex(params[1], tasks)) {
+ throw new InvalidCommandParameterException("Delete command expects a valid index.");
+ }
+
+ int taskIndex = Integer.parseInt(params[1]);
+ tasks.deleteTask(taskIndex);
+ }
+
+ private void handleDeadlineCommand(String[] params, TaskList tasks) throws InvalidCommandParameterException {
+ if (params.length < MIN_DEADLINE_LENGTH) {
+ throw new InvalidCommandParameterException("Invalid deadline command. Deadline command: deadline /by ");
+ }
+
+ int byIndex = Arrays.asList(params).indexOf("/by");
+ if (byIndex == -1 || byIndex >= params.length - 2) {
+ throw new InvalidCommandParameterException("Deadline command expects a valid due date and time of the format yyyy-MM-dd HHMM.");
+ }
+ if (!isValidDate(params[byIndex + 1])) {
+ throw new InvalidCommandParameterException("Deadline command expects a valid due date of the format yyyy-MM-dd HHMM.");
+ }
+ if (!isValidTime(params[byIndex + 2])) {
+ throw new InvalidCommandParameterException("Deadline command expects a valid time of the format HHMM (no separators between hour and minutes).");
+ }
+
+ String taskName = String.join(" ", Arrays.copyOfRange(params, 1, byIndex));
+ String dueDateStr = params[byIndex + 1];
+ String dueTimeStr = params[byIndex + 2];
+ DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HHmm");
+
+ tasks.addDeadline(taskName, LocalDate.parse(dueDateStr, dateFormatter), LocalTime.parse(dueTimeStr, timeFormatter));
+ }
+
+ private void handleEventCommand(String[] params, TaskList tasks) throws InvalidCommandParameterException {
+ if (params.length < MIN_EVENT_LENGTH) {
+ throw new InvalidCommandParameterException("Invalid event format. Event command: event /from /to ");
+ }
+
+ int fromIndex = Arrays.asList(params).indexOf("/from");
+ int toIndex = Arrays.asList(params).indexOf("/to");
+
+ if (fromIndex == -1 || toIndex == -1 || toIndex == params.length - 1 || toIndex == params.length - 1) {
+ throw new InvalidCommandParameterException("Event command expects a valid start and end date time.");
+ }
+ if (toIndex < fromIndex || fromIndex + 1 == toIndex) {
+ throw new InvalidCommandParameterException("Invalid event format. Event command: event /from /to ");
+ }
+ if (fromIndex == 1) {
+ throw new InvalidCommandParameterException("Event name expected.");
+ }
+
+ String eventName = String.join(" ", Arrays.copyOfRange(params, 1, fromIndex));
+ String fromDate = String.join(" ", Arrays.copyOfRange(params, fromIndex + 1, toIndex));
+ String toDate = String.join(" ", Arrays.copyOfRange(params, toIndex + 1, params.length));
+ tasks.addEvent(eventName, fromDate, toDate);
+ }
+
+ private void handleFindCommand(String[] params, TaskList tasks, UserInterface ui) throws InvalidCommandParameterException {
+ if (params.length <= LIST_COMMAND_LENGTH) {
+ throw new InvalidCommandParameterException("Invalid find command. Find takes at least one keyword as argument.");
+ }
+
+ String searchStr = String.join(" ", Arrays.copyOfRange(params, 1, params.length));
+ ui.printLineBreak();
+ ui.printWithoutLineBreak("Results of find command:");
+ ArrayList taskIterable = tasks.getTasks();
+ int index = 1;
+
+ for (Task task : taskIterable) {
+ if (!task.getDescription().toLowerCase().contains(searchStr.toLowerCase())) {
+ continue;
+ }
+ ui.printWithoutLineBreak(String.valueOf(index) + ". " + task.toString());
+ index++;
+ }
+ if (index == 1) {
+ ui.printWithoutLineBreak("No tasks found.");
+ }
+
+ ui.printLineBreak();
+ }
+
+
+ private boolean isValidInteger(String input) {
+ try {
+ Integer.parseInt(input);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isValidIndex(String input, TaskList tasks) {
+ int index = Integer.parseInt(input);
+ return index >= 1 && index <= tasks.getListSize();
+ }
+
+ private boolean isValidDate(String dateStr) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ try {
+ LocalDate.parse(dateStr, formatter);
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isValidTime(String timeStr) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HHmm");
+ try {
+ LocalTime.parse(timeStr, formatter);
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/walle/command/CommandType.java b/src/main/java/walle/command/CommandType.java
new file mode 100644
index 000000000..dd41d18d8
--- /dev/null
+++ b/src/main/java/walle/command/CommandType.java
@@ -0,0 +1,16 @@
+package walle.command;
+
+/**
+ * Enum representing the different types of commands that can be executed.
+ * This enum defines the available commands that the system can handle.
+ */
+public enum CommandType {
+ LIST,
+ MARK,
+ UNMARK,
+ TODO,
+ DEADLINE,
+ EVENT,
+ DELETE,
+ FIND;
+}
diff --git a/src/main/java/walle/exception/InvalidCommandException.java b/src/main/java/walle/exception/InvalidCommandException.java
new file mode 100644
index 000000000..88f5de659
--- /dev/null
+++ b/src/main/java/walle/exception/InvalidCommandException.java
@@ -0,0 +1,10 @@
+package walle.exception;
+
+/**
+ * Exception thrown when an invalid command is encountered.
+ */
+public class InvalidCommandException extends Exception {
+ public InvalidCommandException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/walle/exception/InvalidCommandParameterException.java b/src/main/java/walle/exception/InvalidCommandParameterException.java
new file mode 100644
index 000000000..93c6007b8
--- /dev/null
+++ b/src/main/java/walle/exception/InvalidCommandParameterException.java
@@ -0,0 +1,10 @@
+package walle.exception;
+
+/**
+ * Exception thrown when the parameters of a command are invalid.
+ */
+public class InvalidCommandParameterException extends Exception {
+ public InvalidCommandParameterException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/walle/exception/WallEException.java b/src/main/java/walle/exception/WallEException.java
new file mode 100644
index 000000000..2a521a92d
--- /dev/null
+++ b/src/main/java/walle/exception/WallEException.java
@@ -0,0 +1,10 @@
+package walle.exception;
+
+/**
+ * A custom exception class for handling general exceptions in the WallE application.
+ */
+public class WallEException extends Exception {
+ public WallEException(String errorMessage) {
+ super(errorMessage);
+ }
+}
diff --git a/src/main/java/walle/task/Deadline.java b/src/main/java/walle/task/Deadline.java
new file mode 100644
index 000000000..45c3af3a9
--- /dev/null
+++ b/src/main/java/walle/task/Deadline.java
@@ -0,0 +1,66 @@
+package walle.task;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Represents a task with a deadline.
+ * Inherits from the {@link Task} class and adds a due date.
+ */
+public class Deadline extends Task {
+ protected LocalDate dueDate;
+ protected LocalTime dueTime;
+
+ public Deadline(String description, LocalDate dueDate, LocalTime dueTime) {
+ super(description);
+ this.dueDate = dueDate;
+ this.dueTime = dueTime;
+ }
+
+ public Deadline(String description, String dueDate, String dueTime) {
+ super(description);
+ this.dueDate = LocalDate.parse(dueDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+ this.dueTime = LocalTime.parse(dueTime, DateTimeFormatter.ofPattern("HH:mm"));
+ }
+
+ /**
+ * Returns the type of the task as a string.
+ *
+ * @return A string representing the task type ("[D]").
+ */
+ @Override
+ public String getType() {
+ return "[D]";
+ }
+
+ /**
+ * Returns the type icon for the task.
+ *
+ * @return A string representing the type icon ("D").
+ */
+ public String getTypeIcon() {
+ return "D";
+ }
+
+ /**
+ * Returns the due date of the task.
+ *
+ * @return The due date as a string.
+ */
+ public String getDueDate() {
+ return dueDate.toString();
+ }
+
+ public String getDueTime() {
+ return dueTime.toString();
+ }
+
+ @Override
+ public String toString() {
+ DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
+ DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
+
+ return super.toString() + " (by: " + dueDate.format(dateFormatter) + " " + dueTime.format(timeFormatter) + ")";
+ }
+}
diff --git a/src/main/java/walle/task/Event.java b/src/main/java/walle/task/Event.java
new file mode 100644
index 000000000..ad3ccedda
--- /dev/null
+++ b/src/main/java/walle/task/Event.java
@@ -0,0 +1,65 @@
+package walle.task;
+
+/**
+ * Represents an event task with a start and end date.
+ * Inherits from the {@link Task} class and adds start and end dates.
+ */
+public class Event extends Task {
+ protected String startDate;
+ protected String endDate;
+
+ /**
+ * Constructs an Event task with a description, start date, and end date.
+ *
+ * @param description The description of the event.
+ * @param startDate The start date of the event.
+ * @param endDate The end date of the event.
+ */
+ public Event(String description, String startDate, String endDate) {
+ super(description);
+ this.startDate = startDate;
+ this.endDate = endDate;
+ }
+
+ /**
+ * Returns the type of the event task as a string.
+ *
+ * @return A string representing the task type ("[E]").
+ */
+ @Override
+ public String getType() {
+ return "[E]";
+ }
+
+ /**
+ * Returns the type icon for the event task.
+ *
+ * @return A string representing the type icon ("E").
+ */
+ public String getTypeIcon() {
+ return "E";
+ }
+
+ /**
+ * Returns the start date of the event.
+ *
+ * @return The start date as a string.
+ */
+ public String getStartDate() {
+ return startDate;
+ }
+
+ /**
+ * Returns the end date of the event.
+ *
+ * @return The end date as a string.
+ */
+ public String getEndDate() {
+ return endDate;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " (from: " + startDate + ", to: " + endDate + ")";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/walle/task/Task.java b/src/main/java/walle/task/Task.java
new file mode 100644
index 000000000..a6d89b4c7
--- /dev/null
+++ b/src/main/java/walle/task/Task.java
@@ -0,0 +1,79 @@
+package walle.task;
+
+/**
+ * Represents a general task with a description and completion status.
+ * This is the base class for other specific types of tasks.
+ */
+public class Task {
+ protected String description;
+ protected boolean isDone;
+
+ /**
+ * Constructs a task with the specified description and initializes its status as incomplete.
+ *
+ * @param description The description of the task.
+ */
+ public Task(String description) {
+ this.description = description;
+ this.isDone = false;
+ }
+
+ /**
+ * Returns the status icon representing the completion status of the task.
+ *
+ * @return A string representing the task's completion status ("[X]" for done, "[ ]" for not done).
+ */
+ public String getStatusIcon() {
+ return (isDone ? "[X]" : "[ ]");
+ }
+
+ /**
+ * Marks the task as completed.
+ */
+ public void markAsDone() {
+ isDone = true;
+ }
+
+ /**
+ * Marks the task as incomplete.
+ */
+ public void unmarkAsDone() {
+ isDone = false;
+ }
+
+ /**
+ * Returns the type of the task as a string.
+ *
+ * @return A string representing the task type ("[ ]").
+ */
+ public String getType() {
+ return "[ ]";
+ }
+
+ /**
+ * Returns the type icon for the task.
+ *
+ * @return A string representing the type icon (" ").
+ */
+ public String getTypeIcon() {
+ return " ";
+ }
+
+ /**
+ * Returns the description of the task.
+ *
+ * @return The description as a string.
+ */
+ public boolean isDone() {
+ return isDone;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public String toString() {
+ return getType() + getStatusIcon() + " " + description;
+ }
+}
diff --git a/src/main/java/walle/task/Todo.java b/src/main/java/walle/task/Todo.java
new file mode 100644
index 000000000..dcb85d984
--- /dev/null
+++ b/src/main/java/walle/task/Todo.java
@@ -0,0 +1,35 @@
+package walle.task;
+
+/**
+ * Represents a to-do task.
+ * Inherits from the {@link Task} class and specifies task type as "to-do".
+ */
+public class Todo extends Task {
+ /**
+ * Constructs a Todo task with the specified description.
+ *
+ * @param description The description of the to-do task.
+ */
+ public Todo(String description) {
+ super(description);
+ }
+
+ /**
+ * Returns the type of the to-do task as a string.
+ *
+ * @return A string representing the task type ("[T]").
+ */
+ @Override
+ public String getType() {
+ return "[T]";
+ }
+
+ /**
+ * Returns the type icon for the to-do task.
+ *
+ * @return A string representing the type icon ("T").
+ */
+ public String getTypeIcon() {
+ return "T";
+ }
+}
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 087374464..aee335182 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 WallE < input.txt > ACTUAL.TXT
REM compare the output to the expected output
FC ACTUAL.TXT EXPECTED.TXT