diff --git a/README.md b/README.md
index e243ece764..68e9e84737 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Duke project template
+# FlashBang
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.
diff --git a/build.gradle b/build.gradle
index ea82051fab..b629f2d1c8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,11 +29,11 @@ test {
}
application {
- mainClass.set("seedu.duke.Duke")
+ mainClass.set("seedu.duke.Flashbang")
}
shadowJar {
- archiveBaseName.set("duke")
+ archiveBaseName.set("Flashbang")
archiveClassifier.set("")
}
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..36dc11f521 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,9 +1,10 @@
# About us
-Display | Name | Github Profile | Portfolio
---------|:----:|:--------------:|:---------:
-![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
+
+| Display | Name | Github Profile | Portfolio |
+|-----------------------------------------------------|:--------------------:|:---------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------------------:|
+| ![](https://via.placeholder.com/100.png?text=Photo) | Mikolaj Jedrzejewski | [Github](https://github.com/mikolajed) | [Portfolio](https://ay2425s1-cs2113-t11-2.github.io/tp/team/mikolajed.html) |
+| ![](https://via.placeholder.com/100.png?text=Photo) | Ranee Ng | [Github](https://github.com/raneeng) | [Portfolio](https://ay2425s1-cs2113-t11-2.github.io/tp/team/raneeng.html) |
+| ![](https://via.placeholder.com/100.png?text=Photo) | Frederick | [Github](https://github.com/frederickemerson) | [Portfolio](https://ay2425s1-cs2113-t11-2.github.io/tp/team/frederick.html) |
+| ![](https://via.placeholder.com/100.png?text=Photo) | Nguyen Hoang Minh Ngoc (Angelina) | [Github](https://github.com/angelinawong1210) | [Portfolio](https://ay2425s1-cs2113-t11-2.github.io/tp/team/angelinawong1210.html) |
+| ![](https://via.placeholder.com/100.png?text=Photo) | Ethan Soh | [Github](https://github.com/Paulifyer) | [Portfolio](https://ay2425s1-cs2113-t11-2.github.io/tp/team/ethansoh.html) |
diff --git a/docs/Developer Guide _ tp.pdf b/docs/Developer Guide _ tp.pdf
new file mode 100644
index 0000000000..3eb9cc8495
Binary files /dev/null and b/docs/Developer Guide _ tp.pdf differ
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..1838fe2256 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -3,36 +3,150 @@
## Acknowledgements
{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+Formatting of Developer's Guide was done with reference to [AddressBook-Level3 developer guide](https://se-education.org/addressbook-level3/DeveloperGuide.html#common-classes).
## Design & implementation
{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+### Parser component
+API: `Parser.java`
+
+Parser's role is to given user input create a command which then can be executed. This particular implementation follows
+**Factory design pattern**. It exposes a general purpose method for parsing command `parseCommand(String input)` and then it determines
+command types and creates one of the type. Regular expressions are heavily used for extracting information from input.
+More details are presented on a sequence diagram someName.
+
+![Diagram Description](./diagrams/ParsingSequenceDiagram.png)
+
+#### Alternative approaches/Possible improvements:
+- Command factory could be moved to a separate class
+- Creating a lexer object might be a desirable approach if the commands where much more complex
+
+#### Structure
+Below is a partial class diagram showing the interactions of the `Parser` class.
+![Parser class diagram](./diagrams/ParserPartialClassDiagram.png)
+
+The sequence diagram below illustrates the interactions taking `parseCommand(“delete --m cs2113 --i 1”)` as an example.
+![Sample delete call sequence diagram](./diagrams/ParserDeleteSequenceDiagram.png)
+
+#### Example
+How the `Parser` component works:
+The `Parser` receives the command input.
+It identifies the command type using `parseCommandType`.
+Depending on the command type, it creates the corresponding command object (e.g., `AddCommand`).
+The created command is executed, producing a `CommandResult`.
+The `CommandResult` is then used by `Ui` to provide feedback to the user.
+
+### Ui component
+API: `Ui.java`
+
+Below is a partical class diagram showing the interactions of the `Ui` class.
+![Ui class diagram](./diagrams/UIClassDiagram.png)
+
+The sequence diagram below illustrates the interactions between the user and this class when the program is executed.
+![Ui interactions sequence diagram](./diagrams/UISequenceDiagram.png)
+
+How the `Ui` component works:
+The `Ui` serves as a centralized utility that handles all outputs.
+When a user execute the app, this class displays the welcome message and all available commands. After that, based on users' inputs, it handles the output that is processed by other classes.
+
+### Storage component
+API: `Storage.java`
+
+Below is a class diagram showing the interactions of the `Storage` class.
+![Storage class diagram](./diagrams/StorageClassDiagram.png)
+
+The sequence diagram below illustrates the interactions taking `writeFlashBookToFile()` and `readFlashCardsFromFile()`.
+![Sample delete call sequence diagram](./diagrams/StorageSequenceDiagram.png)
+
+How the `Storage` component works:
+ The `Storage` component is initialized with a directory path where flashcard data will be stored.
+ To save data:
+ `writeFlashBookToFile()` is called, iterating through each `FlashCardSet` in `FlashBook`.
+ For each `FlashCardSet`, a corresponding file is created in the directory, and each Card in the set is written to this file.
+ To load data:
+ `readFlashCardsFromFile()` checks the directory for flashcard files.
+ For each file found, `readFlashCardSetFromFile()` is called to read the cards and create a `FlashCardSet`.
+ The `FlashCardSet` is then added back to the `FlashBook`, reconstructing the flashcard library in memory.
## Product scope
### Target user profile
-{Describe the target user profile}
+- Has a need to create flashcards for their studies
+- Needs to be able to test themselves on flashcard content
+- Needs to be able to track how well they understand the modules they take
+- Can type fast
+- Prefers typing to mouse interactions
+- Is used to using CLI applications
### Value proposition
-{Describe the value proposition: what problem does it solve?}
+Give university students a simple and effective flashcard application which allows them to create flashcards for the
+many modules and topics they have for schools. Flashcards are used to test the student's knowledge and also organise the
+content of the module in a simple and clear way.
## User Stories
-|Version| As a ... | I want to ... | So that I can ...|
-|--------|----------|---------------|------------------|
-|v1.0|new user|see usage instructions|refer to them when I forget how to use the application|
-|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list|
-
+| Version | As a ... | I want to ... | So that I can ... |
+|---------|----------|-----------------------------------------------------------|---------------------------------------------------------------------------------------|
+| v1.0 | new user | see usage instructions | refer to them when I forget how to use the application |
+| v1.0 | student | view existing flashcards | I can test my knowledge to study efficiently |
+| v1.0 | student | view existing flashcards | I can review and learn material |
+| v1.0 | crammer | delete flashcards which im confident at | I can focus on my areas of weakness |
+| v1.0 | student | review flashcards that I have answered incorrectly | I can identify my knowledge gaps |
+| v2.0 | student | have a timer within the app | I am able to time myself taking the quizzes within the app itself for better learning |
+| v2.0 | student | view all incorrect flashcards in previous quizzes | I can focus more on my weak areas |
+| v2.0 | user | search for flashcards based on keyword and module | to test myself on specific topics |
+| v2.0 | student | keep track of how many right and wrong answers in quizzes | so I can focus on how well versed I am in a topic |
+| v2.0 | student | filter flashcards by difficulty | I can choose which ones to focus on based on my current level of understanding |
## Non-Functional Requirements
-{Give non-functional requirements}
+* Should work on any mainstream OS as long as it has Java `17` or above installed
+* Should be able to handle any number of modules as long as there are no repeated module names
+* A user with strong typing ability should be able to use the application faster than with a mouse
## Glossary
-* *glossary item* - Definition
+* **Flashcard** - a card containing a small amount of information as an aid to learning.
+Contains a question and an answer related to a topic.
## Instructions for manual testing
-{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing}
+### Launch and shutdown
+#### Initial launch
+1. Download the jar file and copy into an empty folder
+2. Open the jar file using your command line with the command:
+`java -jar {Path of File}`
+#### Shutdown
+1. Type `quit` to exit the application
+2. Quitting the application also saves all changes made by the user during runtime
+Expected: Application exits and the text files in `./data` are updated accordingly
+#### Adding Flashcards
+1. Adding flashcards into the flash book
+ 1. Test case: `add --m CS2113 --q What is OOP? --a Object-Oriented Programming`
+ Expected: A Card with question "What is OOP" and answer "Object-Oriented Programming" in the module "CS2113"
+ 2. Test case: `add`
+ Expected: An error is thrown and caught printing out "uh oh bad command"
+ 3. Other incorrect add commands to try: `add --q`, `add --a` (with missing fields or empty fields)
+ Expected: Similar to previous
+#### Deleting Flashcards
+##### Prerequisites: There are a several flashcards in the flash book listed out using the view command
+1. Deleting flashcards from the flash book
+ 1. Test case: `delete --m CS2113 --i 1`
+ Expected: the first flashcard in the CS2113 flash card set is deleted. Details of the deleted card should be shown
+ 2. Test case: `delete`
+ Expected: An error is thrown and caught printing out "uh oh bad command"
+ 3. Other incorrect variations to try: `delete --m ModuleNotInList --i 0`, `delete --m`
+ (with missing fields or modules not in the flash book)
+ Expected: Similar to previous
+#### Flashbang
+##### Prerequisites: There are a several flashcards and flashcard sets in the flash book
+1. Quizzing users on the flashcards in a module
+ 1. Test case: `flashbang --m CS2113`
+ Expected: Each question within the module is displayed sequentially
+ where users are prompted to reveal the answer to the question with `y` or `n`
+ 2. Incorrect variation to try: `flashbang`, `flashbang --m`
+ Expected: An error is thrown and caught printing out "uh oh bad command"
+ 3. Other incorrect variations to try: `flashbang --m ModuleNotInList`
+ Expected: An error is thrown and caught printing out "uh oh bad command"
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..8af26b5d69 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,8 +1,11 @@
-# Duke
+# FlashBang
-{Give product intro here}
+FlashBang is a CLI app designed to provide students with a smart way of studying for their modules. The app will manage a limited number of flashcards for a small number of modules, optimized for users who prefer a CLI.
Useful links:
* [User Guide](UserGuide.md)
* [Developer Guide](DeveloperGuide.md)
* [About Us](AboutUs.md)
+
+PPP
+[Frederick](./team/frederickemerson.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index d6cf4c3b3a..655326e408 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -2,41 +2,232 @@
## Introduction
-{Give a product intro}
+**FlashBang** is a CLI app designed to provide students with a smart way of studying for their modules. The app will manage a limited number of flashcards for a small number of modules, optimized for users who prefer a CLI.
-## Quick Start
+## Target User Profile
-{Give steps to get started quickly}
+NUS students who want to review their modules using flashcards. The user:
-1. Ensure that you have Java 17 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+- Has a need to create flashcards for their studies
+- Needs to be able to test themselves on flashcard content
+- Needs to be able to track how well they understand the modules they take
+- Can type fast
+- Prefers typing to mouse interactions
+- Is used to using CLI applications
-## Features
+## Value Propositions
-{Give detailed description of each feature}
+The app will provide NUS students with a smart way of studying for their modules. The app will manage a limited number of flashcards for a small number of modules, optimized for users who prefer a CLI.
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+## Quick Start
+
+1. Ensure that you have Java 17 or above installed.
-Format: `todo n/TODO_NAME d/DEADLINE`
+2. Down the latest version of `FlashBang` from [here](https://github.com/AY2425S1-CS2113-T11-2/tp/releases).
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+3. Copy the jar file into an empty folder.
-Example of usage:
+4. Open a command window in that folder.
-`todo n/Write the rest of the User Guide d/next week`
+5. Run the command java -jar {filename}.jar e.g., java -jar Duke.jar (i.e., run the command in the same folder as the jar file).
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+## Features
-## FAQ
+The app allows for creating and managing flashcards each of which contains
+a question and an answer. Flashcards are organized into modules. Following
+is a list of command which are supported with examples.
-**Q**: How do I transfer my data to another computer?
+### Adding flashcards: `add`
-**A**: {your answer here}
+Add a flashcard to a flashcard set.
+Topics are optional fields that are used to enhance organisation
+**Note* It is not allowed to have the delimiter " | " in the questions and answers.
-## Command Summary
+```bash
+add --m [MODULE NAME] --q [QUESTION] --a [ANSWER]
+```
+or
+```bash
+add --m [MODULE NAME] --t [TOPIC NAME] --q [QUESTION] --a [ANSWER]
+```
+
+**Examples:**
+```bash
+add --m CS2113 --q "What is OOP?" --a "Object-Oriented Programming"
+add --m CS1010 --q "What is a variable?" --a "A storage location in memory with a name"
+add --m MA1521 --q "What is the derivative of sin(x)?" --a "cos(x)"
+add --m CS2113 --t OOP --q "What is an Object?" --a "An entity with state and behaviour"
+```
+
+### Deleting flashcards: `delete`, `deleteall`
+
+To delete one flashcard:
+
+```bash
+delete --m [MODULE NAME] --i [INDEX]
+```
+
+To delete all flashcards in a set:
+
+```bash
+deleteall --m [MODULE NAME]
+```
+
+**Examples:**
+```bash
+delete --m CS2113 --i 2 # Deletes second flashcard in the module CS2113
+delete --m MA1521 --i 5 # Deletes fifth flashcard in the module MA1521
+deleteall --m CS1010 # Deletes all flashcards in the module CS1010
+```
+
+### Viewing all flashcards: `view`
+Lists all flashcards for every module.
+```bash
+view --all
+```
+
+**Example:**
+```bash
+view --all
+```
+
+### Viewing all flashcards in a module without the answers: `flashbang`
+
+```bash
+flashbang --m [MODULE NAME]
+```
+
+**Example:**
+```bash
+flashbang --m CS1010
+```
+```
+Question: "What is a variable?"
+Reveal answer? (Q to quit) (Y/N)
+Y
+Answer: "A storage location in memory with a name."
+Next question: "What is a constant?"
+Reveal answer? (Q to quit) (Y/N)
+Q
+Bye!!
+```
+
+### Filter flashcards by module: `view`
+```bash
+view --m [MODULE NAME]
+```
+
+**Example:**
+```bash
+view --m CS2113
+```
+
+### Edit flashcard: `edit`
+
+Edits an existing flashcard.
+
+```bash
+edit --m [MODULE NAME] --i [INDEX] --q [NEW QUESTION] --a [NEW ANSWER]
+```
+
+Or
+
+```bash
+edit --m [MODULE NAME] --i [INDEX] # Prompts for inputs
+```
+
+**App Prompts:**
+```
+App: New Question: [NEW QUESTION]
+App: New Answer: [NEW ANSWER]
+```
+
+**Examples:**
+```bash
+edit --m CS1010 --i 2 --q "What is a constant?" --a "A value that cannot be changed once initialized."
+edit --m MA1521 --i 3 --q "What is the integral of 1/x?" --a "ln|x| + C"
+
+__________________________________________________
+> edit --m CS2113 --i 1
+Old Question : Question 2
+Do you want to change Question (y/n):
+> y
+Enter new Question :
+> What does OOP stand for?
+Old Answer : Answer for question 2
+Do you want to change Answer (y/n):
+> y
+Enter new Answer :
+> Object-oriented programming
+__________________________________________________
+Successfully edited flashcard
+
+```
+
+### Search for flashcards in a module by topic or by a search term
+Searches for existing flashcards that contain the search term or have topics that contain the search term.
+
+To search by topic, add the `/t` flag after the module name.
+
+When searching, the search term is case-sensitive.
+```bash
+search --m [MODULE NAME] /t --s [SEARCH TERM]
+```
+*or*
+```bash
+search --m [MODULE NAME] --s [SEARCH TERM]
+```
+**Examples:**
+```bash
+search --m CS2113 --s state
+ ____________________________________________________________
+ 1. WHAT is an Object:
+ An entity with a state and a behaviour
+ topic: OOP
+
+ ____________________________________________________________
+search --m CS2113 /t --s OOP
+ ____________________________________________________________
+ 1. What is OOP:
+ Object-Oriented Programing
+ topic: OOP
+ 2. WHAT is an Object:
+ An entity with a state and a behaviour
+ topic: OOP
+
+ ____________________________________________________________
+
+```
+### Quitting the app: `quit`
+Quits the app session.
+```bash
+quit
+```
+
+**Example:**
+```bash
+quit
+```
+
+
+## Command summary
+
+| Command | Description |
+| --- |------------------------------------------------------------------------------|
+| Add flashcards | ```add --m [Module Name] {--t [Topic] (optional)} --q [Question] --a [Answer]``` |
+| Delete one flashcard | ```delete --m [MODULE NAME] --i [INDEX]``` |
+| Delete all flashcards in a set | ```deleteall --m [MODULE NAME]``` |
+| View all flashcards in every module | ```view --all``` |
+| View all flashcards in a module without the answers | ```flashbang --m [MODULE NAME``` |
+| Filter flashcards by module | ```view --m [MODULE NAME]``` |
+| Edit flashcard | ```edit --m [MODULE NAME] --i [INDEX] --q [NEW QUESTION] --a [NEW ANSWER]``` |
+| Quit the app | ```quit``` |
+
+Note that specifying multiple command keywords in the input will be understood as command of the first type.
+
+## FAQs
+Q: Can I add two flashcards same question but different answer.
+
+A: Yes. Adding the two flashcards with both the same answer and question will work.
-{Give a 'cheat sheet' of commands here}
-* Add todo `todo n/TODO_NAME d/DEADLINE`
diff --git a/docs/UserGuide.pdf b/docs/UserGuide.pdf
new file mode 100644
index 0000000000..3ecab0c06d
Binary files /dev/null and b/docs/UserGuide.pdf differ
diff --git a/docs/diagrams/ParserDeleteSequenceDiagram.png b/docs/diagrams/ParserDeleteSequenceDiagram.png
new file mode 100644
index 0000000000..e825d72cf1
Binary files /dev/null and b/docs/diagrams/ParserDeleteSequenceDiagram.png differ
diff --git a/docs/diagrams/ParserDeleteSequenceDiagram.puml b/docs/diagrams/ParserDeleteSequenceDiagram.puml
new file mode 100644
index 0000000000..948284d106
--- /dev/null
+++ b/docs/diagrams/ParserDeleteSequenceDiagram.puml
@@ -0,0 +1,60 @@
+@startuml
+
+participant ":Flashbang"
+participant ":Parser"
+participant "flashBook:FlashBook"
+participant "command:DeleteCommand"
+participant "cs2113:FlashcardSet"
+participant "result:CommandResult"
+
+":Flashbang" -> ":Parser" : parseCommand("delete --m cs2113 --i 1")
+activate ":Parser"
+
+":Parser" -> ":Parser" : parseCommandType("delete --m cs2113 --i 1")
+activate ":Parser"
+deactivate ":Parser"
+
+":Parser" -> ":Parser" : createDeleteCommand("delete --m cs2113 --i 1")
+activate ":Parser"
+
+":Parser" -> "flashBook:FlashBook" : getInstance()
+activate "flashBook:FlashBook"
+return flashBook
+
+":Parser" -> "flashBook:FlashBook" : getFlashcardSet("cs2113")
+activate "flashBook:FlashBook"
+return cs2113
+deactivate "flashBook:FlashBook"
+
+create "command:DeleteCommand"
+":Parser" -> "command:DeleteCommand" : DeleteCommand(cs2113, 1)
+activate "command:DeleteCommand"
+return command
+
+":Parser" --> ":Parser" : command
+deactivate ":Parser"
+":Parser" --> ":Flashbang" : command
+deactivate ":Parser"
+
+":Flashbang" -> "command:DeleteCommand" : execute()
+activate "command:DeleteCommand"
+
+"command:DeleteCommand" -> "cs2113:FlashcardSet" : removeCard(targetCard)
+activate "cs2113:FlashcardSet"
+"cs2113:FlashcardSet" -> "cs2113:FlashcardSet" : remove(targetCard)
+activate "cs2113:FlashcardSet"
+deactivate "cs2113:FlashcardSet"
+deactivate "cs2113:FlashcardSet"
+
+create "result:CommandResult"
+"command:DeleteCommand" -> "result:CommandResult" : CommandResult("success message")
+activate "result:CommandResult"
+return result
+
+destroy "result:CommandResult"
+
+return result
+
+destroy "command:DeleteCommand"
+
+@enduml
diff --git a/docs/diagrams/ParserPartialClassDiagram.png b/docs/diagrams/ParserPartialClassDiagram.png
new file mode 100644
index 0000000000..0d170ec658
Binary files /dev/null and b/docs/diagrams/ParserPartialClassDiagram.png differ
diff --git a/docs/diagrams/ParserPartialClassDiagram.puml b/docs/diagrams/ParserPartialClassDiagram.puml
new file mode 100644
index 0000000000..3d9b7f3195
--- /dev/null
+++ b/docs/diagrams/ParserPartialClassDiagram.puml
@@ -0,0 +1,20 @@
+@startuml
+
+class Flashbang
+class CommandResult
+class Parser
+abstract class "Command"
+class XYZCommand
+
+Flashbang --> Parser
+
+Parser --> "Command" : calls
+Parser --> XYZCommand : creates
+
+"Command" <|-- XYZCommand
+
+"Command" --> CommandResult : creates
+CommandResult --> Flashbang
+
+
+@enduml
diff --git a/docs/diagrams/ParsingSequenceDiagram.png b/docs/diagrams/ParsingSequenceDiagram.png
new file mode 100644
index 0000000000..9a0bbd148f
Binary files /dev/null and b/docs/diagrams/ParsingSequenceDiagram.png differ
diff --git a/docs/diagrams/ParsingSequenceDiagram.puml b/docs/diagrams/ParsingSequenceDiagram.puml
new file mode 100644
index 0000000000..7ba3082424
--- /dev/null
+++ b/docs/diagrams/ParsingSequenceDiagram.puml
@@ -0,0 +1,42 @@
+@startuml
+!include Style.puml
+
+actor User as user
+participant Parser
+participant FlashBook
+participant FlashCardSet
+participant Command as command
+
+user -> Parser : parseCommand(input)
+activate Parser
+
+Parser -> Parser : parseCommandType(input)
+activate Parser
+ Parser <-- Parser : commandType
+deactivate Parser
+
+alt commandType is valid
+ Parser <-- Parser : extractFields()
+ Parser -> Parser : create a command
+ activate Parser
+ Parser -> FlashBook : getModule()
+ activate FlashBook
+
+ FlashBook -> FlashCardSet : getFlashCardSet()
+ activate FlashCardSet
+ FlashCardSet --> Parser : FlashCardSet
+ deactivate FlashCardSet
+
+ deactivate FlashBook
+ Parser --> command : ValidCommand
+ deactivate Parser
+else commandType is invalid
+ Parser -> Parser : create a command
+ activate Parser
+ Parser --> command : InvalidCommand
+ deactivate Parser
+end
+
+user <-- command : Command instance
+deactivate Parser
+@enduml
diff --git a/docs/diagrams/StorageClassDiagram.png b/docs/diagrams/StorageClassDiagram.png
new file mode 100644
index 0000000000..215db6fe63
Binary files /dev/null and b/docs/diagrams/StorageClassDiagram.png differ
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
new file mode 100644
index 0000000000..1010615686
--- /dev/null
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -0,0 +1,56 @@
+@startuml
+
+class Storage {
+ + Storage(String)
+ + writeFlashBookToFile(FlashBook)
+ + readFlashCardsFromFile(): HashMap
+ - createDir(): void
+ - cardFormatter(String): Card
+ - readFlashCardSetFromFile(String, File): FlashCardSet
+ - createFile(File): void
+}
+
+class FlashBook {
+ - instance: FlashBook
+ - allFlashCardSets: HashMap
+ + getInstance(): FlashBook
+ + setInstance(flashCards: HashMap): void
+ + getAllFlashCardSets(): HashMap
+ + addFlashCardSet(module: String): void
+ + getFlashCardSet(module: String): FlashCardSet
+}
+
+class FlashCardSet {
+ - flashCardSet: ArrayList
+ - moduleName: String
+ + FlashCardSet(module: String)
+ + FlashCardSet(module: String, flashCardSet: ArrayList)
+ + getModuleName(): String
+ + getFlashCardSet(): ArrayList
+ + getCard(cardIndex: int): Card
+ + addCard(toAdd: Card): void
+ + removeCard(toRemove: Card): void
+ + viewFlashCards(module: String): void
+ + performFlashBang(): void
+ + iterator(): Iterator
+}
+
+class Card {
+ - question: String
+ - answer: String
+ - topic: String
+ + Card(question : String, answer : String)
+ + Card(question : String, answer : String, topic : String)
+ + getQuestion() : String
+ + getTopic() : String
+ + setQuestion(question : String) : void
+ + getAnswer() : String
+ + setAnswer(answer : String) : void
+ + toWritableString() : String
+ + toString() : String
+}
+
+Storage --> FlashBook : "uses"
+FlashBook --> "1" FlashCardSet
+FlashCardSet --> "*" Card
+@enduml
diff --git a/docs/diagrams/StorageSequenceDiagram.png b/docs/diagrams/StorageSequenceDiagram.png
new file mode 100644
index 0000000000..a24f768210
Binary files /dev/null and b/docs/diagrams/StorageSequenceDiagram.png differ
diff --git a/docs/diagrams/StorageSequenceDiagram.puml b/docs/diagrams/StorageSequenceDiagram.puml
new file mode 100644
index 0000000000..a84c8cc683
--- /dev/null
+++ b/docs/diagrams/StorageSequenceDiagram.puml
@@ -0,0 +1,87 @@
+@startuml
+actor Client
+participant Storage
+participant File
+participant FlashCardSet
+participant Card
+participant FlashBook
+participant FileWriter
+
+== Initialization and Directory Creation ==
+Client -> Storage: Storage(directoryPath)
+activate Storage
+Storage -> File: File(directoryPath)
+activate File
+File --> Storage: File instance
+deactivate File
+
+Storage -> Storage: createDir()
+alt Directory doesn't exist
+ Storage -> File: mkdirs()
+ File --> Storage: Success/Failure
+else Directory exists
+ Storage -> Storage: Directory ready
+end
+deactivate Storage
+
+== Writing FlashBook to File ==
+Client -> Storage: writeFlashBookToFile(flashBook)
+activate Storage
+Storage -> FlashBook: getAllFlashCardSets()
+FlashBook --> Storage: HashMap
+
+loop HashMap
+ Storage -> File: File(directory, module+".txt")
+ activate File
+ File --> Storage: File instance
+ deactivate File
+
+ Storage -> Storage: createFile(File)
+ Storage -> FileWriter: FileWriter(flashCardSetFile)
+ activate FileWriter
+ FileWriter --> Storage: FileWriter instance
+ deactivate FileWriter
+
+ loop FlashCardSet
+ Storage -> Card: toWritableString()
+ activate Card
+ Card --> Storage: formattedString
+ deactivate Card
+ Storage -> FileWriter: write(formattedString)
+ end
+
+ FileWriter -> FileWriter: close()
+end
+Storage --> Client: Write Complete
+deactivate Storage
+
+== Reading FlashCards from File ==
+Client -> Storage: readFlashCardsFromFile()
+activate Storage
+
+Storage -> File: directory.listFiles(".txt files")
+alt Files found
+ loop for each File
+ Storage -> FlashCardSet: FlashCardSet(module)
+ activate FlashCardSet
+ FlashCardSet --> Storage: FlashCardSet instance
+
+ loop File
+ File -> Storage: line (card data)
+ Storage -> Storage: cardFormatter(line)
+ activate Card
+ Card --> Storage: Card instance
+ deactivate Card
+
+ Storage -> FlashCardSet: addCard(Card)
+ end
+ Storage -> File: close()
+ deactivate FlashCardSet
+ end
+ Storage --> Client: HashMap
+else No files found
+ Storage --> Client: Empty HashMap
+end
+deactivate Storage
+
+@enduml
diff --git a/docs/diagrams/Style.puml b/docs/diagrams/Style.puml
new file mode 100644
index 0000000000..3ce60ae4bf
--- /dev/null
+++ b/docs/diagrams/Style.puml
@@ -0,0 +1,5 @@
+!define LOGIC_COLOR #3333C4
+!define LOGIC_COLOR_T1 #7777DB
+!define LOGIC_COLOR_T2 #5252CE
+!define LOGIC_COLOR_T3 #1616B0
+!define LOGIC_COLOR_T4 #101086
\ No newline at end of file
diff --git a/docs/diagrams/UIClassDiagram.png b/docs/diagrams/UIClassDiagram.png
new file mode 100644
index 0000000000..8c71f43011
Binary files /dev/null and b/docs/diagrams/UIClassDiagram.png differ
diff --git a/docs/diagrams/UISequenceDiagram.png b/docs/diagrams/UISequenceDiagram.png
new file mode 100644
index 0000000000..339283da02
Binary files /dev/null and b/docs/diagrams/UISequenceDiagram.png differ
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
new file mode 100644
index 0000000000..61650b678d
--- /dev/null
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -0,0 +1,16 @@
+@startuml
+skinparam classAttributeIconSize 0
+
+title UI - Class Diagram
+class Ui {
+ - scanner: Scanner
+ + Ui()
+ + {static} displayCommands(): void
+ + {static} welcomeMessage(): void
+ + {static} printResponse(text: String): void
+ + {static} getRequest(): String
+ + {static} displayGetNewPromptFromUser(prompt: String): void
+ + {static} displayConfirmationQuestion(prompt: String): void
+ + {static} displayOldStoredValue(prompt: String, value: String): void
+}
+@enduml
diff --git a/docs/diagrams/UiSequenceDiagram.puml b/docs/diagrams/UiSequenceDiagram.puml
new file mode 100644
index 0000000000..4a129689eb
--- /dev/null
+++ b/docs/diagrams/UiSequenceDiagram.puml
@@ -0,0 +1,41 @@
+@startuml
+!include docs/diagrams/Style.puml
+
+actor User
+
+User -> Ui : welcomeMessage()
+activate Ui
+Ui -> Ui : displayCommands()
+deactivate Ui
+
+User -> Ui: printResponse()
+activate Ui
+Ui -> Ui : displayCommands()
+deactivate Ui
+
+User -> Ui : getRequest()
+activate Ui
+Ui -> Ui : return scanner.nextLine();
+deactivate Ui
+
+User -> Ui : displayGetNewPromptFromUser("request")
+activate Ui
+Ui -> User : "Enter new request:"
+deactivate Ui
+
+User -> Ui: displayConfirmationQuestion("prompt")
+activate Ui
+Ui -> User : "Do you want to change: "
+deactivate Ui
+
+User -> Ui: displayOldStoredValue("prompt", "value")
+activate Ui
+Ui -> User : "Old: "
+deactivate Ui
+
+
+
+
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/team/angelinawong1210.md b/docs/team/angelinawong1210.md
new file mode 100644
index 0000000000..8f9b30dbc3
--- /dev/null
+++ b/docs/team/angelinawong1210.md
@@ -0,0 +1,37 @@
+# Nguyen Hoang Minh Ngoc - Project Portfolio Page
+
+## Overview
+
+**FlashBang** is a CLI app designed to provide students with a smart way of studying for their modules. The app will manage a limited number of flashcards for a small number of modules, optimized for users who prefer a CLI.
+
+To me, this project is a precious opportunity to build a comprehensive CLI app in a team, covering different tasks such as programming, documentation and testing. I also learned how to use the Git for better code collaboration, as well as practice essential coding conventions.
+
+## Summary of Contributions
+
+### Code Contributed
+Here is the link to my [RepoSense report](https://nus-cs2113-ay2425s1.github.io/tp-dashboard/?search=angelinawong1210&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-09-20&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other).
+
+### Enhancements implemented
+1. Implemeted the function of viewing all flashcards - **ViewCommand**, including creating the code and JUnit test cases. This function plays an essential role as it displays all the flashcards within a module.
+
+2. Updated the **Ui** class with welcome message and list of available commands of the app.
+
+3. Error handling enhancement for the **AddCommand** to capture invalid module names, questions and answers.
+- For the module names, the app restricts users from creating flashcard set with empty module name.
+- For the questions and answers, the app restricts users from including delimiters "|" in any questions or answers to avoid confusion in the storage process.
+
+### Contributions to the UG
+- Updated the User Guide with **Target User Profile** and **Value Proposition** to provide an overview about the app for users.
+- Added the Command Summary for user's quick reference.
+- Updated proper examples for the **Edit** function.
+
+### Contributions to the DG:
+- Created the class diagram and sequence diagram for the Ui class.
+- Based on the created diagrams, wrote the interpretations.
+- Fix minor cosmetics issue to enhance the readability.
+
+### Team-based tasks:
+- Updated the Javadoc for some parts of the code base to improve readability.
+
+### Review/mentoring contributions:
+- Helped to review and merge the pull requests for other team members: [List of PRs reviewed by me](https://github.com/AY2425S1-CS2113-T11-2/tp/pulls?q=is%3Apr+reviewed-by%3A%40me+is%3Aclosed)
diff --git a/docs/team/frederickemerson.md b/docs/team/frederickemerson.md
new file mode 100644
index 0000000000..3432c278fe
--- /dev/null
+++ b/docs/team/frederickemerson.md
@@ -0,0 +1,47 @@
+# Frederick Amal Emerson's Project Portfolio Page
+
+## Project: Flashbang
+Flashbang - is a desktop application for creating flashcards and and learning in an effective way. The user interacts with it using a CLI with predefied set of commands. It is written in Java, and has about 1000LoC.
+
+# Summary of Contributions
+Given below are my contributions to the project.
+
+- ## Features
+- Added ability to load and save flashcards to the user's system
+- Added Edit command so users can edit saved flashcards and provided various flexibility options to make the process
+ smoother
+- Added Help Command for easy viewing of available Commands
+- Set up basic types Class,FlashCardSet,FlashCard
+
+- ## Enhancement
+- Developed the core command for the Flashbang session (#189) and (#190), enabling users to engage in a flashcard-based Q&A session
+ with options to reveal answers upon request. Enhanced the command with a timer component to allow users to track the
+ time spent on each flashcard and the entire flashcard set.
+- Added UI fixes :
+- - Added Code Indication Pointer so users are aware of the typing zone to refine user input handling
+- - Removed the repetitive spam of available commands for every invalid command with a simple direction to
+- - Refactored the Card's ToString component so its more compact, informative and fits in with other command's invokation
+- - Conducted refactoring of the `Parser` class (#83) to streamline command parsing using regular expressions by mainly
+ fixing regex expressions and logic issues
+- - Resolved issues and enhanced the `view` command (#194) and (#196) to ensure accurate display of flashcards.
+
+- ## User Guide
+- Wrote feature sections: `view`, `edit`, `search`
+ - **Edit**: Detailed instructions on how users can edit flashcards.
+ - **View**: Explained the process for viewing flashcards.
+ - **search**: Provided a comprehensive guide on using the search feature.
+
+- ## Developer Guide
+- Wrote ‘Storage component’ section:
+ - Explained the role and functionality of the storage in loading and storing flashcards.
+- Made Storage Class Diagram:
+ - Created a visual representation of the storage class structure and the types of object involved.
+- Made Storage Sequence Diagram:
+ - Illustrated the sequence of operations involved in both writing and reading flashcards.
+
+- ## Testing
+- Wrote testcases for ViewAll Command (#206)
+
+- ## Links
+- **Code contributed:** [link](https://nus-cs2113-ay2425s1.github.io/tp-dashboard/?search=frederickemerson&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&since=2024-09-20&tabOpen=true&tabType=authorship&checkedFileTypes=docs~functional-code~test-code~other&tabAuthor=frederickemerson&tabRepo=AY2425S1-CS2113-T11-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+- **PRs:** [list of PRs from GitHub](https://github.com/AY2425S1-CS2113-T11-2/tp/pulls?q=+is%3Apr+author%3Afrederickemerson+)
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index ab75b391b8..0000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# John Doe - Project Portfolio Page
-
-## Overview
-
-
-### Summary of Contributions
diff --git a/docs/team/mikolajed.md b/docs/team/mikolajed.md
new file mode 100644
index 0000000000..48f268106c
--- /dev/null
+++ b/docs/team/mikolajed.md
@@ -0,0 +1,27 @@
+# Mikolaj Jedrzejewski's Project Portfolio Page
+
+## Project: Flashbang
+Flashbang - is a desktop application for creating flashcards and and learning in an effective way. The user interacts with it using a CLI with predefied set of commands. It is written in Java, and has about 1000LoC.
+
+Given below are my contributions to the project.
+
+- **New feature** Added ability to perform a quiz for a set of flashcards as it is called - Flashbang.
+ - What is does: the part implemented by me goes through all flashcards and displays a question and asks user about the answer.
+ - Justification: it is an essential part of the app to learn from flashcards - getting a question and not revealing the answer.
+ - Highlights: It is the most complex command that is used - it uses UI component for both input and output.
+- **New feature/Enhancement** Did major refactoring of Parser class.
+ - It uses regular expression for extracting command type and the parameters.
+ - It uses a factory design pattern to create commands.
+ - Command creation separate from execution.
+- **New feature** Added ability to display formatted outputs in the UI class and getting inputs.
+- **New feature** Implemented command for viewing all flashcards in the app.
+- **Enhancement** Removed dependency of command execution from Storage component.
+ - Was it hard: No, only one command was using it and that responsibility was moved elsewhere.
+- **Testing** Added JUnit tests for the Parser class.
+- **Code contributed:** [link](https://nus-cs2113-ay2425s1.github.io/tp-dashboard/?search=mikolajed&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-09-20&tabOpen=true&tabType=authorship&tabAuthor=mikolajed&tabRepo=AY2425S1-CS2113-T11-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+- **Documentation**
+ - Added sequence diagram for Parser and described its workings.
+ - Updated UG with a few LoC
+ - Unified style for PlantUML diagrams.
+- **Community**
+ - PRs: [list of PRs from GitHub](https://github.com/AY2425S1-CS2113-T11-2/tp/pulls?q=is%3Apr+is%3Aclosed+reviewed-by%3A%40me)
diff --git a/docs/team/mikolajed.pdf b/docs/team/mikolajed.pdf
new file mode 100644
index 0000000000..8682c21817
Binary files /dev/null and b/docs/team/mikolajed.pdf differ
diff --git a/docs/team/raneeng.md b/docs/team/raneeng.md
new file mode 100644
index 0000000000..423a772f20
--- /dev/null
+++ b/docs/team/raneeng.md
@@ -0,0 +1,68 @@
+# Project Portfolio Page (PPP)
+
+## Overview
+**FlashBang** is a CLI app designed to provide students with a smart way of studying for their modules. The app will manage a limited number of flashcards for a small number of modules, optimized for users who prefer a CLI.
+
+## Summary of Contributions
+
+### Code Contributed
+[RepSense Link](https://nus-cs2113-ay2425s1.github.io/tp-dashboard/?search=raneeng&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-09-20&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+### Enhancements Implemented
+1. **Command Classes:**
+ - **How**: Implemented classes such as `AddCommand`, `DeleteCommand`, `FlashBangCommand`, etc.
+
+2. **Command Class Testing:**
+ - **How**: Used JUnit framework to write tests covering different scenarios and edge cases.
+
+3. **Show FlashBang Percentage:**
+ - **How**: Added methods to calculate and display the percentage based on user performance.
+
+4. **Show FlashBang Mistakes:**
+ - **How**: Implemented methods to track incorrect answers and present them to the user.
+
+### Contributions to User Guide (UG)
+[UG](https://ay2425s1-cs2113-t11-2.github.io/tp/UserGuide.html)
+- Wrote feature sections: `add`, `delete`, `flashbang`
+ - **Add**: Detailed instructions on how users can add new flashcards.
+ - **Delete**: Explained the process for removing flashcards.
+ - **FlashBang**: Provided a comprehensive guide on using the flashbang feature.
+
+### Contributions to Developer’s Guide (DG)
+[DG](https://ay2425s1-cs2113-t11-2.github.io/tp/DeveloperGuide.html)
+- Wrote ‘Parser component’ section:
+ - Explained the role and functionality of the parser in interpreting user commands.
+- Made Parser Partial Class Diagram:
+ - Created a visual representation of the parser structure.
+- Made Parser Delete Sequence Diagram:
+ - Illustrated the sequence of operations for the delete command.
+
+### Contributions to Team-Based Tasks
+1. Conducting Code Reviews and Providing Feedback
+2. Maintaining the Issue Tracker
+3. Updating User Docs
+
+### Review/Mentoring Contributions:
+[Example 1](https://github.com/AY2425S1-CS2113-T11-2/tp/pull/160)
+[Example 2](https://github.com/AY2425S1-CS2113-T11-2/tp/pull/146)
+
+### Contributions Beyond the Project Team
+[Bugs reported in other team's products](https://github.com/raneeng/ped/issues)
+
+## DG Extract
+
+#### Structure
+Below is a partial class diagram showing the interactions of the `Parser` class.
+![Parser class diagram](./../diagrams/ParserPartialClassDiagram.jpg)
+
+The sequence diagram below illustrates the interactions taking `parseCommand(“delete --m cs2113 --i 1”)` as an example.
+![Sample delete call sequence diagram](./../diagrams/ParserDeleteSequenceDiagram.png)
+
+#### Example
+How the `Parser` component works:
+1. The `Parser` receives the command input.
+2. It identifies the command type using `parseCommandType`.
+3. Depending on the command type, it creates the corresponding command object (e.g., `AddCommand`).
+4. The created command is executed, producing a `CommandResult`.
+5. The `CommandResult` is then used by `Ui` to provide feedback to the user.
+
diff --git a/docs/team/raneeng.pdf b/docs/team/raneeng.pdf
new file mode 100644
index 0000000000..2ca4d2fc77
Binary files /dev/null and b/docs/team/raneeng.pdf differ
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
deleted file mode 100644
index 5c74e68d59..0000000000
--- a/src/main/java/seedu/duke/Duke.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.duke;
-
-import java.util.Scanner;
-
-public class Duke {
- /**
- * Main entry-point for the java.duke.Duke application.
- */
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- System.out.println("What is your name?");
-
- Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
- }
-}
diff --git a/src/main/java/seedu/duke/Flashbang.java b/src/main/java/seedu/duke/Flashbang.java
new file mode 100644
index 0000000000..24fee323be
--- /dev/null
+++ b/src/main/java/seedu/duke/Flashbang.java
@@ -0,0 +1,66 @@
+package seedu.duke;
+
+import seedu.duke.flashutils.commands.Command;
+import seedu.duke.flashutils.commands.CommandResult;
+import seedu.duke.flashutils.commands.DeleteCommand;
+import seedu.duke.flashutils.commands.QuitCommand;
+import seedu.duke.flashutils.types.FlashBook;
+import seedu.duke.flashutils.utils.Parser;
+import seedu.duke.flashutils.utils.Storage;
+import seedu.duke.flashutils.utils.Ui;
+
+import java.io.IOException;
+
+import static seedu.duke.flashutils.utils.Ui.displayCommands;
+
+public class Flashbang {
+ /**
+ * Main entry-point for the java.duke.Flashbang application.
+ */
+
+ private Ui ui;
+ private Storage storage;
+ private FlashBook flashBook;
+
+ private Flashbang(String dataPath) {
+ ui = new Ui();
+ storage = new Storage(dataPath);
+ try {
+ FlashBook.setInstance(storage.readFlashCardsFromFile());
+ } catch (IOException e) {
+ System.out.println(e.getMessage());
+ }
+ flashBook = FlashBook.getInstance();
+ }
+
+ private void run() {
+ Ui.welcomeMessage();
+ displayCommands();
+ String input = "";
+ Command command = null;
+ while (!(command instanceof QuitCommand)) {
+ try {
+ input = Ui.getRequest().trim();
+ command = Parser.parseCommand(input);
+ CommandResult result = command.execute();
+ Ui.printResponse(result.feedbackToUser);
+ storage.writeFlashBookToFile(FlashBook.getInstance());
+ if (command instanceof DeleteCommand && ((DeleteCommand) command).getTargetCard() == null) {
+ storage.deleteFlashCardSetFile(((DeleteCommand) command).getTargetSet().getModuleName());
+ }
+ } catch (IllegalArgumentException e) {
+ Ui.printResponse(e.getMessage());
+ displayCommands();
+ } catch (IOException e) {
+ Ui.printResponse(e.getMessage() + "\nAn IO Exception has been detected, please reset the App!");
+ }
+ }
+ }
+
+ /**
+ * Main function to run the Flashbang app
+ */
+ public static void main(String[] args) {
+ new Flashbang("./data").run();
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/AddCommand.java b/src/main/java/seedu/duke/flashutils/commands/AddCommand.java
new file mode 100644
index 0000000000..31754f427e
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/AddCommand.java
@@ -0,0 +1,85 @@
+package seedu.duke.flashutils.commands;
+
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashCardSet;
+import seedu.duke.flashutils.utils.Ui;
+
+/**
+ * Adds a flashcard to flashcard set.
+ */
+public class AddCommand extends Command {
+
+ // Confirmation message to be displayed to user, with placeholder for flashcard details
+ public static final String SUCCESS_MESSAGE = "Successfully added flashcard to module '%1$s'";
+
+ private Card cardToAdd;
+ private FlashCardSet targetSet;
+
+ /**
+ * Constructs a new Add Command with specified module, question and answer
+ * This creates new card and add it to the desired module
+ *
+ * @param module
+ * @param question
+ * @param answer
+ */
+ public AddCommand(FlashCardSet module, String question, String answer) {
+ if (module == null || question == null || answer == null) {
+ throw new NullPointerException("Please enter a valid input");
+ }
+
+ String currentModuleName = module.getModuleName();
+ if (currentModuleName.contains("--m") || currentModuleName.trim().isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+
+ cardToAdd = new Card(question, answer);
+ this.targetSet = module;
+ }
+
+ /**
+ * Constructs a new Add Command that add an existing card into the module
+ *
+ * @param cardToAdd
+ * @param module
+ */
+ public AddCommand(FlashCardSet module, Card cardToAdd) {
+ String currentModuleName = module.getModuleName();
+ if (currentModuleName.contains("--m") || currentModuleName.trim().isEmpty()) {
+ throw new IllegalArgumentException("Please enter a valid module name");
+ }
+
+ this.cardToAdd = cardToAdd;
+ this.targetSet = module;
+ }
+
+ /**
+ * Gets the card to be added
+ *
+ * @return The Card object to be added
+ */
+ public Card getCardToAdd() {
+ return cardToAdd;
+ }
+
+ /**
+ * Gets the module name of the card
+ *
+ * @return The target module
+ */
+ public FlashCardSet getTargetSet() {
+ return targetSet;
+ }
+
+ /**
+ * Prints result of the command,
+ * which includes the success message and the Card to be added
+ *
+ * @return The result of the command
+ */
+ @Override
+ public CommandResult execute() {
+ targetSet.addCard(cardToAdd);
+ return new CommandResult(String.format(SUCCESS_MESSAGE, targetSet.getModuleName()));
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/Command.java b/src/main/java/seedu/duke/flashutils/commands/Command.java
new file mode 100644
index 0000000000..833566fb90
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/Command.java
@@ -0,0 +1,13 @@
+package seedu.duke.flashutils.commands;
+
+
+/**
+ * Represents an executable command.
+ */
+public abstract class Command {
+
+ /**
+ * Executes the command and returns the result.
+ */
+ public abstract CommandResult execute();
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/CommandResult.java b/src/main/java/seedu/duke/flashutils/commands/CommandResult.java
new file mode 100644
index 0000000000..c64a6c9217
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/CommandResult.java
@@ -0,0 +1,29 @@
+package seedu.duke.flashutils.commands;
+
+/**
+ * Represents the result of a command execution.
+ */
+public class CommandResult {
+
+ // The feedback message displayed to user after command is executed
+ public String feedbackToUser;
+
+ /**
+ * Construct a Command Result with specified feedback to user
+ *
+ * @param feedbackToUser
+ */
+ public CommandResult(String feedbackToUser) {
+ this.feedbackToUser = feedbackToUser;
+ }
+
+ /**
+ * Gets feedbackToUser
+ *
+ * @return feedbackToUser
+ */
+ public String getFeedbackToUser() {
+ return feedbackToUser;
+ }
+
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/DeleteCommand.java b/src/main/java/seedu/duke/flashutils/commands/DeleteCommand.java
new file mode 100644
index 0000000000..afd57eea2d
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/DeleteCommand.java
@@ -0,0 +1,71 @@
+package seedu.duke.flashutils.commands;
+
+
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashBook;
+import seedu.duke.flashutils.types.FlashCardSet;
+
+/**
+ * Removes flashcard from flashcard set.
+ */
+public class DeleteCommand extends Command {
+
+ // Confirmation message to be displayed to user, with placeholder for flashcard details
+ public static final String SUCCESS_MESSAGE = "Successfully deleted flashcard(s): %1$s\n";
+
+ public static final int INDEX_OFFSET = 1;
+
+ private Card targetCard;
+ private FlashCardSet targetSet;
+
+ /**
+ * Constructs a Delete Command with specified module and card index
+ *
+ * @param module FlashCardSet to perform DeleteCommand on
+ * @param cardIndex Index of card to delete
+ */
+ public DeleteCommand(FlashCardSet module, int cardIndex) throws IndexOutOfBoundsException {
+ targetSet = module;
+ if (cardIndex > 0) {
+ targetCard = targetSet.getCard(cardIndex - INDEX_OFFSET);
+ } else {
+ targetCard = null;
+ }
+ }
+
+ /**
+ * Gets the target module that has flashcard to be deleted
+ *
+ * @return The module having the flashcard to be deleted
+ */
+ public FlashCardSet getTargetSet() {
+ return targetSet;
+ }
+
+ /**
+ * Gets the target card to be deleted
+ * @return The card to be deleted
+ */
+ public Card getTargetCard() {
+ return targetCard;
+ }
+
+ /**
+ * Prints result of the command,
+ * which includes the success message and the {@code Card} or {@code FlashCardSet} to be deleted
+ *
+ * @return The result of the command
+ */
+ @Override
+ public CommandResult execute() {
+ CommandResult deleteResult;
+ if (targetCard != null) {
+ targetSet.removeCard(targetCard);
+ deleteResult = new CommandResult(String.format(SUCCESS_MESSAGE, targetCard));
+ } else {
+ FlashBook.getInstance().deleteFlashCardSet(targetSet.getModuleName());
+ deleteResult = new CommandResult(String.format(SUCCESS_MESSAGE, targetSet.toString()));
+ }
+ return deleteResult;
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/EditCommand.java b/src/main/java/seedu/duke/flashutils/commands/EditCommand.java
new file mode 100644
index 0000000000..7d05d2ec18
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/EditCommand.java
@@ -0,0 +1,141 @@
+package seedu.duke.flashutils.commands;
+
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashCardSet;
+
+import static seedu.duke.flashutils.utils.Ui.getRequest;
+import static seedu.duke.flashutils.utils.Ui.displayConfirmationQuestion;
+import static seedu.duke.flashutils.utils.Ui.displayOldStoredValue;
+import static seedu.duke.flashutils.utils.Ui.displayGetNewPromptFromUser;
+
+/**
+ * Updates information in an existing flashcard.
+ */
+public class EditCommand extends Command {
+
+ // Confirmation message to be displayed to user, with placeholder for flashcard details
+ public static final String SUCCESS_MESSAGE = "Successfully edited flashcard";
+
+ public static final int INDEX_OFFSET = 1;
+
+ private Card cardToEdit;
+ private Card newCard;
+ private FlashCardSet targetSet;
+
+ /**
+ * Constructs an Edit Command with specified module, index, new question and new answer
+ *
+ * @param module FlashCardSet to perform EditCommand on
+ * @param cardIndex Index of card to edit
+ * @param newQuestion String to replace question of Card
+ * @param newAnswer String to replace answer of Card
+ */
+ public EditCommand(FlashCardSet module, int cardIndex, String newQuestion, String newAnswer) {
+ this.targetSet = module;
+ this.cardToEdit = targetSet.getCard(cardIndex - INDEX_OFFSET);
+ this.newCard = new Card(newQuestion, newAnswer);
+ }
+
+ /**
+ * Construct an Edit Command with specified module and index
+ * The new question and answer will be collected from user later on
+ *
+ * @param module FlashCardSet to perform EditCommand on
+ * @param cardIndex Index of card to edit
+ */
+ public EditCommand(FlashCardSet module, int cardIndex) throws IndexOutOfBoundsException {
+ this.targetSet = module;
+ this.cardToEdit = targetSet.getCard(cardIndex - INDEX_OFFSET);
+ this.newCard = getUpdatedQuestionAnswerFromUser(cardToEdit);
+ }
+
+ protected Card getUpdatedQuestionAnswerFromUser(Card cardToEdit) {
+ this.newCard = new Card(cardToEdit.getQuestion(), cardToEdit.getAnswer());
+
+ // Update question
+ String updatedQuestion = updateField(cardToEdit.getQuestion(), "Question");
+ newCard.setQuestion(updatedQuestion);
+
+ // Update answer
+ String updatedAnswer = updateField(cardToEdit.getAnswer(), "Answer");
+ newCard.setAnswer(updatedAnswer);
+
+ return newCard;
+ }
+
+ /**
+ * Prompts the user to confirm if they want to update the specified field.
+ * If the user confirms, it will prompt for new input until valid data is entered.
+ *
+ * @param oldFieldValue the current value of the field
+ * @param fieldName the name of the field (e.g., "Question" or "Answer")
+ * @return the updated value entered by the user
+ */
+ private String updateField(String oldFieldValue, String fieldName) {
+ displayOldStoredValue(fieldName, oldFieldValue);
+ displayConfirmationQuestion(fieldName);
+
+ String confirmation = getRequest();
+
+ if (confirmation.equalsIgnoreCase("y")) {
+ String newFieldValue;
+ do {
+ displayGetNewPromptFromUser(fieldName);
+ newFieldValue = getRequest();
+
+ // Check if the new value is empty
+ if (newFieldValue == null || newFieldValue.trim().isEmpty()) {
+ System.out.println(fieldName + " cannot be empty. Please try again.");
+ }
+ } while (newFieldValue == null || newFieldValue.trim().isEmpty());
+
+ return newFieldValue;
+ } else if (confirmation.equalsIgnoreCase("n")) {
+ System.out.println("Alright, Noted! Using Old " + fieldName);
+ return oldFieldValue; // Return the old value if the user opts not to change it
+ } else {
+ System.out.println("Sorry! Unknown option entered. Using Old " + fieldName);
+ return oldFieldValue; // Return the old value in case of an unknown option
+ }
+ }
+
+ /**
+ * Prints result of the command,
+ * which includes the success message and the Card to be edited
+ *
+ * @return The result of the command
+ */
+ @Override
+ public CommandResult execute() {
+ cardToEdit.setQuestion(newCard.getQuestion());
+ cardToEdit.setAnswer(newCard.getAnswer());
+ return new CommandResult(SUCCESS_MESSAGE);
+ }
+
+ /**
+ * Gets the module that has the card to be edited
+ *
+ * @return The module having the card to be edited
+ */
+ public FlashCardSet getTargetSet() {
+ return targetSet;
+ }
+
+ /**
+ * Gets the card to be edited
+ *
+ * @return The card to be edited
+ */
+ public Card getCardToAdd() {
+ return cardToEdit;
+ }
+
+ /**
+ * Gets the updated card
+ *
+ * @return The updated card
+ */
+ public Card getNewCard() {
+ return newCard;
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/FlashbangCommand.java b/src/main/java/seedu/duke/flashutils/commands/FlashbangCommand.java
new file mode 100644
index 0000000000..bbaa07b6a3
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/FlashbangCommand.java
@@ -0,0 +1,49 @@
+package seedu.duke.flashutils.commands;
+
+import seedu.duke.flashutils.types.FlashCardSet;
+
+/**
+ * Starts a FlashBang session, where questions for each flashcard are displayed
+ * and users can choose to display answers.
+ */
+public class FlashbangCommand extends Command {
+ // Confirmation message to be displayed to user, with placeholder for flashcard details
+ public static final String SUCCESS_MESSAGE = "Successful FlashBang for flashcard set: %1$s";
+ private final FlashCardSet targetSet;
+ private long timerThreshold;
+
+
+ /**
+ * Constructs the Flashbang Command with specified target set (module)
+ *
+ * @param targetSet represents the FlashCardSet to be tested on
+ */
+ public FlashbangCommand(FlashCardSet targetSet) {
+ this.targetSet = targetSet;
+ }
+
+ public FlashbangCommand(FlashCardSet targetSet, long timerThreshold) {
+ this.targetSet = targetSet;
+ this.timerThreshold = timerThreshold;
+ }
+
+ public long getTimerThreshold() {
+ return timerThreshold;
+ }
+
+ /**
+ * Prints result of the command,
+ * which includes the success message and the module to be displayed
+ *
+ * @return The result of the command
+ */
+ @Override
+ public CommandResult execute() {
+ targetSet.performFlashBang(timerThreshold);
+ return new CommandResult(String.format(SUCCESS_MESSAGE, targetSet.getModuleName()));
+ }
+
+ public FlashCardSet getTargetSet() {
+ return targetSet;
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/HelpCommand.java b/src/main/java/seedu/duke/flashutils/commands/HelpCommand.java
new file mode 100644
index 0000000000..a4f596a0ee
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/HelpCommand.java
@@ -0,0 +1,27 @@
+package seedu.duke.flashutils.commands;
+
+public class HelpCommand extends Command {
+ @Override
+ public CommandResult execute() {
+ String availableCommands = "Available Commands: \n"
+ + " 1. Add a flashcard: \n"
+ + " \t add --m [Module Name] {--t [Topic] (optional)} --q [Question] --a [Answer] \n"
+ + " 2. View all flashcards of a module: \n"
+ + " \t view --m [Module Name] \n"
+ + " 3. View all flashcards: \n"
+ + " \t view --all\n"
+ + " 4. Delete a flashcard: \n"
+ + " \t delete --m [Module Name] --i [Index] \n"
+ + " \t deleteall --m [Module Name] \n"
+ + " 5. Edit a flashcard: \n"
+ + " \t edit --m [Module Name] --i [Index] --q [New Question] --a [New Answer] \n"
+ + " 6. Flashbang - view all the flashcards of a module without seeing the answers: \n"
+ + " \t flashbang --m [Module Name] --t [time] [unit (second/seconds/minute/minutes)]\n"
+ + " 7. Search for flashcards: \n"
+ + " \t search --m [Module Name] {/t (optional)} --s [Search Term] \n"
+ + " 8. Quit the app: \n"
+ + " \t quit";
+
+ return new CommandResult(availableCommands);
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/InvalidCommand.java b/src/main/java/seedu/duke/flashutils/commands/InvalidCommand.java
new file mode 100644
index 0000000000..369c0a9519
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/InvalidCommand.java
@@ -0,0 +1,18 @@
+package seedu.duke.flashutils.commands;
+
+public class InvalidCommand extends Command {
+ private String errorMessage;
+
+ public InvalidCommand() {
+ this.errorMessage = "uh oh bad command";
+ }
+
+ public InvalidCommand(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ @Override
+ public CommandResult execute() {
+ throw new IllegalArgumentException(errorMessage);
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/QuitCommand.java b/src/main/java/seedu/duke/flashutils/commands/QuitCommand.java
new file mode 100644
index 0000000000..43296a5ab8
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/QuitCommand.java
@@ -0,0 +1,18 @@
+package seedu.duke.flashutils.commands;
+
+/**
+ * Terminates the program.
+ */
+public class QuitCommand extends Command {
+
+ /**
+ * Prints result of the command,
+ * which includes the success message
+ *
+ * @return The result of the command
+ */
+ @Override
+ public CommandResult execute() {
+ return new CommandResult("Quit Flash Session");
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/SearchCommand.java b/src/main/java/seedu/duke/flashutils/commands/SearchCommand.java
new file mode 100644
index 0000000000..14fd4226f1
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/SearchCommand.java
@@ -0,0 +1,43 @@
+package seedu.duke.flashutils.commands;
+
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashCardSet;
+
+/**
+ * Represents searching for flashcards which match the term
+ */
+public class SearchCommand extends Command {
+ private final String searchTerm;
+ private final boolean byTopic;
+ private final FlashCardSet targetSet;
+
+ /**
+ * Constructs a search command
+ * @param searchTerm represents the term that all found cards must contain
+ * @param byTopic represents whether the search term only checks topics
+ * @param targetSet represents the module being searched from
+ */
+ public SearchCommand(String searchTerm, boolean byTopic, FlashCardSet targetSet) {
+ this.searchTerm = searchTerm;
+ this.byTopic = byTopic;
+ this.targetSet = targetSet;
+ }
+
+ @Override
+ public CommandResult execute() {
+ StringBuilder matchingCards = new StringBuilder();
+ int counter = 0;
+ for (Card card : targetSet) {
+ if ((byTopic && card.getTopic().contains(searchTerm))
+ || (card.getAnswer().toLowerCase().contains(searchTerm)
+ || card.getQuestion().toLowerCase().contains(searchTerm))) {
+ matchingCards.append(++counter).append(". ").append(card).append("\n");
+ }
+ }
+ if (matchingCards.isEmpty()) {
+ return new CommandResult("No Cards found :(");
+ } else {
+ return new CommandResult(matchingCards.toString());
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/ViewAllCommand.java b/src/main/java/seedu/duke/flashutils/commands/ViewAllCommand.java
new file mode 100644
index 0000000000..0a46556d3d
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/ViewAllCommand.java
@@ -0,0 +1,32 @@
+package seedu.duke.flashutils.commands;
+
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashBook;
+import seedu.duke.flashutils.types.FlashCardSet;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ViewAllCommand extends Command {
+ @Override
+ public CommandResult execute() {
+ HashMap sets = FlashBook.getInstance().getAllFlashCardSets();
+ StringBuilder sb = new StringBuilder();
+
+ for (Map.Entry entry : sets.entrySet()) {
+ sb.append("MODULE NAME: ").append(entry.getKey()).append("\n");
+ int flashCardIndex = 1;
+ for (Card card : entry.getValue()) {
+ sb.append(String.format("#%1$s -> ",flashCardIndex)).append(card.toString()).append("\n");
+ flashCardIndex+=1;
+ }
+ sb.append("\n");
+ }
+
+ if (sb.length() >= 2 && sb.substring(sb.length() - 2).equals("\n\n")) {
+ sb.delete(sb.length() - 2, sb.length());
+ }
+
+ return new CommandResult(sb.toString());
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/commands/ViewCommand.java b/src/main/java/seedu/duke/flashutils/commands/ViewCommand.java
new file mode 100644
index 0000000000..6b73ded6b2
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/commands/ViewCommand.java
@@ -0,0 +1,49 @@
+package seedu.duke.flashutils.commands;
+
+import seedu.duke.flashutils.types.FlashCardSet;
+
+/**
+ * Displays flashcards for specified flashcard set.
+ */
+public class ViewCommand extends Command {
+
+ // Confirmation message to be displayed to user, with placeholder for flashCardSet details
+ public static final String SUCCESS_MESSAGE = "All flashcards have been displayed for set: %1$s";
+ public String currentModule;
+
+ private FlashCardSet targetSet;
+
+ /**
+ * Constructs a ViewCommand with a specified module
+ * @param module FlashCardSet to view
+ */
+ public ViewCommand(FlashCardSet module) {
+ this.targetSet = module;
+ }
+
+ /**
+ * Gets the module whose flashcards will be displayed
+ */
+ public void getModuleToView() {
+ currentModule = targetSet.getModuleName();
+ }
+
+ /**
+ * Prints the command result,
+ * which includes the success message and the flashcards under that module
+ */
+ @Override
+ public CommandResult execute() {
+ getModuleToView();
+ targetSet.viewFlashCards(currentModule);
+ return new CommandResult(String.format(SUCCESS_MESSAGE, targetSet.getModuleName()));
+ }
+
+ /**
+ * Gets the module to be displayed
+ * @return The module to be displayed
+ */
+ public FlashCardSet getTargetSet() {
+ return targetSet;
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/exceptions/FlashCardSetDoesNotExistException.java b/src/main/java/seedu/duke/flashutils/exceptions/FlashCardSetDoesNotExistException.java
new file mode 100644
index 0000000000..0b9e796111
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/exceptions/FlashCardSetDoesNotExistException.java
@@ -0,0 +1,6 @@
+package seedu.duke.flashutils.exceptions;
+
+public class FlashCardSetDoesNotExistException extends Exception {
+
+ public FlashCardSetDoesNotExistException() {}
+}
diff --git a/src/main/java/seedu/duke/flashutils/types/Card.java b/src/main/java/seedu/duke/flashutils/types/Card.java
new file mode 100644
index 0000000000..99a42d9a87
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/types/Card.java
@@ -0,0 +1,54 @@
+package seedu.duke.flashutils.types;
+
+/**
+ * Represents an individual flashcard with a question, answer and topic
+ */
+public class Card {
+ private String question;
+ private String answer;
+ private String topic;
+
+ public Card(String question, String answer) {
+ this.question = question;
+ this.answer = answer;
+ this.topic = "";
+ }
+
+ public Card(String question, String answer, String topic) {
+ this.question = question;
+ this.answer = answer;
+ this.topic = topic;
+ }
+
+ public String getQuestion() {
+ return question;
+ }
+
+ public String getTopic() {
+ return topic;
+ }
+
+ public void setQuestion(String question) {
+ this.question = question;
+ }
+
+ public String getAnswer() {
+ return answer;
+ }
+
+ public void setAnswer(String answer) {
+ this.answer = answer;
+ }
+
+ public String toWritableString() {
+ return String.format("%1$s | %2$s | %3$s", question, answer, topic);
+ }
+
+ @Override
+ public String toString() {
+ if(topic==null || topic.isEmpty() || topic.equalsIgnoreCase("null")){
+ return String.format("%1$s : %2$s", question, answer);
+ }
+ return String.format("%1$s : %2$s (Topic: %3$s)", question, answer, topic);
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/types/FlashBook.java b/src/main/java/seedu/duke/flashutils/types/FlashBook.java
new file mode 100644
index 0000000000..4e439db662
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/types/FlashBook.java
@@ -0,0 +1,55 @@
+package seedu.duke.flashutils.types;
+
+import java.util.HashMap;
+
+/**
+ * Represents the complete list of flashcards
+ */
+public class FlashBook {
+
+ private static FlashBook instance = new FlashBook();
+
+ private final HashMap allFlashCardSets;
+
+ private FlashBook() {
+ this.allFlashCardSets = new HashMap<>();
+ }
+
+ private FlashBook(HashMap flashCards) {
+ this.allFlashCardSets = flashCards;
+ }
+
+ public static FlashBook getInstance() {
+ if (instance == null) {
+ instance = new FlashBook();
+ }
+ return instance;
+ }
+
+ public static void setInstance(HashMap flashCards) {
+ instance = new FlashBook(flashCards);
+ }
+
+ public HashMap getAllFlashCardSets() {
+ return allFlashCardSets;
+ }
+
+ public void addFlashCardSet(String module) {
+ allFlashCardSets.put(module, new FlashCardSet(module));
+ }
+
+ public FlashCardSet getFlashCardSet(String module) {
+ if (allFlashCardSets.get(module) == null) {
+ addFlashCardSet(module);
+ }
+ return allFlashCardSets.get(module);
+ }
+ public void deleteFlashCardSet(String module) {
+ allFlashCardSets.remove(module);
+ }
+
+ public boolean flashCardSetExists(String module) {
+ return allFlashCardSets.get(module) != null;
+ }
+
+}
diff --git a/src/main/java/seedu/duke/flashutils/types/FlashCardSet.java b/src/main/java/seedu/duke/flashutils/types/FlashCardSet.java
new file mode 100644
index 0000000000..8cb609623d
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/types/FlashCardSet.java
@@ -0,0 +1,156 @@
+package seedu.duke.flashutils.types;
+
+import seedu.duke.flashutils.utils.Ui;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Represents the list of flashcards of the same module
+ */
+public class FlashCardSet implements Iterable {
+
+ private final ArrayList flashCardSet;
+ private final String moduleName;
+
+ public FlashCardSet(String module) {
+ this.moduleName = module;
+ this.flashCardSet = new ArrayList<>();
+ }
+
+ public FlashCardSet(String module, ArrayList flashCardSet) {
+ this.moduleName = module;
+ this.flashCardSet = flashCardSet;
+ }
+
+ public String getModuleName() {
+ return this.moduleName;
+ }
+
+ public ArrayList getFlashCardSet() {
+ return this.flashCardSet;
+ }
+
+ public Card getCard(int cardIndex) throws IndexOutOfBoundsException {
+ if (!indexIsValid(cardIndex)) {
+ throw new IndexOutOfBoundsException();
+ }
+ return this.flashCardSet.get(cardIndex);
+ }
+
+ public void addCard(Card toAdd) {
+ flashCardSet.add(toAdd);
+ assert flashCardSet.contains(toAdd);
+ }
+
+ public void removeCard(Card toRemove) {
+ flashCardSet.remove(toRemove);
+ assert !flashCardSet.contains(toRemove);
+ }
+
+
+ // Displays all flashcards (view command) in FLashCardSet
+ public void viewFlashCards(String module) {
+ String currentModule = getModuleName();
+ assert (currentModule != null);
+ if ((currentModule != null) && (currentModule.equals(module)) && (!flashCardSet.isEmpty())) {
+ int index = 1;
+ System.out.println("_".repeat(50));
+ for (Card flashCard : flashCardSet) {
+ System.out.println(index + ". " + flashCard);
+ System.out.println("_".repeat(50));
+ index++;
+ }
+ } else if (flashCardSet.isEmpty()) {
+ Ui.printResponse("No flashcards found for this module.");
+ }
+ }
+
+ public void performFlashBang(long timerThreshold) {
+ //start keeps track of time spent in answering all questions,
+ // recurring keeps track of time spent in answering each question
+ Date start = new Date();
+ Date recurring = new Date();
+ // variables to store the number of correct and wrong answers
+ int flashCardIndex = 0;
+ int correctAnswers = 0;
+ int wrongAnswers = 0;
+
+ List mistakes = new ArrayList<>();
+ for (Card card : flashCardSet) {
+ Ui.printResponse("Flashcard no." + flashCardIndex + "\n\t" + card.getQuestion());
+ Ui.printResponse("Reveal the answer? (y/n)");
+ String reveal = Ui.getRequest();
+
+ while (!reveal.equalsIgnoreCase("y") && !reveal.equalsIgnoreCase("n")) {
+ Ui.printResponse("Invalid input. Please enter 'y' or 'n'.");
+ reveal = Ui.getRequest();
+ }
+ if (reveal.equals("y")) {
+ System.out.println("Answer : "+card.getAnswer());
+ }
+
+ Ui.printResponse("Did you get the correct answer? (y/n)");
+ String answerCorrect = Ui.getRequest();
+
+ while (!answerCorrect.equalsIgnoreCase("y") && !answerCorrect.equalsIgnoreCase("n")) {
+ Ui.printResponse("Invalid input. Please enter 'y' or 'n'.");
+ answerCorrect = Ui.getRequest();
+ }
+
+ if (answerCorrect.equals("y")) {
+ correctAnswers+=1;
+ } else if (answerCorrect.equals("n")) {
+ wrongAnswers+=1;
+ mistakes.add(card); // Add card to the mistake list
+ }
+
+ double timeSpentPerQuestion = Math.round(((new Date()).getTime()-recurring.getTime())/1000.00);
+
+ Ui.printResponse("You spent "+timeSpentPerQuestion+" seconds reviewing this flashcard.");
+ recurring = new Date();
+
+ if(timerThreshold > 0) {
+ if (recurring.getTime() - start.getTime() > timerThreshold) {
+ Ui.printResponse("Oops You've run out of time! ");
+ }
+ }
+ flashCardIndex++;
+ }
+
+ // Calculate percentage of right/wrong answers
+ int totalAnswers = correctAnswers + wrongAnswers;
+ double correctPercentage = (double) correctAnswers / totalAnswers * 100;
+ System.out.println("Your score is: " + correctPercentage + "% (" + correctAnswers + "/" + totalAnswers + ")");
+ // Print mistakes list
+ System.out.println("You answered the following flashcards incorrectly:\n");
+ for (Card card : mistakes) {
+ System.out.println(card.toString());
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder setString = new StringBuilder(String.format("MODULE: %1$s\n", moduleName));
+ for (Card card : flashCardSet) {
+ setString.append(card.toString()).append("\n");
+ }
+ return setString.toString();
+ }
+
+ public int getNumberOfFlashcards() {
+ return flashCardSet.size();
+ }
+
+ public boolean indexIsValid(int index) {
+ return index >= 0 && index < flashCardSet.size();
+ }
+
+ @Override
+ public Iterator iterator() {
+ return flashCardSet.iterator();
+ }
+
+}
diff --git a/src/main/java/seedu/duke/flashutils/utils/Parser.java b/src/main/java/seedu/duke/flashutils/utils/Parser.java
new file mode 100644
index 0000000000..60c399608c
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/utils/Parser.java
@@ -0,0 +1,256 @@
+package seedu.duke.flashutils.utils;
+
+import seedu.duke.flashutils.commands.AddCommand;
+import seedu.duke.flashutils.commands.Command;
+import seedu.duke.flashutils.commands.DeleteCommand;
+import seedu.duke.flashutils.commands.EditCommand;
+import seedu.duke.flashutils.commands.FlashbangCommand;
+import seedu.duke.flashutils.commands.HelpCommand;
+import seedu.duke.flashutils.commands.InvalidCommand;
+import seedu.duke.flashutils.commands.QuitCommand;
+import seedu.duke.flashutils.commands.SearchCommand;
+import seedu.duke.flashutils.commands.ViewAllCommand;
+import seedu.duke.flashutils.commands.ViewCommand;
+
+
+import seedu.duke.flashutils.exceptions.FlashCardSetDoesNotExistException;
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashBook;
+import seedu.duke.flashutils.types.FlashCardSet;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Parser {
+ private enum CommandType { Add, Delete, DeleteAll, Edit, View, FlashBang, Quit, Invalid, Search, Help }
+
+ private static CommandType parseCommandType(String input) {
+ String commandKeyword = "^(\\badd\\b|\\bdelete\\b|\\bdeleteall\\b|\\bedit\\b|\\bview\\b|\\bflashbang\\b" +
+ "|\\bquit\\b|\\bsearch\\b|\\bhelp\\b)";
+ Pattern commandPattern = Pattern.compile(commandKeyword);
+ Matcher matcher = commandPattern.matcher(input);
+ if (matcher.find()) {
+ return switch (matcher.group(1).toLowerCase()) {
+ case "add" -> CommandType.Add;
+ case "delete" -> CommandType.Delete;
+ case "edit" -> CommandType.Edit;
+ case "view" -> CommandType.View;
+ case "flashbang" -> CommandType.FlashBang;
+ case "search" -> CommandType.Search;
+ case "quit" -> CommandType.Quit;
+ case "help" -> CommandType.Help;
+ default -> CommandType.Invalid;
+ };
+ }
+ return CommandType.Invalid;
+ }
+
+ public static Command parseCommand(String input) {
+ CommandType commandType = parseCommandType(input);
+ return switch (commandType) {
+ case Add -> createAddCommand(input);
+ case Delete -> createDeleteCommand(input);
+ case Edit -> createEditCommand(input);
+ case View -> createViewCommand(input);
+ case FlashBang -> createFlashbangCommand(input);
+ case Search -> createSearchCommand(input);
+ case Quit -> createQuitCommand();
+ case Help -> createHelpCommand();
+ default -> new InvalidCommand();
+ };
+ }
+
+ public static Command createHelpCommand(){
+ return new HelpCommand();
+ }
+
+ public static Command createAddCommand(String input) {
+ Pattern addPattern = Pattern.compile("--m\\s+(.+?)(?:\\s+--t\\s+(.+))?\\s+--q\\s+(.+?)\\s+--a\\s+(.+)");
+ Matcher matcher = addPattern.matcher(input);
+ if (matcher.find()) {
+ String moduleName = matcher.group(1);
+ String topic = matcher.group(2);
+ if (!(moduleName.contains("--m") || moduleName.trim().isEmpty())) {
+ FlashCardSet module = FlashBook.getInstance().getFlashCardSet(moduleName);
+ String question = matcher.group(3);
+ String answer = matcher.group(4);
+ if (question.contains("|") && answer.contains("|")) {
+ throw new IllegalArgumentException("Please enter another pair of question and answer." +
+ " Valid question and answer cannot include '|' ");
+ }
+ if (question.contains("|")) {
+ throw new IllegalArgumentException("Please enter another question." +
+ " A valid question cannot include '|' ");
+ }
+ if (answer.contains("|")) {
+ throw new IllegalArgumentException("Please enter another answer." +
+ " A valid answer cannot include '|' ");
+ }
+
+ if (topic == null) {
+ topic = "";
+ }
+ assert !(module == null);
+ return new AddCommand(module, new Card(question, answer, topic));
+ } else {
+ throw new IllegalArgumentException("Please enter a valid module name");
+ }
+ } else {
+ return new InvalidCommand();
+ }
+ }
+
+ public static Command createDeleteCommand(String input) {
+ try {
+ Pattern deletePattern = Pattern.compile("-m\\s+(.+?)(?=\\s+--i|$)(?:\\s+--i\\s+(\\d+))?");
+ Matcher matcher = deletePattern.matcher(input);
+ if (matcher.find()) {
+ String moduleName = matcher.group(1);
+
+ if (!FlashBook.getInstance().flashCardSetExists(moduleName)) {
+ throw new FlashCardSetDoesNotExistException();
+ }
+
+ FlashCardSet module = FlashBook.getInstance().getFlashCardSet(moduleName);
+ int index;
+ if (matcher.group(2) != null) {
+ index = Integer.parseInt(matcher.group(2));
+ } else {
+ index = -1;
+ }
+ return new DeleteCommand(module, index);
+ } else {
+ return new InvalidCommand();
+ }
+
+ } catch (IndexOutOfBoundsException e) {
+ Ui.printResponse("Please enter a valid index");
+ return new InvalidCommand();
+
+ } catch (FlashCardSetDoesNotExistException e) {
+ return new InvalidCommand("No such module exists");
+ }
+ }
+
+
+ public static Command createEditCommand(String input) {
+ try {
+ Pattern editPattern = Pattern.
+ compile("--m\\s+(.+?)\\s+--i\\s+(\\d+)(?:\\s+--q\\s+(.+?)\\s+--a\\s+(.+))?$");
+ Matcher matcher = editPattern.matcher(input);
+
+ if (matcher.find()) {
+ String moduleName = matcher.group(1);
+
+ if (!FlashBook.getInstance().flashCardSetExists(moduleName)) {
+ throw new FlashCardSetDoesNotExistException();
+ }
+
+ FlashCardSet module = FlashBook.getInstance().getFlashCardSet(moduleName);
+ int index = Integer.parseInt(matcher.group(2));
+
+ // Check if new question and answer are provided in the input
+ if (matcher.group(4) != null && matcher.group(5) != null) {
+ // Use the provided question and answer
+ String newQuestion = matcher.group(4);
+ String newAnswer = matcher.group(5);
+ return new EditCommand(module, index, newQuestion, newAnswer);
+ } else {
+ // No question and answer provided; create EditCommand with prompts
+ return new EditCommand(module, index);
+ }
+ } else {
+ return new InvalidCommand("Please enter a valid index");
+ }
+ } catch (IndexOutOfBoundsException e) {
+ return new InvalidCommand();
+
+ } catch (FlashCardSetDoesNotExistException e) {
+ return new InvalidCommand("No such module exists");
+ }
+
+ }
+
+ public static Command createViewCommand(String input) {
+ Pattern viewModulePattern = Pattern.compile("--m\\s+(.+)");
+ Matcher matcher = viewModulePattern.matcher(input);
+ Pattern viewAllModulePattern = Pattern.compile("--all");
+ Matcher matcherAll = viewAllModulePattern.matcher(input);
+ if (matcher.find()) {
+ String moduleName = matcher.group(1);
+ FlashCardSet module = FlashBook.getInstance().getFlashCardSet(moduleName);
+ return new ViewCommand(module);
+ } else if (matcherAll.find()) {
+ return new ViewAllCommand();
+ } else {
+ return new InvalidCommand();
+ }
+ }
+
+ public static Command createFlashbangCommand(String input) {
+ Pattern flashbangPattern = Pattern
+ .compile("--m\\s+(\\S+)(?:\\s+--t\\s+(\\d+)\\s+(second|seconds|minute|minutes))?$");
+ Matcher matcher = flashbangPattern.matcher(input);
+ if (matcher.find()) {
+ String moduleName = matcher.group(1);
+ String timer = matcher.group(2) != null ? input.substring(input.indexOf("--t")+3).trim() : "";
+ FlashCardSet module = FlashBook.getInstance().getFlashCardSet(moduleName);
+ if (!timer.isEmpty()) {
+ try{
+ long milliseconds = parseTimer(timer);
+ return new FlashbangCommand(module, milliseconds);
+ } catch (IllegalArgumentException e){
+ Ui.printResponse(e.getMessage());
+ }
+ }
+ return new FlashbangCommand(module);
+ } else {
+ return new InvalidCommand();
+ }
+ }
+ public static Command createSearchCommand(String input) {
+ Pattern searchPattern = Pattern.compile("--m\\s+(.+?)(?:\\s+(/t))?\\s+--s\\s+(.+)");
+ Matcher searchMatcher = searchPattern.matcher(input);
+ if (searchMatcher.find()) {
+ String module = searchMatcher.group(1);
+ boolean byTopic = searchMatcher.group(2) != null && searchMatcher.group(2).equals("/t");
+ String searchTerm = searchMatcher.group(3);
+ assert (!(module == null || searchTerm == null));
+ return new SearchCommand(searchTerm, byTopic, FlashBook.getInstance().getFlashCardSet(module));
+ } else {
+ return new InvalidCommand("Invalid format for search =.=");
+ }
+ }
+
+ private static long parseTimer(String timer) {
+ timer = timer.trim().toLowerCase();
+
+ String[] parts = timer.split(" ");
+ if (parts.length != 2) {
+ throw new IllegalArgumentException("Invalid timer format. Expected format: ' '");
+ }
+
+ // Parse the number part
+ double value;
+ try {
+ value = Double.parseDouble(parts[0]);
+
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid number format: " + parts[0]);
+ }
+
+ // Determine the unit part
+ String unit = parts[1];
+
+ return switch (unit) {
+ case "s","second", "seconds" -> (long) (value * 1000);
+ case "min","minute", "minutes" -> (long) (value * 1000 * 60);
+ default -> throw new IllegalArgumentException("Unsupported time unit: " +
+ unit + "supported time units are second,seconds,minute,minutes");
+ };
+ }
+
+ public static Command createQuitCommand() {
+ return new QuitCommand();
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/utils/Storage.java b/src/main/java/seedu/duke/flashutils/utils/Storage.java
new file mode 100644
index 0000000000..ee72a1c331
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/utils/Storage.java
@@ -0,0 +1,153 @@
+package seedu.duke.flashutils.utils;
+
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashBook;
+import seedu.duke.flashutils.types.FlashCardSet;
+
+import javax.xml.namespace.QName;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Scanner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Storage class is responsible for storing and reading data inputted from Flashbang
+ */
+public class Storage {
+ private final File directory;
+
+ public Storage(String directoryPath) {
+ this.directory = new File(directoryPath);
+ createDir();
+ }
+
+ /**
+ * This function creates a directory at the file path of the class instance
+ */
+ private void createDir() {
+ try {
+ if(!directory.exists()){
+ if(!directory.mkdirs()){
+ throw new IOException("Could not create directory");
+ }
+ }
+ } catch (IOException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * This function formats lines from the txt file into appropriate strings for flashcards
+ * @param line represents a line from the txt file as a String
+ * @return a {@code Card} formatted from the file
+ */
+ private Card cardFormatter(String line) {
+ Pattern cardPattern = Pattern.compile(
+ "(.+?)\\s*\\|\\s*(.+?)\\s*\\|\\s*(.+?)?");
+ Matcher cardMatcher = cardPattern.matcher(line);
+ boolean isMatch = cardMatcher.matches();
+ assert isMatch: "Text Format Problem in File";
+ if (cardMatcher.matches()) {
+ String question = cardMatcher.group(1);
+ String answer = cardMatcher.group(2);
+ String topic = cardMatcher.group(3);
+ return new Card(question, answer, topic);
+ }
+ return null;
+ }
+
+ /**
+ * Reads the txt file with the matching name as the module
+ * @param module represents the name of the module of the {@code FlashCardSet}
+ * @param flashCardSetFile represents the file containing the {@code Card} in the set
+ * @return a {@code FlashCardSet} read from the file
+ * @throws IOException when an input/output error occurs
+ */
+ private FlashCardSet readFlashCardSetFromFile(String module, File flashCardSetFile) throws IOException {
+
+ ArrayList cards = new ArrayList<>();
+
+ try {
+ Scanner scanner = new Scanner(flashCardSetFile);
+ while (scanner.hasNext()) {
+ Card card = cardFormatter(scanner.nextLine());
+ if (card != null) {
+ cards.add(card);
+ }
+ }
+ scanner.close();
+ } catch (IOException e) {
+ throw new IOException("An error occurred while reading from the file.");
+ }
+ return new FlashCardSet(module,cards);
+ }
+
+ private void createFile(File file) {
+ try {
+ if(!file.exists()){
+ if(!file.createNewFile()){
+ throw new IOException("Could not create file");
+ }
+ }
+ } catch (IOException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Writes the {@code FlashBook} into a txt file
+ * @param flashBook represents the {@code FlashBook} instance to be written
+ */
+ public void writeFlashBookToFile(FlashBook flashBook){
+ flashBook.getAllFlashCardSets().forEach((module, flashCardSet)-> {
+ File flashCardSetFile = new File(directory, module+".txt");
+ createFile(flashCardSetFile);
+ try {
+ FileWriter fileWriter = new FileWriter(flashCardSetFile.getPath());
+ for (Card card : flashCardSet.getFlashCardSet()) {
+ fileWriter.write(card.toWritableString()+"\n");
+ }
+ fileWriter.close();
+ } catch (IOException e) {
+ System.out.println("An error occurred while writing to the file. ");
+ }
+ });
+ }
+
+ /**
+ * Reads the all txt files within the directory
+ * @return a {@code HashMap}. The key is a {@code String} which is the name of the txt file
+ * and the value stored is the {@code FlashCardSet} read from the file.
+ * @throws IOException when an input/output error occurs
+ */
+ public HashMap readFlashCardsFromFile() throws IOException {
+ HashMap flashCard = new HashMap<>();
+ File[] files = directory.listFiles((dir, name) -> name.endsWith(".txt"));
+
+ if (files != null && files.length > 0) {
+ for (File file : files) {
+ String module = file.getName().split("\\.")[0];
+ flashCard.put(module,readFlashCardSetFromFile(module, file));
+ }
+ } else {
+ System.out.println("No text files found in the directory.");
+ }
+
+ return flashCard;
+ }
+ public void deleteFlashCardSetFile(String module) throws IOException {
+ File[] fileList = directory.listFiles((dir, name) -> name.equals(module + ".txt"));
+ assert fileList != null;
+ if (fileList[0].exists() && fileList[0].delete()) {
+ System.out.println("Successfully deleted file");
+ } else {
+ throw new IOException();
+ }
+ }
+}
+
+
diff --git a/src/main/java/seedu/duke/flashutils/utils/StubStorage.java b/src/main/java/seedu/duke/flashutils/utils/StubStorage.java
new file mode 100644
index 0000000000..4a12fbf187
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/utils/StubStorage.java
@@ -0,0 +1,22 @@
+package seedu.duke.flashutils.utils;
+
+import seedu.duke.flashutils.types.FlashBook;
+
+public class StubStorage extends Storage {
+
+ private boolean isWriteFlashBookToFileCalled = false;
+
+ public StubStorage() {
+ super("./data");
+ }
+
+ @Override
+ public void writeFlashBookToFile(FlashBook flashBook) {
+ super.writeFlashBookToFile(flashBook);
+ isWriteFlashBookToFileCalled = true;
+ }
+
+ public boolean isWriteFlashBookToFileCalled() {
+ return isWriteFlashBookToFileCalled;
+ }
+}
diff --git a/src/main/java/seedu/duke/flashutils/utils/Ui.java b/src/main/java/seedu/duke/flashutils/utils/Ui.java
new file mode 100644
index 0000000000..e66e831be2
--- /dev/null
+++ b/src/main/java/seedu/duke/flashutils/utils/Ui.java
@@ -0,0 +1,85 @@
+package seedu.duke.flashutils.utils;
+
+import java.util.Scanner;
+
+/**
+ * A class responsible for handling user interaction and displaying information to the console.
+ * It provides methods to display responses, get user input, and format the output.
+ */
+public class Ui {
+ private static Scanner scanner;
+ private static final String LINE_SEPARATOR = "_".repeat(50);
+
+ /**
+ * Initializes the scanner object for reading user input.
+ */
+ public Ui() {
+ scanner = new Scanner(System.in);
+ }
+
+ /**
+ * Displays all available commands for users
+ */
+ public static void displayCommands() {
+ String availableCommands = "Type help to view all the available commands";
+ System.out.println(availableCommands);
+ System.out.println(LINE_SEPARATOR);
+ }
+
+ /**
+ * Prints welcome message when users enter the app
+ * The welcome message includes the app's logo and introduction
+ */
+ public static void welcomeMessage() {
+ String logo = "FlashBang";
+ String intro = "Welcome to the FlashBang app - learning your modules through engaging flashcards";
+ System.out.println(logo + "\n" + intro);
+ System.out.println(LINE_SEPARATOR);
+ }
+
+ /**
+ * Prints a formatted response to the console with line separators.
+ *
+ * @param text The message to be printed.
+ */
+ public static void printResponse(String text) {
+ text = LINE_SEPARATOR + "\n" + text + "\n" + LINE_SEPARATOR + "\n";
+ System.out.print(text);
+ }
+
+ /**
+ * Retrieves a user's input from the console.
+ *
+ * @return The raw string input from the user.
+ */
+ public static String getRequest() {
+ System.out.print("> ");
+ return scanner.nextLine();
+ }
+
+ /**
+ * Prints the announcement to get the prompt from user
+ * @param prompt
+ */
+ public static void displayGetNewPromptFromUser(String prompt) {
+ System.out.println("Enter new "+prompt+" :");
+ }
+
+ /**
+ * Prints the confirmation question
+ * @param prompt
+ */
+ public static void displayConfirmationQuestion(String prompt) {
+ System.out.println("Do you want to change "+prompt+" (y/n):");
+ }
+
+ /**
+ * Prints the old value
+ * @param prompt
+ * @param value
+ */
+ public static void displayOldStoredValue(String prompt, String value) {
+ System.out.println("Old "+prompt+" : "+value);
+ }
+
+}
diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/FlashbangTest.java
similarity index 74%
rename from src/test/java/seedu/duke/DukeTest.java
rename to src/test/java/seedu/duke/FlashbangTest.java
index 2dda5fd651..2e0d73d3b5 100644
--- a/src/test/java/seedu/duke/DukeTest.java
+++ b/src/test/java/seedu/duke/FlashbangTest.java
@@ -4,9 +4,9 @@
import org.junit.jupiter.api.Test;
-class DukeTest {
+class FlashbangTest {
@Test
- public void sampleTest() {
+ public void flashbangTest() {
assertTrue(true);
}
}
diff --git a/src/test/java/seedu/duke/flashutils/commands/AddCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/AddCommandTest.java
new file mode 100644
index 0000000000..51ee798f63
--- /dev/null
+++ b/src/test/java/seedu/duke/flashutils/commands/AddCommandTest.java
@@ -0,0 +1,53 @@
+package seedu.duke.flashutils.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashCardSet;
+import seedu.duke.flashutils.utils.Parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AddCommandTest {
+ @Test
+ public void testAddCommandConstructor() {
+ String testQuestion = "Some question";
+ String testAnswer = "Some answer";
+ FlashCardSet testModule = new FlashCardSet("Some module");
+ AddCommand command = new AddCommand(testModule, new Card(testQuestion, testAnswer));
+
+ assertEquals(testQuestion, command.getCardToAdd().getQuestion());
+ assertEquals(testAnswer, command.getCardToAdd().getAnswer());
+ assertEquals(testModule, command.getTargetSet());
+ }
+
+ @Test public void testFlashcardActuallyAdded() {
+ FlashCardSet testModule = new FlashCardSet("Some module");
+ Card testCard = new Card("Some question", "Some answer");
+ AddCommand command = new AddCommand(testModule, testCard);
+ command.execute();
+ assertTrue(testModule.getFlashCardSet().contains(testCard));
+ }
+
+ @Test public void testAddMultipleFlashcards() {
+ FlashCardSet testModule = new FlashCardSet("Some module");
+ Card testCard1 = new Card("Question 1", "Answer 1");
+ Card testCard2 = new Card("Question 2", "Answer 2");
+ new AddCommand(testModule, testCard1).execute();
+ new AddCommand(testModule, testCard2).execute();
+ assertEquals(2, testModule.getFlashCardSet().size());
+ }
+
+ @Test
+ public void testInvalidAddCommand() {
+ String testString = "add --m --t --q --a ";
+ assertThrows(IllegalArgumentException.class, () -> Parser.parseCommand(testString).execute());
+ }
+
+ @Test public void testAddCommandNullFields() {
+ FlashCardSet testModule = new FlashCardSet("Some module");
+ assertThrows(NullPointerException.class, () -> new AddCommand(testModule, null, "Answer"));
+ assertThrows(NullPointerException.class, () -> new AddCommand(testModule, "Question", null));
+ }
+}
diff --git a/src/test/java/seedu/duke/flashutils/commands/DeleteCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/DeleteCommandTest.java
new file mode 100644
index 0000000000..d220924cdb
--- /dev/null
+++ b/src/test/java/seedu/duke/flashutils/commands/DeleteCommandTest.java
@@ -0,0 +1,53 @@
+package seedu.duke.flashutils.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashCardSet;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class DeleteCommandTest {
+ @Test
+ public void testDeleteCommandConstructor() {
+ String testQuestion = "Some question";
+ String testAnswer = "Some answer";
+ FlashCardSet testModule = new FlashCardSet("Some module");
+ Card testCard = new Card(testQuestion, testAnswer);
+ testModule.addCard(testCard);
+ DeleteCommand command = new DeleteCommand(testModule, 1);
+
+ assertEquals(testCard, command.getTargetCard());
+ assertEquals(testModule, command.getTargetSet());
+ }
+
+ @Test
+ public void testSuccessfulDeleteCommand() {
+ String testQuestion = "Some question";
+ String testAnswer = "Some answer";
+ FlashCardSet testModule = new FlashCardSet("Some module");
+ Card testCard = new Card(testQuestion, testAnswer);
+
+ testModule.addCard(testCard);
+ DeleteCommand command = new DeleteCommand(testModule, 1);
+ command.execute();
+
+ assertFalse(testModule.getFlashCardSet().contains(testCard));
+ }
+
+ @Test
+ public void testInvalidIndex() {
+ FlashCardSet testModule = new FlashCardSet("Some module");
+ Card testCard1 = new Card("Question 1", "Answer 1");
+ Card testCard2 = new Card("Question 2", "Answer 2");
+ new AddCommand(testModule, testCard1).execute();
+ new AddCommand(testModule, testCard2).execute();
+
+ assertThrows(IndexOutOfBoundsException.class, () -> new DeleteCommand(testModule, -1)
+ .execute());
+ assertThrows(IndexOutOfBoundsException.class, () -> new DeleteCommand(testModule, 3)
+ .execute());
+ }
+
+}
diff --git a/src/test/java/seedu/duke/flashutils/commands/EditCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/EditCommandTest.java
new file mode 100644
index 0000000000..0dc23b5313
--- /dev/null
+++ b/src/test/java/seedu/duke/flashutils/commands/EditCommandTest.java
@@ -0,0 +1,48 @@
+package seedu.duke.flashutils.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashCardSet;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class EditCommandTest {
+ @Test
+ public void testEditCommandConstructor() {
+ FlashCardSet testModule = new FlashCardSet("Some module");
+ testModule.addCard(new Card("Some Question", "Some Answer"));
+
+ EditCommand command = new EditCommand(testModule, 0, "New Question", "New Answer");
+
+ assertEquals("New Question", command.getNewCard().getQuestion());
+ assertEquals("New Answer", command.getNewCard().getAnswer());
+ assertEquals(testModule, command.getTargetSet());
+ }
+
+ @Test
+ public void testSuccessfulCardEditCommand() {
+ String testQuestion = "Some question";
+ String testAnswer = "Some answer";
+ String newQuestion = "New Question";
+ String newAnswer = "New Answer";
+ FlashCardSet testModule = new FlashCardSet("Some module");
+ Card testCard = new Card(testQuestion, testAnswer);
+
+ testModule.addCard(testCard);
+ EditCommand command = new EditCommand(testModule, 0, newQuestion, newAnswer);
+ command.execute();
+
+ Card editedCard = testModule.getCard(0);
+ assertEquals(newQuestion, editedCard.getQuestion());
+ assertEquals(newAnswer, editedCard.getAnswer());
+ }
+
+ @Test
+ public void testEditInvalidIndex() {
+ FlashCardSet testModule = new FlashCardSet("Some module");
+ assertThrows(IndexOutOfBoundsException.class, () ->
+ new EditCommand(testModule, -1, "New Question", "New Answer"));
+ assertThrows(IndexOutOfBoundsException.class, () ->
+ new EditCommand(testModule, 1, "New Question", "New Answer"));
+ }
+}
diff --git a/src/test/java/seedu/duke/flashutils/commands/FlashbangCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/FlashbangCommandTest.java
new file mode 100644
index 0000000000..e0c63f1b49
--- /dev/null
+++ b/src/test/java/seedu/duke/flashutils/commands/FlashbangCommandTest.java
@@ -0,0 +1,25 @@
+package seedu.duke.flashutils.commands;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.flashutils.types.FlashCardSet;
+import seedu.duke.flashutils.utils.Ui;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class FlashbangCommandTest {
+
+ @BeforeEach
+ public void setUp() {
+ String simulatedUserInput = "Some user input\n";
+ new Ui(); // Initialize the Ui instance and scanner
+ }
+
+ @Test
+ public void testFlashbangCommandConstructor() {
+ FlashCardSet testModule = new FlashCardSet("Some module");
+ FlashbangCommand command = new FlashbangCommand(testModule);
+ assertEquals(testModule, command.getTargetSet());
+ }
+
+}
diff --git a/src/test/java/seedu/duke/flashutils/commands/QuitCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/QuitCommandTest.java
new file mode 100644
index 0000000000..efef798acf
--- /dev/null
+++ b/src/test/java/seedu/duke/flashutils/commands/QuitCommandTest.java
@@ -0,0 +1,16 @@
+package seedu.duke.flashutils.commands;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class QuitCommandTest {
+
+ @Test
+ public void testQuitCommandSuccessMessage() {
+ QuitCommand command = new QuitCommand();
+ CommandResult result = command.execute();
+
+ assertEquals("Quit Flash Session", result.getFeedbackToUser());
+ }
+
+}
diff --git a/src/test/java/seedu/duke/flashutils/commands/ViewAllCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/ViewAllCommandTest.java
new file mode 100644
index 0000000000..4b0a1d6a2f
--- /dev/null
+++ b/src/test/java/seedu/duke/flashutils/commands/ViewAllCommandTest.java
@@ -0,0 +1,96 @@
+package seedu.duke.flashutils.commands;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashBook;
+import seedu.duke.flashutils.types.FlashCardSet;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ViewAllCommandTest {
+
+ private FlashCardSet module1;
+ private FlashCardSet module2;
+ private Card card1;
+ private Card card2;
+ private Card card3;
+ private FlashBook flashBook;
+
+ @BeforeEach
+ public void setUp() {
+ // Set up the flashcards and sets for testing
+ card1 = new Card("What is Java?", "A programming language.");
+ card2 = new Card("What is OOP?", "Object-Oriented Programming.");
+ card3 = new Card("What is Python?","A programming language.");
+
+ flashBook = FlashBook.getInstance();
+ flashBook.addFlashCardSet("CS1010");
+ flashBook.getFlashCardSet("CS1010").addCard(card1);
+ flashBook.getFlashCardSet("CS1010").addCard(card3);
+ flashBook.addFlashCardSet("CS2113");
+ flashBook.getFlashCardSet("CS2113").addCard(card2);
+ }
+
+ @Test
+ public void viewAllCommandTestExecute() {
+ // Test the execute method for ViewAllCommand, which should view all flashcards from all modules
+ ViewAllCommand command = new ViewAllCommand();
+ CommandResult result = command.execute();
+
+ // Check that the command result contains flashcards for both modules
+ String expectedMessage = "MODULE NAME: CS1010\n#1 -> What is Java? : A programming language.\n" +
+ "#2 -> What is Python? : A programming language.\n\n" +
+ "MODULE NAME: CS2113\n#1 -> What is OOP? : Object-Oriented Programming.";
+
+ assertEquals(expectedMessage, result.getFeedbackToUser());
+ }
+
+ @Test
+ public void viewAllCommandTestLongCardContent() {
+ // Test handling of flashcards with lengthy content
+ Card longCard = new Card("Describe the OSI model",
+ "The OSI model is a conceptual framework used to understand network interactions. " +
+ "It has 7 layers: Physical, Data Link, Network, Transport, Session, Presentation, and Application.");
+ flashBook.getFlashCardSet("CS1010").addCard(longCard);
+
+ ViewAllCommand command = new ViewAllCommand();
+ CommandResult result = command.execute();
+
+ String expectedMessage = "MODULE NAME: CS1010\n" +
+ "#1 -> What is Java? : A programming language.\n" +
+ "#2 -> What is Python? : A programming language.\n" +
+ "#3 -> Describe the OSI model : The OSI model is a conceptual framework used to understand network interactions. " +
+ "It has 7 layers: Physical, Data Link, Network, Transport, Session, Presentation, and Application.\n\n" +
+ "MODULE NAME: CS2113\n" +
+ "#1 -> What is OOP? : Object-Oriented Programming.";
+
+ assertEquals(expectedMessage, result.getFeedbackToUser());
+ }
+
+ @Test
+ public void viewAllCommandTestEmptyModule() {
+ // Test the handling of a module with no flashcards
+ flashBook.deleteFlashCardSet("CS1010");
+ ViewAllCommand command = new ViewAllCommand();
+ CommandResult result = command.execute();
+
+ String expectedMessage =
+ "MODULE NAME: CS2113\n" +
+ "#1 -> What is OOP? : Object-Oriented Programming.";
+
+ assertEquals(expectedMessage, result.getFeedbackToUser());
+ }
+
+ @Test
+ public void viewAllCommandTestEmptyBook() {
+ // Test the behavior when there are no flashcards in FlashBook
+ flashBook.deleteFlashCardSet("CS1010");
+ flashBook.deleteFlashCardSet("CS2113");// Empty the FlashBook
+ ViewAllCommand command = new ViewAllCommand();
+ CommandResult result = command.execute();
+
+ // Expect an empty result message as there are no flashcards
+ assertEquals("", result.getFeedbackToUser());
+ }
+}
diff --git a/src/test/java/seedu/duke/flashutils/commands/ViewCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/ViewCommandTest.java
new file mode 100644
index 0000000000..526c108a27
--- /dev/null
+++ b/src/test/java/seedu/duke/flashutils/commands/ViewCommandTest.java
@@ -0,0 +1,23 @@
+package seedu.duke.flashutils.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.flashutils.types.FlashCardSet;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ViewCommandTest {
+
+ @Test
+ public void viewCommandTestEmpty() {
+ FlashCardSet module1 = new FlashCardSet("Module 1");
+ assertTrue(module1.getFlashCardSet().isEmpty());
+ }
+
+ @Test
+ public void viewCommandTestConstructor() {
+ FlashCardSet module2 = new FlashCardSet("Module 2");
+ ViewCommand command = new ViewCommand(module2);
+ assertEquals(module2, command.getTargetSet());
+ }
+}
diff --git a/src/test/java/seedu/duke/flashutils/utils/ParserTest.java b/src/test/java/seedu/duke/flashutils/utils/ParserTest.java
new file mode 100644
index 0000000000..78a0bd3ec2
--- /dev/null
+++ b/src/test/java/seedu/duke/flashutils/utils/ParserTest.java
@@ -0,0 +1,70 @@
+package seedu.duke.flashutils.utils;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.flashutils.commands.AddCommand;
+import seedu.duke.flashutils.commands.Command;
+import seedu.duke.flashutils.commands.FlashbangCommand;
+import seedu.duke.flashutils.commands.InvalidCommand;
+import seedu.duke.flashutils.commands.QuitCommand;
+import seedu.duke.flashutils.commands.SearchCommand;
+import seedu.duke.flashutils.commands.ViewCommand;
+
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ParserTest {
+
+ @Test
+ public void testParseAddCommand() {
+ String input = "add --m SampleModule --q What is Java? --a A programming language.";
+ Command command = Parser.parseCommand(input);
+ assertInstanceOf(AddCommand.class, command);
+
+ AddCommand addCommand = (AddCommand) command;
+ assertEquals("SampleModule", addCommand.getTargetSet().getModuleName());
+ assertEquals("What is Java?", addCommand.getCardToAdd().getQuestion());
+ assertEquals("A programming language.", addCommand.getCardToAdd().getAnswer());
+ }
+
+ @Test
+ public void testParseViewCommand() {
+ String input = "view --m SampleModule";
+ Command command = Parser.parseCommand(input);
+ assertInstanceOf(ViewCommand.class, command);
+
+ ViewCommand viewCommand = (ViewCommand) command;
+ assertEquals("SampleModule", viewCommand.getTargetSet().getModuleName());
+ }
+
+ @Test
+ public void testParseFlashbangCommand() {
+ String input = "flashbang --m SampleModule --t 100";
+ Command command = Parser.parseCommand(input);
+ assertInstanceOf(FlashbangCommand.class, command);
+
+ FlashbangCommand flashbangCommand = (FlashbangCommand) command;
+ assertEquals("SampleModule", flashbangCommand.getTargetSet().getModuleName());
+ }
+
+ @Test
+ public void testParseQuitCommand() {
+ String input = "quit";
+ Command command = Parser.parseCommand(input);
+ assertInstanceOf(QuitCommand.class, command);
+ }
+
+ @Test
+ public void testParseInvalidCommand() {
+ String input = "invalid --m SampleModule";
+ Command command = Parser.parseCommand(input);
+ assertInstanceOf(InvalidCommand.class, command);
+ }
+
+ @Test
+ public void testParseSearchCommand() {
+ String input = "search --m SampleModule --s SearchTerm";
+ Command command = Parser.parseCommand(input);
+ assertInstanceOf(SearchCommand.class, command);
+
+ }
+}
diff --git a/src/test/java/seedu/duke/flashutils/utils/StorageTest.java b/src/test/java/seedu/duke/flashutils/utils/StorageTest.java
new file mode 100644
index 0000000000..e83d9b6219
--- /dev/null
+++ b/src/test/java/seedu/duke/flashutils/utils/StorageTest.java
@@ -0,0 +1,99 @@
+package seedu.duke.flashutils.utils;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.flashutils.types.Card;
+import seedu.duke.flashutils.types.FlashBook;
+import seedu.duke.flashutils.types.FlashCardSet;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Scanner;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class StorageTest {
+ private final String directoryPath = "./data";
+ private File testFile;
+ private Storage storage;
+
+ @BeforeEach
+ public void createFile() {
+ this.storage = new Storage(directoryPath);
+ }
+
+ @Test
+ void writeAndReadFlashCardsTest() {
+ FlashBook flashBook = FlashBook.getInstance();
+ flashBook.addFlashCardSet("FunModule");
+ FlashCardSet testSet = flashBook.getFlashCardSet("FunModule");
+ testSet.addCard(new Card("is water wet?", "no?", "wetness"));
+ storage.writeFlashBookToFile(flashBook);
+ testFile = new File(directoryPath +"/FunModule.txt");
+ assertTrue(testFile.exists());
+ Scanner scanner;
+ try {
+ scanner = new Scanner(testFile);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ assertEquals("is water wet? | no? | wetness", scanner.nextLine());
+ HashMap testBook;
+ try {
+ testBook = storage.readFlashCardsFromFile();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ FlashCardSet testSet2 = testBook.get("FunModule");
+ FlashCardSet actualSet = flashBook.getFlashCardSet("FunModule");
+ assertTrue(testSet2.getCard(0).getAnswer().equals(actualSet.getCard(0).getAnswer())
+ && testSet2.getCard(0).getQuestion().equals(actualSet.getCard(0).getQuestion()));
+ scanner.close();
+ }
+
+ private class StubCard extends Card {
+ public StubCard(Card card) {
+ super(card.getQuestion(), card.getAnswer(), card.getTopic());
+ }
+ public boolean isEqual(Card card) {
+ return this.getQuestion().equals(card.getQuestion())
+ && this.getAnswer().equals(card.getAnswer())
+ && this.getTopic().equals(card.getTopic());
+ }
+ }
+ @Test
+ public void readAndWriteMultipleCards() throws IOException {
+ FlashCardSet set1 = new FlashCardSet("CS2113");
+ set1.addCard(new Card("question1", "answer1", "topic1"));
+ set1.addCard(new Card("question2", "answer2", "topic2"));
+ set1.addCard(new Card("question3", "answer3", "topic3"));
+ set1.addCard(new Card("question4", "answer4", "topic4"));
+ ArrayList testList = new ArrayList<>(set1.getFlashCardSet());
+ FlashBook testBook = FlashBook.getInstance();
+ testBook.getAllFlashCardSets().put(set1.getModuleName(), set1);
+ storage.writeFlashBookToFile(testBook);
+ testFile = new File(directoryPath + "/CS2113.txt");
+ assertTrue(testFile.exists());
+ HashMap actualMap = storage.readFlashCardsFromFile();
+ FlashCardSet actualList = actualMap.get("CS2113");
+ for (int i = 0; i < testList.size(); i++) {
+ StubCard actualCard = new StubCard(actualList.getCard(i));
+ StubCard expectedCard = new StubCard(testList.get(i));
+ assertTrue(actualCard.isEqual(expectedCard));
+ }
+ }
+ @AfterEach
+ public void cleanFile() throws IOException {
+ if (Files.deleteIfExists(testFile.toPath())) {
+ System.out.println("file deleted successfully");
+ } else {
+ System.out.println("error");
+ }
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..e69de29bb2 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +0,0 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
-What is your name?
-Hello James Gosling