It recognizes specific keywords in the user input (such as "todo", "deadline",
+ * "event", etc.) and creates the corresponding Command objects with the necessary
+ * information extracted from the input.
+ */
+public class Parser {
+
+
+ private static final DateTimeFormatter INPUT_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("d/M/yyyy HHmm");
+ private static final String EXIT_COMMAND = "bye";
+ private static final String LIST_COMMAND = "list";
+ private static final String TODO_COMMAND = "todo";
+ private static final String DEADLINE_COMMAND = "deadline";
+ private static final String EVENT_COMMAND = "event";
+ private static final String DELETE_COMMAND = "delete";
+ private static final String MARK_COMMAND = "mark";
+ private static final String UNMARK_COMMAND = "unmark";
+ private static final String FIND_COMMAND = "find";
+ private static final String UPCOMING_COMMAND = "upcoming";
+
+ /**
+ * Parses user input into a recognizable Command object.
+ *
+ * @param fullCommand The entire user input string.
+ * @return The Command object corresponding to the user input.
+ * @throws JarvisException If the user input is not recognized or has an incorrect format.
+ */
+ public Command parse(String fullCommand) throws JarvisException {
+ String[] parts = fullCommand.split(" ", 2);
+ String commandType = parts[0];
+
+ switch (commandType) {
+ case EXIT_COMMAND:
+ return new ExitCommand();
+ case LIST_COMMAND:
+ return new ListCommand();
+ case UPCOMING_COMMAND:
+ return new UpcomingCommand();
+ case TODO_COMMAND:
+ return parseTodo(parts);
+ case DEADLINE_COMMAND:
+ return parseDeadline(parts);
+ case EVENT_COMMAND:
+ return parseEvent(parts);
+ case DELETE_COMMAND:
+ return parseDelete(parts);
+ case MARK_COMMAND:
+ return parseMark(parts);
+ case UNMARK_COMMAND:
+ return parseUnmark(parts);
+ case FIND_COMMAND:
+ return parseFind(parts);
+ default:
+ throw new JarvisUnrecognisedCommandException();
+ }
+ }
+
+ private String[] ensureValidParts(String[] parts, String[] parameters, String[] delimiters) throws JarvisException {
+ assert parts != null : "Parts array should not be null";
+
+ if (parts.length < 2 || parts[1].isEmpty()) {
+ throw new JarvisMissingValueException(parameters[0], parts[0]);
+ }
+
+ // Start by using the original parts[1] for extraction
+ String remainingPart = parts[1];
+ String[] extractedParameters = new String[parameters.length];
+
+ // Iterate through each delimiter and extract the corresponding parameter
+ for (int i = 0; i < delimiters.length; i++) {
+ if (remainingPart.contains(delimiters[i])) {
+ String[] splitParts = remainingPart.split(delimiters[i], 2);
+ extractedParameters[i] = splitParts[0].trim();
+ remainingPart = splitParts[1];
+ } else {
+ throw new JarvisMissingValueException(parameters[i + 1], parts[0]);
+ }
+ }
+
+ // The last parameter will be the remaining content after all splits
+ extractedParameters[parameters.length - 1] = remainingPart.trim();
+
+ return extractedParameters;
+ }
+
+ private LocalDateTime parseDateTime(String dateTime) throws JarvisWrongDateFormatException {
+ try {
+ return LocalDateTime.parse(dateTime, INPUT_DATE_TIME_FORMATTER);
+ } catch (DateTimeParseException e) {
+ throw new JarvisWrongDateFormatException();
+ }
+ }
+
+ private Command parseTodo(String[] parts) throws JarvisException {
+ String[] taskParameters = new String[]{"description"};
+ String[] delimiters = new String[]{}; // no delimiters
+ String[] parameterValues = ensureValidParts(parts, taskParameters, delimiters);
+ String description = parameterValues[0];
+ return new AddCommand(new Todo(description));
+ }
+
+ private Command parseDeadline(String[] parts) throws JarvisException {
+ String[] taskParameters = new String[]{"description", "deadline time"};
+ String[] delimiters = new String[]{" /by "};
+ String[] parameterValues = ensureValidParts(parts, taskParameters, delimiters);
+ String description = parameterValues[0];
+ LocalDateTime by = parseDateTime(parameterValues[1]);
+ return new AddCommand(new Deadline(description, by));
+ }
+
+ private Command parseEvent(String[] parts) throws JarvisException {
+ String[] taskParameters = new String[]{"description", "start time", "end time"};
+ String[] delimiters = new String[]{" /from ", " /to "};
+ String[] parameterValues = ensureValidParts(parts, taskParameters, delimiters);
+ String description = parameterValues[0];
+ LocalDateTime from = parseDateTime(parameterValues[1]);
+ LocalDateTime to = parseDateTime(parameterValues[2]);
+ return new AddCommand(new Event(description, from, to));
+ }
+
+ private Command parseFind(String[] parts) throws JarvisException {
+ String[] taskParameters = new String[]{"keyword"};
+ String[] delimiters = new String[]{}; // no delimiters
+ String[] parameterValues = ensureValidParts(parts, taskParameters, delimiters);
+ String keyword = parameterValues[0];
+ return new FindCommand(keyword);
+ }
+
+ private int parseIndex(String[] parts) throws JarvisException {
+ String[] taskParameters = new String[]{"task index"};
+ String[] delimiters = new String[]{}; // no delimiters
+ String[] parameterValues = ensureValidParts(parts, taskParameters, delimiters);
+ String indexString = parameterValues[0];
+ try {
+ return Integer.parseInt(indexString);
+ } catch (NumberFormatException e) {
+ throw new JarvisException("Please provide a valid task index.");
+ }
+ }
+
+ private Command parseDelete(String[] parts) throws JarvisException {
+ int deleteIndex = parseIndex(parts);
+ return new DeleteCommand(deleteIndex);
+ }
+
+ private Command parseMark(String[] parts) throws JarvisException {
+ int markIndex = parseIndex(parts);
+ return new MarkCommand(markIndex);
+ }
+
+ private Command parseUnmark(String[] parts) throws JarvisException {
+ int unmarkIndex = parseIndex(parts);
+ return new UnmarkCommand(unmarkIndex);
+ }
+}
diff --git a/src/main/java/jarvis/storage/Storage.java b/src/main/java/jarvis/storage/Storage.java
new file mode 100644
index 0000000000..bceb39792f
--- /dev/null
+++ b/src/main/java/jarvis/storage/Storage.java
@@ -0,0 +1,119 @@
+package jarvis.storage;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+import jarvis.task.Deadline;
+import jarvis.task.Event;
+import jarvis.task.Task;
+import jarvis.task.Todo;
+import jarvis.tasklist.TaskList;
+
+/**
+ * The Storage class is responsible for saving and loading the task list to and from the hard disk.
+ */
+public class Storage {
+ private static final DateTimeFormatter SAVE_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("MMM d yyyy HHmm");
+ private static final String TODO_IDENTIFIER = "T";
+ private static final String DEADLINE_IDENTIFIER = "D";
+ private static final String EVENT_IDENTIFIER = "E";
+ private String filePath;
+
+ /**
+ * Constructs a Storage object.
+ *
+ * @param filePath The path to the save file.
+ */
+ public Storage(String filePath) {
+ this.filePath = filePath;
+ }
+
+ /**
+ * Saves the task list to the save file.
+ *
+ * @param tasks The list of tasks to be saved.
+ */
+ public void saveTasks(TaskList tasks) {
+ try {
+ FileWriter writer = new FileWriter(this.filePath);
+
+ assert tasks != null : "Tasks list to be saved should not be null";
+
+ for (int i = 0; i < tasks.size(); i++) {
+ writer.write(tasks.get(i).toSaveString() + "\n");
+ }
+
+ writer.close();
+ } catch (IOException e) {
+ System.out.println("An error occurred while saving tasks.");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Loads the task list from the save file.
+ *
+ * @return The list of tasks loaded from the save file.
+ */
+ public ArrayList loadTasks() {
+ File file = new File(this.filePath);
+
+ assert file != null : "File path to load tasks from cannot be null";
+
+ if (!file.exists()) {
+ System.out.println("No save file detected. Attempting to create one...");
+ if (file.getParentFile() != null) {
+ file.getParentFile().mkdirs(); // This creates the directory structure if it doesn't exist
+ }
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ System.out.println("An error occurred while creating a new save file.");
+ e.printStackTrace();
+ }
+ System.out.println("Save file created successfully at " + this.filePath);
+ }
+
+ try {
+ List lines = Files.readAllLines(file.toPath());
+ return getTasks(lines);
+ } catch (IOException e) {
+ System.out.println("An error occurred while loading tasks.");
+ return null;
+ }
+ }
+
+ private LocalDateTime parseSavedDateTime(String dateTimeString) {
+ return LocalDateTime.parse(dateTimeString, SAVE_DATE_TIME_FORMATTER);
+ }
+
+ private ArrayList getTasks(List lines) {
+ ArrayList tasks = new ArrayList<>();
+
+ for (String line : lines) {
+ String[] parts = line.split(" \\| ");
+ boolean isMarked = parts[1].equals("1");
+
+ switch (parts[0]) {
+ case TODO_IDENTIFIER:
+ tasks.add(new Todo(parts[2], isMarked));
+ break;
+ case DEADLINE_IDENTIFIER:
+ tasks.add(new Deadline(parts[2], isMarked, parseSavedDateTime(parts[3])));
+ break;
+ case EVENT_IDENTIFIER:
+ tasks.add(new Event(parts[2], isMarked, parseSavedDateTime(parts[3]), parseSavedDateTime(parts[4])));
+ break;
+ default:
+ break;
+ }
+ }
+ return tasks;
+ }
+}
diff --git a/src/main/java/jarvis/task/Deadline.java b/src/main/java/jarvis/task/Deadline.java
new file mode 100644
index 0000000000..07b3fb4a9c
--- /dev/null
+++ b/src/main/java/jarvis/task/Deadline.java
@@ -0,0 +1,75 @@
+package jarvis.task;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Represents a deadline task.
+ *
+ * A Deadline is a task with an additional LocalDateTime attribute to indicate its deadline.
+ */
+public class Deadline extends Task {
+
+ protected LocalDateTime by;
+
+ /**
+ * Constructs a Deadline object with the specified description and deadline date-time.
+ *
+ * @param description The description of the deadline.
+ * @param by The deadline date-time of the task.
+ */
+ public Deadline(String description, LocalDateTime by) {
+ super(description, false);
+ this.by = by;
+ }
+
+ /**
+ * Constructs a Deadline object with the specified description, completion status, and deadline date-time.
+ *
+ * @param description The description of the deadline.
+ * @param isDone Indicates if the task has been completed.
+ * @param by The deadline date-time of the task.
+ */
+ public Deadline(String description, Boolean isDone, LocalDateTime by) {
+ super(description, isDone);
+ this.by = by;
+ }
+
+ /**
+ * Returns the deadline date-time.
+ *
+ * @return The deadline date-time of the task.
+ */
+ public LocalDateTime getBy() {
+ return this.by;
+ }
+
+ /**
+ * Returns the deadline date-time as a formatted string.
+ *
+ * @return A string representing the formatted deadline date-time.
+ */
+ public String getByString() {
+ return this.by.format(DateTimeFormatter.ofPattern("MMM d yyyy HHmm"));
+ }
+
+ /**
+ * Converts the Deadline object to a string format suitable for saving to a file.
+ *
+ * @return A string representation of the Deadline object for saving purposes.
+ */
+ @Override
+ public String toSaveString() {
+ return "D | " + super.toSaveString() + " | " + getByString();
+ }
+
+ /**
+ * Returns a string representation of the Deadline object.
+ *
+ * @return A string representation of the Deadline object.
+ */
+ @Override
+ public String toString() {
+ return "[D]" + super.toString() + " (by: " + getByString() + ")";
+ }
+}
diff --git a/src/main/java/jarvis/task/Event.java b/src/main/java/jarvis/task/Event.java
new file mode 100644
index 0000000000..04caaf0c68
--- /dev/null
+++ b/src/main/java/jarvis/task/Event.java
@@ -0,0 +1,98 @@
+package jarvis.task;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Represents an event task.
+ *
+ * An Event is a task with additional LocalDateTime attributes to indicate its start and end times.
+ */
+public class Event extends Task {
+
+ protected LocalDateTime from;
+ protected LocalDateTime to;
+
+ /**
+ * Constructs an Event object with the specified description, start time, and end time.
+ *
+ * @param description The description of the event.
+ * @param from The start time of the event.
+ * @param to The end time of the event.
+ */
+ public Event(String description, LocalDateTime from, LocalDateTime to) {
+ super(description, false);
+ this.from = from;
+ this.to = to;
+ }
+
+ /**
+ * Constructs an Event object with the specified description, completion status, start time, and end time.
+ *
+ * @param description The description of the event.
+ * @param isDone Indicates if the task has been completed.
+ * @param from The start time of the event.
+ * @param to The end time of the event.
+ */
+ public Event(String description, Boolean isDone, LocalDateTime from, LocalDateTime to) {
+ super(description, isDone);
+ this.from = from;
+ this.to = to;
+ }
+
+ /**
+ * Returns the start time of the event.
+ *
+ * @return The start time of the event.
+ */
+ public LocalDateTime getFrom() {
+ return this.from;
+ }
+
+ /**
+ * Returns the end time of the event.
+ *
+ * @return The end time of the event.
+ */
+ public LocalDateTime getTo() {
+ return this.to;
+ }
+
+ /**
+ * Returns the start time of the event as a formatted string.
+ *
+ * @return A string representing the formatted start time of the event.
+ */
+ public String getFromString() {
+ return this.from.format(DateTimeFormatter.ofPattern("MMM d yyyy HHmm"));
+ }
+
+ /**
+ * Returns the end time of the event as a formatted string.
+ *
+ * @return A string representing the formatted end time of the event.
+ */
+ public String getToString() {
+ return this.to.format(DateTimeFormatter.ofPattern("MMM d yyyy HHmm"));
+ }
+
+ /**
+ * Converts the Event object to a string format suitable for saving to a file.
+ *
+ * @return A string representation of the Event object for saving purposes.
+ */
+ @Override
+ public String toSaveString() {
+ return "E | " + super.toSaveString() + " | " + getFromString() + " | " + getToString();
+ }
+
+ /**
+ * Returns a string representation of the Event object.
+ *
+ * @return A string representation of the Event object.
+ */
+ @Override
+ public String toString() {
+ return "[E]" + super.toString() + " (from: " + getFromString() + " to: " + getToString() + ")";
+ }
+}
diff --git a/src/main/java/jarvis/task/Task.java b/src/main/java/jarvis/task/Task.java
new file mode 100644
index 0000000000..bd5bfeaa16
--- /dev/null
+++ b/src/main/java/jarvis/task/Task.java
@@ -0,0 +1,86 @@
+package jarvis.task;
+
+/**
+ * Represents a generic task.
+ *
+ * This class serves as a parent for more specific types of tasks such as
+ * {@code Todo}, {@code Deadline}, and {@code Event}.
+ */
+public class Task {
+
+ protected String description;
+ protected boolean isDone;
+
+ /**
+ * Constructs a Task object with the specified description and completion status.
+ *
+ * @param description The description or name of the task.
+ * @param isDone Indicates if the task has been completed.
+ */
+ public Task(String description, Boolean isDone) {
+ this.description = description;
+ this.isDone = isDone;
+ }
+
+ /**
+ * Returns the completion status of the task.
+ *
+ * @return true if the task is completed, false otherwise.
+ */
+ public Boolean isDone() {
+ return this.isDone;
+ }
+
+ /**
+ * Returns the description of the task.
+ *
+ * @return A string representing the description of the task.
+ */
+ public String getDescription() {
+ return this.description;
+ }
+
+ /**
+ * Returns the status icon of the task based on its completion status.
+ * A completed task is represented by "X", and an incomplete task is represented by a space.
+ *
+ * @return A string representing the status icon ("X" or " ").
+ */
+ public String getStatusIcon() {
+ return (isDone ? "X" : " "); // mark done task with X
+ }
+
+ /**
+ * Marks the task as done.
+ */
+ public void mark() {
+ this.isDone = true;
+ }
+
+ /**
+ * Marks the task as not done.
+ */
+ public void unmark() {
+ this.isDone = false;
+ }
+
+ /**
+ * Converts the Task object to a string format suitable for saving to a file.
+ *
+ * @return A string representation of the Task object for saving purposes.
+ */
+ public String toSaveString() {
+ return (isDone ? "1" : "0") + " | " + this.description;
+ }
+
+ /**
+ * Returns a string representation of the Task object.
+ * The representation includes the status icon and the description of the task.
+ *
+ * @return A string representation of the Task object.
+ */
+ @Override
+ public String toString() {
+ return "[" + this.getStatusIcon() + "] " + this.description;
+ }
+}
diff --git a/src/main/java/jarvis/task/Todo.java b/src/main/java/jarvis/task/Todo.java
new file mode 100644
index 0000000000..87400d8bc2
--- /dev/null
+++ b/src/main/java/jarvis/task/Todo.java
@@ -0,0 +1,52 @@
+package jarvis.task;
+
+/**
+ * Represents a Todo task.
+ *
+ * It is a type of task that only contains a description without any specific time or date associated with it.
+ */
+public class Todo extends Task {
+
+ /**
+ * Constructs a Todo object with the specified description.
+ * The completion status of the task is set to false (i.e., not done) by default.
+ *
+ * @param description The description or name of the todo task.
+ */
+ public Todo(String description) {
+ super(description, false);
+ }
+
+ /**
+ * Constructs a Todo object with the specified description and completion status.
+ *
+ * @param description The description or name of the todo task.
+ * @param isDone Indicates if the task has been completed.
+ */
+ public Todo(String description, Boolean isDone) {
+ super(description, isDone);
+ }
+
+ /**
+ * Converts the Todo object to a string format suitable for saving to a file.
+ * The format includes an identifier for the task type ("T") followed by the save string of the parent Task class.
+ *
+ * @return A string representation of the Todo object for saving purposes.
+ */
+ @Override
+ public String toSaveString() {
+ return "T | " + super.toSaveString();
+ }
+
+ /**
+ * Returns a string representation of the Todo object.
+ * The representation includes an identifier for the task type ("T")
+ * followed by the string representation of the parent Task class.
+ *
+ * @return A string representation of the Todo object.
+ */
+ @Override
+ public String toString() {
+ return "[T]" + super.toString();
+ }
+}
diff --git a/src/main/java/jarvis/tasklist/TaskList.java b/src/main/java/jarvis/tasklist/TaskList.java
new file mode 100644
index 0000000000..cc7651cc16
--- /dev/null
+++ b/src/main/java/jarvis/tasklist/TaskList.java
@@ -0,0 +1,140 @@
+package jarvis.tasklist;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+
+import jarvis.task.Deadline;
+import jarvis.task.Task;
+
+/**
+ * Represents a list of tasks, providing utility methods for managing tasks.
+ * This class provides a convenient way to handle tasks, such as adding, removing, and retrieving tasks.
+ */
+public class TaskList {
+
+ private ArrayList tasks;
+
+ /**
+ * Initializes a new empty task list.
+ */
+ public TaskList() {
+ this.tasks = new ArrayList<>();
+ }
+
+ /**
+ * Initializes a task list with a given list of tasks.
+ *
+ * @param loadedTasks An ArrayList of tasks to initialize the task list with.
+ */
+ public TaskList(ArrayList loadedTasks) {
+ this.tasks = loadedTasks;
+ }
+
+ /**
+ * Adds a task to the list.
+ *
+ * @param task The task to be added to the list.
+ */
+ public void add(Task task) {
+ assert task != null : "Task to be added should not be null";
+
+ tasks.add(task);
+ }
+
+ /**
+ * Removes a task from the list based on its index.
+ *
+ * @param index The index of the task to be removed.
+ * @return The task that was removed.
+ */
+ public Task remove(int index) {
+ assert isValidIndex(index) : "Invalid task index for retrieval";
+
+ return tasks.remove(index);
+ }
+
+ /**
+ * Retrieves a task from the list based on its index.
+ *
+ * @param index The index of the task to be retrieved.
+ * @return The task at the specified index.
+ */
+ public Task get(int index) {
+ assert isValidIndex(index) : "Invalid task index for retrieval";
+
+ return tasks.get(index);
+ }
+
+ /**
+ * Checks if the list contains the specified task.
+ *
+ * @param task The task to be checked.
+ * @return True if the task is in the list, false otherwise.
+ */
+ public boolean contains(Task task) {
+ assert task != null : "Task to be checked should not be null";
+
+ return tasks.contains(task);
+ }
+
+ /**
+ * Returns the number of tasks in the list.
+ *
+ * @return The size of the task list.
+ */
+ public int size() {
+ return tasks.size();
+ }
+
+ /**
+ * Provides access to the entire list of tasks.
+ *
+ * @return An ArrayList containing all tasks in the TaskList.
+ */
+ public ArrayList getTasks() {
+ return tasks;
+ }
+
+ /**
+ * Returns a list of tasks that contain the specified keyword in their description.
+ *
+ * @param keyword The keyword to search for.
+ * @return A list of tasks that match the keyword.
+ */
+ public ArrayList find(String keyword) {
+ ArrayList matchingTasks = new ArrayList<>();
+ for (Task task : tasks) {
+ if (task.getDescription().contains(keyword)) {
+ matchingTasks.add(task);
+ }
+ }
+ return matchingTasks;
+ }
+
+ /**
+ * Returns the nearest deadline that is not done yet
+ *
+ * @param now The time at which the command was given
+ * @return The nearest deadline that is not done yet
+ */
+ public Deadline getUpcomingDeadline(LocalDateTime now) {
+ Deadline upcomingDeadline = null;
+ LocalDateTime upcomingDeadlineDateTime = null;
+
+ for (Task task : tasks) {
+ if (!task.isDone() && task instanceof Deadline) {
+ Deadline deadline = (Deadline) task;
+ LocalDateTime deadlineDateTime = deadline.getBy();
+ if (upcomingDeadline == null || deadlineDateTime.isBefore(upcomingDeadlineDateTime)) {
+ upcomingDeadline = deadline;
+ upcomingDeadlineDateTime = deadlineDateTime;
+ }
+ }
+ }
+ return upcomingDeadline;
+ }
+
+ private boolean isValidIndex(int index) {
+ return index >= 0 && index < tasks.size();
+ }
+}
diff --git a/src/main/java/jarvis/ui/Ui.java b/src/main/java/jarvis/ui/Ui.java
new file mode 100644
index 0000000000..32cce13119
--- /dev/null
+++ b/src/main/java/jarvis/ui/Ui.java
@@ -0,0 +1,156 @@
+package jarvis.ui;
+
+import java.util.ArrayList;
+
+import jarvis.task.Deadline;
+import jarvis.task.Task;
+import jarvis.tasklist.TaskList;
+
+
+/**
+ * Represents the user interface for interacting with the user.
+ * The class is responsible for displaying messages to the user and reading user input.
+ */
+public class Ui {
+
+ /**
+ * Initializes the Ui class and prepares it to read user input.
+ */
+ public Ui() {
+ }
+
+ /**
+ * Displays the provided texts to the user surrounded by horizontal lines.
+ *
+ * @param text The messages to be displayed to the user.
+ */
+ public String display(String... text) {
+ assert text != null : "Text to be displayed should not be null";
+
+ String response = "";
+ for (String i : text) {
+ response += i + "\n";
+ }
+ return response;
+ }
+
+ /**
+ * Displays a greeting message to the user.
+ */
+ public String greet() {
+ return display("Hello Master! I'm Jarvis, your AI personal assistant.", "What can I do for you?");
+ }
+
+ /**
+ * Displays a farewell message to the user.
+ */
+ public String farewell() {
+ return display("Bye. Hope to see you again soon!");
+ }
+
+ /**
+ * Displays a message confirming that a task has been added.
+ *
+ * @param task The task that has been added.
+ * @param tasks The current task list.
+ */
+ public String displayAddedTask(Task task, TaskList tasks) {
+ assert task != null : "Task to be displayed should not be null";
+ assert tasks != null : "Task list should not be null";
+
+ return display("Got it. I've added this task:", task.toString(),
+ "Now you have " + tasks.size() + " tasks in the list.");
+ }
+
+ /**
+ * Displays a message confirming that a task has been deleted.
+ *
+ * @param task The task that has been deleted.
+ * @param tasks The current task list.
+ */
+ public String displayDeletedTask(Task task, TaskList tasks) {
+ assert task != null : "Task to be displayed should not be null";
+ assert tasks != null : "Task list should not be null";
+
+ return display("Got it. I've removed this task:", task.toString(),
+ "Now you have " + tasks.size() + " tasks in the list.");
+ }
+
+ /**
+ * Displays a message confirming that a task has been marked as done.
+ *
+ * @param task The task that has been marked.
+ */
+ public String displayMarkedTask(Task task) {
+ assert task != null : "Task to be displayed should not be null";
+
+ return display("Nice! I've marked this task as done:", task.toString());
+ }
+
+ /**
+ * Displays a message confirming that a task has been unmarked.
+ *
+ * @param task The task that has been unmarked.
+ */
+ public String displayUnmarkedTask(Task task) {
+ assert task != null : "Task to be displayed should not be null";
+
+ return display("OK, I've marked this task as not done yet:", task.toString());
+ }
+
+ /**
+ * Displays a message informing the user that their task list is currently empty.
+ */
+ public String displayEmptyList() {
+ return display("You currently have no tasks in your list.");
+ }
+
+ /**
+ * Displays all the tasks in the user's task list.
+ *
+ * @param tasks The user's current task list.
+ */
+ public String displayList(TaskList tasks) {
+ assert tasks != null : "Task list should not be null";
+
+ String response = "";
+ response += "Here are the tasks in your list:\n";
+ for (int i = 0; i < tasks.size(); i++) {
+ response += (i + 1) + "." + tasks.get(i) + "\n";
+ }
+ return response;
+ }
+
+ /**
+ * Displays tasks that match a search query.
+ *
+ * @param tasks The list of tasks that match the search query.
+ */
+ public String displayMatchingTasks(ArrayList tasks) {
+ assert tasks != null : "Task list should not be null";
+
+ if (tasks.isEmpty()) {
+ return display("No tasks matched your search query.");
+ } else {
+ String response = "";
+ response += "Here are the matching tasks in your list:\n";
+ for (int i = 0; i < tasks.size(); i++) {
+ response += (i + 1) + "." + tasks.get(i) + "\n";
+ }
+ return response;
+ }
+ }
+
+ /**
+ * Displays the nearest upcoming deadline that is not done yet.
+ *
+ * @param upcomingDeadline The nearest deadline that is not done yet.
+ */
+ public String displayUpcomingDeadline(Deadline upcomingDeadline) {
+ if (upcomingDeadline == null) {
+ return display("There are no upcoming deadlines that are not done yet.");
+ } else {
+ return display("The nearest deadline that needs to be done is", upcomingDeadline.toString());
+ }
+ }
+}
diff --git a/src/main/resources/images/Jarvis.jpg b/src/main/resources/images/Jarvis.jpg
new file mode 100644
index 0000000000..6d0203d27f
Binary files /dev/null and b/src/main/resources/images/Jarvis.jpg differ
diff --git a/src/main/resources/images/Users.jpg b/src/main/resources/images/Users.jpg
new file mode 100644
index 0000000000..08466ebf6d
Binary files /dev/null and b/src/main/resources/images/Users.jpg differ
diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml
new file mode 100644
index 0000000000..d3e6e9db86
--- /dev/null
+++ b/src/main/resources/view/DialogBox.fxml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
new file mode 100644
index 0000000000..a86a02b144
--- /dev/null
+++ b/src/main/resources/view/MainWindow.fxml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/jarvis/task/DeadlineTest.java b/src/test/java/jarvis/task/DeadlineTest.java
new file mode 100644
index 0000000000..e60c389977
--- /dev/null
+++ b/src/test/java/jarvis/task/DeadlineTest.java
@@ -0,0 +1,34 @@
+package jarvis.task;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.LocalDateTime;
+
+import org.junit.jupiter.api.Test;
+
+public class DeadlineTest {
+
+ @Test
+ public void testToString() {
+ Deadline deadline = new Deadline("return book", LocalDateTime.of(2023, 9, 20, 12, 0));
+ assertEquals("[D][ ] return book (by: Sep 20 2023 1200)", deadline.toString());
+ }
+
+ @Test
+ public void testToSaveString() {
+ Deadline deadline = new Deadline("return book", false, LocalDateTime.of(2023, 9, 20, 12, 0));
+ assertEquals("D | 0 | return book | Sep 20 2023 1200", deadline.toSaveString());
+ }
+
+ @Test
+ public void testToSaveStringCompleted() {
+ Deadline deadline = new Deadline("return book", true, LocalDateTime.of(2023, 9, 20, 12, 0));
+ assertEquals("D | 1 | return book | Sep 20 2023 1200", deadline.toSaveString());
+ }
+
+ @Test
+ public void testGetByString() {
+ Deadline deadline = new Deadline("return book", LocalDateTime.of(2023, 9, 20, 12, 0));
+ assertEquals("Sep 20 2023 1200", deadline.getByString());
+ }
+}
diff --git a/src/test/java/jarvis/task/EventTest.java b/src/test/java/jarvis/task/EventTest.java
new file mode 100644
index 0000000000..2fb2ab54c4
--- /dev/null
+++ b/src/test/java/jarvis/task/EventTest.java
@@ -0,0 +1,33 @@
+package jarvis.task;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.LocalDateTime;
+
+import org.junit.jupiter.api.Test;
+
+public class EventTest {
+
+ @Test
+ public void testToString() {
+ Event event = new Event("Meeting", LocalDateTime.of(2023, 9, 20, 10, 30),
+ LocalDateTime.of(2023, 9, 20, 11, 30));
+ assertEquals("Meeting", event.getDescription());
+ assertEquals("[E][ ] Meeting (from: Sep 20 2023 1030 to: Sep 20 2023 1130)", event.toString());
+ }
+
+ @Test
+ public void testToStringCompleted() {
+ Event event = new Event("Conference", true,
+ LocalDateTime.of(2023, 10, 1, 9, 0),
+ LocalDateTime.of(2023, 10, 1, 17, 0));
+ assertEquals("[E][X] Conference (from: Oct 1 2023 0900 to: Oct 1 2023 1700)", event.toString());
+ }
+
+ @Test
+ public void testToSaveString() {
+ Event event = new Event("Dinner", LocalDateTime.of(2023, 11, 25, 19, 0),
+ LocalDateTime.of(2023, 11, 25, 21, 0));
+ assertEquals("E | 0 | Dinner | Nov 25 2023 1900 | Nov 25 2023 2100", event.toSaveString());
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e7..e039355228 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,7 +1,108 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
+____________________________________________________________
+Hello Master! I'm Jarvis, your AI personal assistant.
+What can I do for you?
+____________________________________________________________
+____________________________________________________________
+OOPS!!! I'm sorry, but I don't know what that means :-(
+____________________________________________________________
+____________________________________________________________
+You currently have no tasks in your list.
+____________________________________________________________
+____________________________________________________________
+OOPS!!! The description of todo cannot be empty.
+____________________________________________________________
+____________________________________________________________
+Got it. I've added this task:
+[T][ ] borrow book
+Now you have 1 tasks in the list.
+____________________________________________________________
+____________________________________________________________
+OOPS!!! The description of deadline cannot be empty.
+____________________________________________________________
+____________________________________________________________
+OOPS!!! The deadline time of deadline cannot be empty.
+____________________________________________________________
+____________________________________________________________
+Got it. I've added this task:
+[D][ ] return book (by: Sep 1 2023 1800)
+Now you have 2 tasks in the list.
+____________________________________________________________
+____________________________________________________________
+OOPS!!! The description of event cannot be empty.
+____________________________________________________________
+____________________________________________________________
+OOPS!!! The start time of event cannot be empty.
+____________________________________________________________
+____________________________________________________________
+OOPS!!! The end time of event cannot be empty.
+____________________________________________________________
+____________________________________________________________
+Got it. I've added this task:
+[E][ ] project meeting (from: Sep 2 2022 1200 to: Sep 2 2022 1600)
+Now you have 3 tasks in the list.
+____________________________________________________________
+____________________________________________________________
+Here are the tasks in your list:
+1.[T][ ] borrow book
+2.[D][ ] return book (by: Sep 1 2023 1800)
+3.[E][ ] project meeting (from: Sep 2 2022 1200 to: Sep 2 2022 1600)
+____________________________________________________________
+____________________________________________________________
+OOPS!!! The task index of mark cannot be empty.
+____________________________________________________________
+____________________________________________________________
+Nice! I've marked this task as done:
+[T][X] borrow book
+____________________________________________________________
+____________________________________________________________
+Nice! I've marked this task as done:
+[E][X] project meeting (from: Sep 2 2022 1200 to: Sep 2 2022 1600)
+____________________________________________________________
+____________________________________________________________
+Here are the tasks in your list:
+1.[T][X] borrow book
+2.[D][ ] return book (by: Sep 1 2023 1800)
+3.[E][X] project meeting (from: Sep 2 2022 1200 to: Sep 2 2022 1600)
+____________________________________________________________
+____________________________________________________________
+Here are the matching tasks in your list:
+1.[T][X] borrow book
+2.[D][ ] return book (by: Sep 1 2023 1800)
+____________________________________________________________
+____________________________________________________________
+OOPS!!! The task index of unmark cannot be empty.
+____________________________________________________________
+____________________________________________________________
+OK, I've marked this task as not done yet:
+[T][ ] borrow book
+____________________________________________________________
+____________________________________________________________
+Here are the tasks in your list:
+1.[T][ ] borrow book
+2.[D][ ] return book (by: Sep 1 2023 1800)
+3.[E][X] project meeting (from: Sep 2 2022 1200 to: Sep 2 2022 1600)
+____________________________________________________________
+____________________________________________________________
+OOPS!!! No such task with index 0
+____________________________________________________________
+____________________________________________________________
+Got it. I've removed this task:
+[D][ ] return book (by: Sep 1 2023 1800)
+Now you have 2 tasks in the list.
+____________________________________________________________
+____________________________________________________________
+Got it. I've removed this task:
+[T][ ] borrow book
+Now you have 1 tasks in the list.
+____________________________________________________________
+____________________________________________________________
+Got it. I've removed this task:
+[E][X] project meeting (from: Sep 2 2022 1200 to: Sep 2 2022 1600)
+Now you have 0 tasks in the list.
+____________________________________________________________
+____________________________________________________________
+You currently have no tasks in your list.
+____________________________________________________________
+____________________________________________________________
+Bye. Hope to see you again soon!
+____________________________________________________________
\ No newline at end of file
diff --git a/text-ui-test/data/jarvis.txt b/text-ui-test/data/jarvis.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index e69de29bb2..9715103a2a 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -0,0 +1,27 @@
+hi
+list
+todo
+todo borrow book
+deadline
+deadline return book
+deadline return book /by 01/09/2023 1800
+event
+event Project meeting
+event project meeting /from 02/09/2022 1200
+event project meeting /from 02/09/2022 1200 /to 02/09/2022 1600
+list
+mark
+mark 1
+mark 3
+list
+find book
+unmark
+unmark 1
+list
+upcoming
+delete 0
+delete 2
+delete 1
+delete 1
+list
+bye
\ No newline at end of file
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 0873744649..500eb83595 100644
--- a/text-ui-test/runtest.bat
+++ b/text-ui-test/runtest.bat
@@ -7,7 +7,15 @@ REM delete output from previous run
if exist ACTUAL.TXT del ACTUAL.TXT
REM compile the code into the bin folder
-javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java
+javac -cp ..\src\main\java -Xlint:none -d ..\bin ^
+..\src\main\java\jarvis\*.java ^
+..\src\main\java\jarvis\ui\*.java ^
+..\src\main\java\jarvis\tasklist\*.java ^
+..\src\main\java\jarvis\task\*.java ^
+..\src\main\java\jarvis\storage\*.java ^
+..\src\main\java\jarvis\parser\*.java ^
+..\src\main\java\jarvis\exception\*.java ^
+..\src\main\java\jarvis\command\*.java
IF ERRORLEVEL 1 (
echo ********** BUILD FAILURE **********
exit /b 1
@@ -15,7 +23,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 jarvis.Jarvis < input.txt > ACTUAL.TXT
REM compare the output to the expected output
FC ACTUAL.TXT EXPECTED.TXT
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
old mode 100644
new mode 100755