diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000000..c5f3f6b9c7
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "java.configuration.updateBuildConfiguration": "interactive"
+}
\ No newline at end of file
diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..29dec0c3c2
--- /dev/null
+++ b/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: seedu.duke.FlirtFork.Duke
+
diff --git a/README.md b/README.md
index f82e2494b7..d8972cf13c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Duke project template
+# Flirt & Fork
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..82e1286428 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,11 +29,11 @@ test {
}
application {
- mainClass.set("seedu.duke.Duke")
+ mainClass.set("seedu.flirtfork.FlirtFork")
}
shadowJar {
- archiveBaseName.set("duke")
+ archiveBaseName.set("Flirt_and_Fork")
archiveClassifier.set("")
}
@@ -43,4 +43,17 @@ checkstyle {
run{
standardInput = System.in
+ enableAssertions = true
}
+
+apply plugin: 'jacoco'
+jacoco {
+ toolVersion = "0.8.5"
+}
+jacocoTestReport {
+ reports {
+ xml.enabled = true
+ csv.enabled = false
+ html.enabled = true
+ }
+}
\ No newline at end of file
diff --git a/data/ActivityList.txt b/data/ActivityList.txt
new file mode 100644
index 0000000000..6c431305d1
--- /dev/null
+++ b/data/ActivityList.txt
@@ -0,0 +1,322 @@
+Botanic Garden Tours | C | C | U
+Gardens by the Bay | C | C | U
+Kampong Glam Cultural Exploration | C | C | U
+Little India Heritage Walk | C | C | U
+Bugis Street Market | C | C | U
+Esplanade Free Concerts | C | C | U
+National Library Visits | C | C | U
+St Andrew's Cathedral Tour | C | C | U
+Singapore River Walk | C | C | U
+Clarke Quay Nightlife | C | B | U
+Bras Basah Complex Art Galleries | C | B | U
+National Museum of Singapore Visit | C | B | U
+Fort Canning Hill Hike | C | B | U
+Stamford Arts Centre Performances | C | B | U
+Raffles Hotel Historical Tour | C | B | U
+Chinatown Food Adventure | C | B | U
+Marina Bay Sands Light and Water Show | C | B | U
+Singapore River Cruise | C | B | U
+Haw Par Villa | C | B | U
+Mount Faber Cable Car Ride | C | B | U
+Orchard Road Shopping Experience | C | A | U
+Merlion Park Visit | C | A | U
+Chinatown Heritage Centre | C | A | U
+Duck Tours at Marina Bay | C | A | U
+Suntec City Shopping | C | A | U
+Peranakan Museum Discovery | C | A | U
+Haji Lane Shopping | C | A | U
+Fort Canning Park Archaeological Dig and Historical Tour | C | A | U
+Museum of Ice Cream | C | A | U
+Katong Antique House Visit | C | A | U
+Katong Food Walk | C | A | U
+Singapore Cable Car Dining | C | P | U
+National Gallery Singapore Exclusive Tour | C | P | U
+Victoria Theatre Concert Night | C | P | U
+Singapore River Fine Dining Cruise | C | P | U
+Helix Bridge Evening Walk | C | P | U
+MBS Casino Experience | C | P | U
+Singapore Flyer | C | P | U
+Universal Studios Singapore | C | P | U
+Singapore Zoo Night Safari | C | P | U
+Marina Bay Sands SkyPark | C | P | U
+Private Luxury Boat Charter at Marina Bay | C | S | U
+VIP Shopping Experience on Orchard Road | C | S | U
+Esplanade Theatres VIP Show Tickets | C | S | U
+Private Helicopter Tour over Singapore | C | S | U
+Exclusive Ritz-Carlton Spa Day | C | S | U
+MBS Skypark Infinity Pool Access | C | S | U
+Marina Bay Sands Skypark Observation Deck | C | S | U
+Marina Bay Sands Infinity Pool | C | S | U
+ArtScience Museum | C | S | U
+Marina Bay Sands Shopping | C | S | U
+Gardens by the Bay | C | S | U
+East Coast Lagoon Food Village | E | C | U
+Pasir Ris Park Mangrove Walk | E | C | U
+Bedok Reservoir Park | E | C | U
+Tampines Eco Green Tour | E | C | U
+Siglap Canal Graffiti Art Walk | E | C | U
+Changi Point Coastal Boardwalk | E | C | U
+Jewel Changi Airport Exploration | E | C | U
+Peranakan Culture Exploration in Joo Chiat | E | C | U
+Pulau Ubin Adventure | E | C | U
+Marina Barrage Kite Flying | E | C | U
+Punggol Waterway Park | E | C | U
+Simei Street Art Trail | E | B | U
+Paya Lebar Square Shopping | E | B | U
+Changi Village Hawker Centre Food Trip | E | B | U
+Tanah Merah Country Club Golf | E | B | U
+Bedok Jetty Fishing Experience | E | B | U
+Pasir Ris Beach Park BBQ | E | B | U
+Haw Par Villa | E | B | U
+Katong Laksa Tasting | E | B | U
+East Coast Park Cycling | E | B | U
+Coney Island Park Exploration | E | B | U
+Katong Antique House Visit | E | A | U
+Changi Airport Jewel Exploration | E | A | U
+East Coast Park BBQ Party | E | A | U
+Laguna National Golf & Country Club | E | A | U
+Tampines Hub Sports Facilities | E | A | U
+Singapore Wake Park | E | A | U
+Chek Jawa Wetlands Tour | E | A | U
+Katong Food Walk | E | A | U
+Changi Museum Visit | E | A | U
+Punggol Promenade Nature Walk | E | A | U
+Cycling at Punggol Waterway | E | A | U
+Jewel Changi Airport Canopy Park | E | P | U
+Wild Wild Wet VIP Package | E | P | U
+Aloha Sea Sports Centre Windsurfing | E | P | U
+D'Resort Rainforest Suite Stay | E | P | U
+Tanjong Beach Club Day Pass | E | P | U
+Changi Cove Conference Centre Retreat | E | P | U
+Ferry Ride to Pulau Ubin with Private Guide | E | P | U
+Changi Experience Studio | E | P | U
+Marina Bay Sands Skypark Observation Deck | E | P | U
+Marina Bay Sands Casino | E | P | U
+Jewel Changi Airport Canopy Park | E | P | U
+Marina Bay Sands Infinity Pool | E | S | U
+Marina Bay Sands Shopping | E | S | U
+Marina Bay Sands Helicopter Tour | E | S | U
+Pulau Ubin Adventure | E | S | U
+Punggol Waterway Park | E | S | U
+Exclusive Jewel Changi Airport Tour | E | S | U
+Private Yacht Charter from Changi | E | S | U
+VIP Sky Dining on Singapore Cable Car | E | S | U
+Premium Seafood Dinner at East Coast | E | S | U
+Ultimate Pulau Ubin Cycling Adventure | E | S | U
+Luxurious Spa Day at Capri by Fraser | E | S | U
+Clementi Woods Park Nature Walk | W | C | U
+Bukit Batok Nature Park Hiking | W | C | U
+Jurong Lake Gardens Picnic | W | C | U
+West Coast Park Playground Adventure | W | C | U
+Chinese Garden Leisure Visit | W | C | U
+Science Centre Observatory Stargazing | W | C | U
+Singapore Botanic Gardens | W | C | U
+Jurong Bird Park | W | C | U
+Singapore Science Centre | W | C | U
+Henderson Waves Bridge Walk | W | C | U
+Singapore Botanic Gardens | W | C | U
+West Coast Park Picnic | W | B | U
+IMM Outlet Mall Shopping | W | B | U
+Jurong Bird Park Basic Tour | W | B | U
+Sungei Buloh Wetland Reserve Visit | W | B | U
+Westgate Shopping Spree | W | B | U
+Snow City Singapore Fun Visit | W | B | U
+Army Museum of Singapore Exploration | W | B | U
+Hawker Centre Hopping in Jurong | W | B | U
+JCube Ice Skating | W | B | U
+Haw Par Villa | W | B | U
+Mount Faber Cable Car Ride | W | B | U
+Jurong East Swimming Complex Day Out | W | A | U
+JCube Ice Skating Session | W | A | U
+Chinese and Japanese Gardens Guided Tour | W | A | U
+Adventure Cove Waterpark Standard Access | W | A | U
+Lee Kong Chian Natural History Museum Visit | W | A | U
+The Rink Ice Skating | W | A | U
+Jurong Lake Gardens Cycling | W | A | U
+Haw Par Villa | W | A | U
+Jurong East Swimming Complex | W | A | U
+Jurong Hill Lookout Tower | W | A | U
+Jurong Eco-Garden | W | A | U
+Singapore Discovery Centre Premium Experience | W | P | U
+River Safari Exclusive Tour | W | P | U
+Jurong Bird Park Private Encounter | W | P | U
+Omni-Theatre Immersive Experience | W | P | U
+The Live Turtle and Tortoise Museum Private Visit | W | P | U
+Jem Shopping and Dining Experience | W | P | U
+Jurong Hill Lookout Tower | W | P | U
+Jem Shopping Mall | W | P | U
+West Coast Park Sunset Viewing | W | P | U
+Orchard Road Shopping | W | P | U
+VivoCity Shopping | W | P | U
+Night Safari VIP Tour | W | S | U
+Jurong Bird Park Behind-the-Scenes Experience | W | S | U
+River Safari Amazon River Quest Private Boat | W | S | U
+Science Centre Singapore VIP Tour | W | S | U
+The Rink Exclusive Ice Skating Party | W | S | U
+Luxury Spa Retreat at Genting Hotel Jurong | W | S | U
+Jurong Eco-Garden | W | S | U
+Marina Bay Sands Infinity Pool | W | S | U
+Marina Bay Sands Skypark Observation Deck | W | S | U
+West Coast Park Sunset Viewing | W | S | U
+Henderson Waves Bridge Walk | W | S | U
+Henderson Waves Bridge Walk | S | C | U
+Labrador Park Nature Walk | S | C | U
+Sentosa Beach Leisure Day | S | C | U
+Mount Faber Park Hiking | S | C | U
+VivoCity Waterfront Promenade | S | C | U
+Sentosa Nature Discovery Trail | S | C | U
+Fort Siloso Skywalk Visit | S | C | U
+Southern Ridges Walk | S | C | U
+Sentosa Merlion | S | C | U
+Marina Barrage Picnic | S | C | U
+Gardens by the Bay | S | C | U
+Madame Tussauds Singapore Basic Tour | S | B | U
+Sentosa Boardwalk Experience | S | B | U
+Mega Adventure Park Basic Package | S | B | U
+Sentosa 4D AdventureLand Standard Ticket | S | B | U
+Trick Eye Museum Visit | S | B | U
+Sentosa Merlion Basic Visit | S | B | U
+Haw Par Villa | S | B | U
+Mount Faber Cable Car Ride | S | B | U
+Resorts World Sentosa Adventure Cove Waterpark | S | B | U
+Sentosa Skyline Luge | S | B | U
+Underwater World | S | B | U
+Resorts World Sentosa Adventure Cove Waterpark | S | A | U
+Sentosa Cable Car Standard Ride | S | A | U
+Adventure Cove Waterpark Full Access | S | A | U
+Wings of Time Show Standard Seating | S | A | U
+Sentosa Island Tour | S | A | U
+Siloso Beach Bars Evening Out | S | A | U
+S.E.A. Aquarium Standard Entry | S | A | U
+Siloso Beach Party | S | A | U
+Sentosa Island Beach Clubs | S | A | U
+Marina Bay Sands Fine Dining | S | A | U
+Siloso Beach Party | S | A | U
+Marina Bay Sands Skating Rink | S | P | U
+Universal Studios Singapore Express Pass | S | P | U
+Sentosa Luxury Yacht Charter | S | P | U
+Capella Singapore Spa Day | S | P | U
+Sentosa Golf Club Green Fees | S | P | U
+Quayside Isle Fine Dining Experience | S | P | U
+Dolphin Island Exclusive Encounter | S | P | U
+S.E.A. Aquarium | S | P | U
+Underwater World | S | P | U
+Marina Bay Sands Casino | S | P | U
+Singapore Flyer Dining | S | P | U
+iFly Singapore Indoor Skydiving | S | S | U
+Skyline Luge Sentosa | S | S | U
+AJ Hackett Sentosa Bungee Jump | S | S | U
+Marina Bay Sands Infinity Pool | S | S | U
+Sentosa Private Beach Villa Stay | S | S | U
+Universal Studios Singapore VIP Tour | S | S | U
+S.E.A. Aquarium VIP Tour with Marine Biologist | S | S | U
+Sentosa Sunset Dinner Cruise | S | S | U
+Capella Singapore Exclusive Dining | S | S | U
+Ola Beach Club Private Party | S | S | U
+Skydiving | S | S | U
+Punggol Waterfront Park Picnic | NE | C | U
+Lorong Halus Wetland Bird Watching | NE | C | U
+Coney Island Nature Walk | NE | C | U
+Punggol Jetty Fishing | NE | C | U
+Sengkang Riverside Park Visit | NE | C | U
+Punggol Promenade Riverside Walk | NE | C | U
+Punggol Waterway Park | NE | C | U
+Punggol Settlement Seafood Dining | NE | C | U
+Coney Island Park Exploration | NE | C | U
+Lorong Halus Wetland Park Bird Watching | NE | C | U
+Punggol Waterway Park | NE | C | U
+Seletar Aerospace Park Viewing | NE | B | U
+Punggol Container Park Shopping | NE | B | U
+Waterway Point Shopping Spree | NE | B | U
+Compass One Mall Experience | NE | B | U
+Punggol Point Horse Riding | NE | B | U
+Oasis Terraces Community Plaza | NE | B | U
+Punggol Waterway Park Kayaking | NE | B | U
+Windsurfing in Punggol | NE | B | U
+Lorong Halus Wetland Park Bird Watching | NE | B | U
+Punggol Jetty Sunset Viewing | NE | B | U
+Lorong Halus Bridge Cycling | NE | B | U
+Punggol Promenade Nature Walk | NE | A | U
+Punggol Safra Adventure Sports | NE | A | U
+North Eastern Riverine Loop Cycling | NE | A | U
+The Animal Resort Visit | NE | A | U
+Seletar Country Club Golfing | NE | A | U
+Coney Island Guided Exploration | NE | A | U
+Punggol Waterway Park Kayaking | NE | A | U
+Punggol Container Park Shopping | NE | A | U
+Cycling at Punggol Waterway | NE | A | U
+Pulau Ubin Adventure | NE | A | U
+Coney Island Park Glamping | NE | A | U
+Jewel Changi Airport Canopy Park | NE | P | U
+Orchid Country Club Golf and Dine | NE | P | U
+Seletar Aerospace Park Private Tour | NE | P | U
+The Seletar Mall Exclusive Shopping | NE | P | U
+Punggol Marina Country Club Yacht Charter | NE | P | U
+Coney Island Luxury Glamping | NE | P | U
+Sengkang Sports Complex Private Pool Booking | NE | P | U
+Hai Bin Prawning | NE | P | U
+Changi Experience Studio | NE | P | U
+Marina Bay Sands SkyPark | NE | P | U
+Jewel Changi Airport Canopy Park | NE | P | U
+Marina Bay Sands Skypark Observation Deck | NE | S | U
+Yacht Party at Punggol Marina | NE | S | U
+Seletar Airbase Helicopter Tour | NE | S | U
+Exclusive Dining at The Summerhouse | NE | S | U
+Private Guided Tour of Coney Island | NE | S | U
+Punggol Point Sunset Yacht Dinner | NE | S | U
+Seletar Country Club VIP Golf Experience | NE | S | U
+Marina Bay Sands Shopping | NE | S | U
+Marina Bay Sands Helicopter Tour | NE | S | U
+Pulau Ubin Adventure | NE | S | U
+Punggol Waterway Park | NE | S | U
+Cultural Shows | ACC | C | U
+Interactive Workshops | ACC | C | U
+Local Community Centre Workshops | ACC | C | U
+Public Library Storytelling Sessions | ACC | C | U
+Heartland Mall Bargain Hunting | ACC | C | U
+Public Park Yoga Sessions | ACC | C | U
+HDB Estate Heritage Walks | ACC | C | U
+Community Farm Visits | ACC | C | U
+Park Visits | ACC | C | U
+Street Food Sampling | ACC | C | U
+Cultural Shows | ACC | B | U
+Interactive Workshops | ACC | B | U
+Neighbourhood Food Trail | ACC | B | U
+Urban Farming Workshops | ACC | B | U
+Local Art Galleries Visit | ACC | B | U
+Community Sports Hub Activities | ACC | B | U
+Heartland Shopping Complexes | ACC | B | U
+Street Performance Watching | ACC | B | U
+Park Visits | ACC | B | U
+Street Food Sampling | ACC | B | U
+Beach Outings | ACC | A | U
+Historical Landmark Visits | ACC | A | U
+Guided City Tours | ACC | A | U
+Public Swimming Complex Access | ACC | A | U
+Artisan Market Shopping | ACC | A | U
+Local Theatre Performances | ACC | A | U
+Urban Sketching Sessions | ACC | A | U
+Community Garden Tours | ACC | A | U
+Local Cooking Class Experience | ACC | A | U
+Museum Trips | ACC | A | U
+Private Guided Tours | ACC | P | U
+Private Art Workshop | ACC | P | U
+Exclusive Culinary Tours | ACC | P | U
+Personal Fitness Training Session | ACC | P | U
+Private Music Concerts | ACC | P | U
+Luxury Spa Treatment in Heartland | ACC | P | U
+High-End Shopping Experience in Local Boutiques | ACC | P | U
+Local Theatre Performances | ACC | P | U
+River Cruise | ACC | P | U
+Spa Treatment | ACC | P | U
+Helicopter Tour | ACC | S | U
+Adventure Sports | ACC | S | U
+Yacht Charter | ACC | S | U
+VIP Event | ACC | S | U
+Helicopter City Tour | ACC | S | U
+Exclusive Private Chef Dining Experience | ACC | S | U
+Luxury Yacht Charter from Local Marina | ACC | S | U
+VIP Access to Local Festivals | ACC | S | U
+Bespoke Tailoring Experience | ACC | S | U
+Private Gallery Viewing and Art Auction | ACC | S | U
+Paragliding | C | C | U
diff --git a/data/Favourites.txt b/data/Favourites.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/data/FoodList.txt b/data/FoodList.txt
new file mode 100644
index 0000000000..ab6fa77c7a
--- /dev/null
+++ b/data/FoodList.txt
@@ -0,0 +1,309 @@
+25 Degrees | C | C | W | U
+49 Seats | C | C | W | U
+888 Mookata | C | C | T | U
+Paddy's Patties | C | C | W | U
+Urban Dumpling | C | C | C | U
+Bangkok Street | C | C | T | U
+Seoul Bites | C | C | K | U
+Rome's Delight | C | C | I | U
+Madrid Tapas | C | C | S | U
+Miso Good Ramen | C | B | J | U
+Curry In A Hurry | C | B | I | U
+Spicy Sichuan | C | B | C | U
+Tom Yum Goong | C | B | T | U
+Nasi Padang | C | B | F | U
+The American Diner | C | B | W | U
+90 Minutes | C | B | K | U
+A9 Noodles and Dumplings | C | B | C | U
+Aburi Kaisen Don Keisuke | C | B | J | U
+Bistro Belle | C | A | F | U
+Sushi Samba | C | A | J | U
+Kimchi Palace | C | A | K | U
+Napoli Nights | C | A | I | U
+Chiang Mai Kitchen | C | A | T | U
+Beijing Bites | C | A | C | U
+Five Guys | C | A | W | U
+Hantol | C | A | K | U
+Swee Choon | C | A | C | U
+Osteria Mozza | C | P | I | U
+Sashimi House | C | P | J | U
+Tapas y Vino | C | P | S | U
+Gourmet Burger Kitchen | C | P | W | U
+Bangkok High | C | P | T | U
+Seoul Fine Dining | C | P | K | U
+Wano Niku | C | P | J | U
+Super Thai Mookata | C | P | T | U
+Cicheti | C | P | I | U
+La Trattoria | C | S | I | U
+Nobu Deluxe | C | S | J | U
+The Royal Thai | C | S | T | U
+French Riviera | C | S | F | U
+Emperor's Feast | C | S | C | U
+Chateau Gourmet | C | S | W | U
+Ikura Japanese | C | S | J | U
+R.K Eating House | C | S | I | U
+Tolido's Expresso Nook | C | S | W | U
+East End Eatery | E | C | W | U
+Wok This Way | E | C | C | U
+Pad Thai Corner | E | C | T | U
+Kim's Kimchi | E | C | K | U
+Little Italy | E | C | I | U
+Tapas Town | E | C | S | U
+886 Bistro | E | C | C | U
+Al Jasra | E | C | I | U
+Breakfast Grill | E | C | W | U
+Noodle Nirvana | E | B | J | U
+Curry Corner | E | B | I | U
+Spicy Dragon | E | B | C | U
+Thai Terrace | E | B | T | U
+East-West Fusion | E | B | F | U
+Classic Diner | E | B | W | U
+Charlie's Corner | E | B | W | U
+E&D Co | E | B | W | U
+Ha-Jun | E | B | K | U
+East Side Bistro | E | A | F | U
+Sushi East | E | A | J | U
+Kimchi Kitchen | E | A | K | U
+Pizzeria Etna | E | A | I | U
+Thai Garden | E | A | T | U
+Sichuan Spot | E | A | C | U
+Missus | E | A | W | U
+Mr Yakiniku | E | A | J | U
+Five Oars | E | A | W | U
+Italian Indulgence | E | P | I | U
+Sushi Spectacular | E | P | J | U
+Spanish Fiesta | E | P | S | U
+Gourmet Grille | E | P | W | U
+Thai Elite | E | P | T | U
+Korean Kuisine | E | P | K | U
+Knead To Eat | E | P | W | U
+Craze Kitchen | E | P | I | U
+Neptune | E | P | I | U
+La Dolce Vita | E | S | I | U
+Japonais Elite | E | S | J | U
+Thai Treasures | E | S | T | U
+Fusion Fine Dining | E | S | F | U
+Chinese Emperor | E | S | C | U
+West End Wonder | E | S | W | U
+Supernova | E | S | W | U
+The Sushi Bar | E | S | J | U
+The Basil Inn | E | S | T | U
+Westside Wraps | W | C | W | U
+Chuan Chuan | W | C | C | U
+Noodle Boat | W | C | T | U
+Seoul Food | W | C | K | U
+Pizza Piazza | W | C | I | U
+Tapas on the Terrace | W | C | S | U
+49 Seats | W | C | W | U
+888 Mookata | W | C | T | U
+Ramen Road | W | B | J | U
+Spaghetti Junction | W | B | I | U
+Dragon Noodle House | W | B | C | U
+Tasty Thai | W | B | T | U
+Fusion Feast | W | B | F | U
+Westside Diner | W | B | W | U
+West Garden Bistro | W | A | F | U
+Sashimi Spot | W | A | J | U
+Kimchi Corner | W | A | K | U
+Trattoria West | W | A | I | U
+Thai Spice | W | A | T | U
+Sichuan House | W | A | C | U
+West End Italiano | W | P | I | U
+Sushi Select | W | P | J | U
+Spanish Square | W | P | S | U
+Gourmet Burger Bistro | W | P | W | U
+Thai Elegance | W | P | T | U
+Korean Cuisine Palace | W | P | K | U
+Italian Eleganza | W | S | I | U
+Japanese Fine Dining | W | S | J | U
+Royal Thai Cuisine | W | S | T | U
+Westside Gourmet | W | S | F | U
+Emperor's Choice | W | S | C | U
+Prime Grill | W | S | W | U
+Southern Bites | S | C | W | U
+Little China | S | C | C | U
+Thai Quick | S | C | T | U
+Seoul Street | S | C | K | U
+Italiano Rapido | S | C | I | U
+Spanish Snacks | S | C | S | U
+46 Mittsu | S | C | W | U
+Abundance | S | C | C | U
+Hwang Shi | S | C | C | U
+Southside Ramen | S | B | J | U
+Mama Mia's | S | B | I | U
+Dragon Delights | S | B | C | U
+Thai Corner | S | B | T | U
+Fusion Foods | S | B | F | U
+Southern Diner | S | B | W | U
+Gyunion | S | B | J | U
+Gyu San | S | B | J | U
+Hamburg Steak | S | B | J | U
+Southern Charm Bistro | S | A | F | U
+Sushi South | S | A | J | U
+Kimchi Kitchen South | S | A | K | U
+Italian Bites | S | A | I | U
+Thai Bistro | S | A | T | U
+Szechuan South | S | A | C | U
+Hantoi | S | A | K | U
+Burger Labo | S | A | W | U
+Old Habits Cafe | S | A | W | U
+South End Italian | S | P | I | U
+Sushi Scene | S | P | J | U
+Southern Spain | S | P | S | U
+Gourmet Southern Grill | S | P | W | U
+Thai Fine Dining | S | P | T | U
+Southern Korean Delights | S | P | K | U
+Publico | S | P | I | U
+Pura Barsa | S | P | S | U
+Marcy's | S | P | I | U
+Italian Excellence | S | S | I | U
+Sushi Extravaganza | S | S | J | U
+Thai Royal Court | S | S | T | U
+Southern Fusion | S | S | F | U
+Chinese Imperial | S | S | C | U
+Southside Steakhouse | S | S | W | U
+Lau Pa Sat | S | S | F | U
+Potato Head | S | S | W | U
+Northeast Nibbles | NE | C | W | U
+Dragon's Den | NE | C | C | U
+Thai Express | NE | C | T | U
+Kim's Korean | NE | C | K | U
+Quick Italiano | NE | C | I | U
+Spanish Snack Shack | NE | C | S | U
+Fuel X | NE | C | W | U
+Samwitch | NE | C | W | U
+Breakfast Club | NE | C | W | U
+Ramen Retreat | NE | B | J | U
+Italian Fast Feast | NE | B | I | U
+China Bowl | NE | B | C | U
+Thai Taste | NE | B | T | U
+Northeast Fusion | NE | B | F | U
+Northeast Diner | NE | B | W | U
+Whisk and Paddle | NE | B | W | U
+Tend Bistro | NE | B | J | U
+Dai Lou | NE | B | C | U
+NE Bistro | NE | A | F | U
+Sushi Spot | NE | A | J | U
+Korean Kitchen | NE | A | K | U
+NE Italian | NE | A | I | U
+Thai Terrace | NE | A | T | U
+Sichuan | NE | A | C | U
+Riang | NE | A | W | U
+Ishiro | NE | A | J | U
+Folk Yard | NE | A | W | U
+Italian Indulgence | NE | P | I | U
+Sushi Sensation | NE | P | J | U
+Spanish Serenade | NE | P | S | U
+Gourmet Grill | NE | P | W | U
+Thai Delight | NE | P | T | U
+Korean Kuisine | NE | P | K | U
+Tie Fun Wan | NE | P | C | U
+Gusta Sourdough Pizza Co | NE | P | I | U
+Curry Otoko Donburi | NE | P | J | U
+Italian Elite | NE | S | I | U
+Japanese Journey | NE | S | J | U
+Thai Royal | NE | S | T | U
+NE Gourmet Fusion | NE | S | F | U
+Chinese Imperial | NE | S | C | U
+NE Steakhouse | NE | S | W | U
+Shabu-GO | NE | S | J | U
+Idaten Udon | NE | S | J | U
+Moments | NE | S | W | U
+Accessible Eats | ACC | C | W | U
+Panda Express | ACC | C | C | U
+Thai Quick Bites | ACC | C | T | U
+Kim's Kitchenette | ACC | C | K | U
+Fast Italian | ACC | C | I | U
+Spanish Street Food | ACC | C | S | U
+Aburi-En | ACC | C | J | U
+Genki Sushi | ACC | C | J | U
+Makisan | ACC | C | J | U
+Ramen Ready | ACC | B | J | U
+Italian Express | ACC | B | I | U
+China Chow | ACC | B | C | U
+Thai Treats | ACC | B | T | U
+Fusion Fast | ACC | B | F | U
+Accessible Diner | ACC | B | W | U
+Lenu | ACC | B | C | U
+A&W | ACC | B | W | U
+Monster Curry | ACC | B | J | U
+All-around Bistro | ACC | A | F | U
+Sushi Stop | ACC | A | J | U
+Quick Korean | ACC | A | K | U
+Easy Italian | ACC | A | I | U
+Thai Table | ACC | A | T | U
+Sichuan Station | ACC | A | C | U
+Pepperlunch | ACC | A | J | U
+Crystal Jade | C | P | C | U
+Shi Li Fang | ACC | A | C | U
+Soup Spoon | ACC | A | W | U
+Italian Impressions | ACC | P | I | U
+Sushi Supreme | ACC | P | J | U
+Spanish Splendor | ACC | P | S | U
+Gourmet Galore | ACC | P | W | U
+Thai Top Table | ACC | P | T | U
+Korean Cuisine Classics | ACC | P | K | U
+Green Dot | ACC | P | W | U
+Hai Di Lao | ACC | P | C | U
+Poulet | ACC | P | W | U
+Gochi-so Shokudo | ACC | S | J | U
+Ichiban Sushi | ACC | S | J | U
+Watami Japanese Dining | ACC | S | J | U
+The West Wing | W | P | W | U
+YC Dining and Bar | W | P | W | U
+Curry House | ACC | C | I | U
+Ocean's Catch | NE | B | S | U
+Pasta Pronto | W | A | I | U
+Spicy Sichuan | E | B | C | U
+Luxury Sushi | S | P | J | U
+World Fusion | ACC | A | F | U
+Burger Joint | ACC | B | W | U
+Noodle House | NE | A | C | U
+Pizza Place | S | B | I | U
+Steak Heaven | W | P | W | U
+Tapas Bar | ACC | A | S | U
+BBQ Smokehouse | C | A | W | U
+Dim Sum Corner | NE | B | C | U
+Gelato Spot | ACC | B | I | U
+Ramen Retreat | ACC | A | J | U
+Tea House | E | C | C | U
+Tortilla Tavern | W | C | S | U
+Bamboo Basket | E | C | C | U
+Roma Pizzeria | S | B | I | U
+Tropical Fusion | NE | B | F | U
+East Sushi Stop | NE | A | J | U
+Mediterranean Mosaic | C | P | F | U
+Nordic Bites | W | A | W | U
+Southern Spice | S | B | I | U
+Pan-Asian Diner | C | C | F | U
+Mountain Kimchi | W | B | K | U
+Baguette & Brie | S | P | W | U
+Dragon Noodle House | NE | A | C | U
+Urban Tandoori | C | A | I | U
+Paella Pavilion | W | P | S | U
+Curry in a Hurry | E | C | I | U
+Tempura Towers | S | B | J | U
+Pasta Port | NE | C | I | U
+Goulash Garden | C | A | W | U
+Bulgogi BBQ | ACC | B | K | U
+Saffron Lounge | W | S | I | U
+Tapas Tienda | NE | P | S | U
+Zen Zen Sushi | ACC | S | J | U
+Oodles of Noodles | E | A | C | U
+Valentino | C | P | I | U
+Kimchi Kingdom | NE | A | K | U
+FOC Sentosa | E | B | S | U
+Tim Ho Wan | ACC | C | C | U
+Tex-Mex Grill | NE | B | W | U
+Dumpling Darlings | W | C | C | U
+Lemongrass Lane | C | A | T | U
+Ichiban Boshi | E | P | J | U
+Flamenco Flavors | NE | P | S | U
+Din Tai Fung | W | P | C | U
+Tori-Q | W | A | J | U
+Grill and Greens | C | C | W | U
+Empanada Esquina | E | A | S | U
+Beauty in the Pot | ACC | S | C | U
+Italian Extravaganza | ACC | S | I | U
+Sushi Spectacle | ACC | S | J | U
+Thai Royal Banquet | ACC | S | T | U
diff --git a/data/GiftList.txt b/data/GiftList.txt
new file mode 100644
index 0000000000..63272ff4e2
--- /dev/null
+++ b/data/GiftList.txt
@@ -0,0 +1,84 @@
+[Accessory] Customised pendant necklace | F | U
+[Accessory] Personalised charm bracelets | F | U
+[Accessory] Studs or dazzling chandelier earrings | F | U
+[Accessory] Classic leather-bound watch or modern smartwatches | M | U
+[Accessory] Vintage brooches or pins | M | U
+[Accessory] Any piece of jewelry that includes a name, initial, or date | F | U
+[Accessory] Sentimental keychains of a special place | U | U
+[Accessory] Desk accessories, mousepad, desk blotter, paperweight | U | U
+[Accessory] Passport holder, pen case embossed with the recipient's name or initials | U | U
+[Accessory] Travel accessories, quality travel pillow and luggage tags | U | U
+[Creative] Custom portraits of you and your significant other | U | U
+[Creative] Custom illustration that reflects a special moment in your relationship or an inside joke | U | U
+[Creative] Small sculpture that symbolises your relationship | U | U
+[Creative] Handwritten letter or poem | U | U
+[Creative] Mixed media collage incorporating photographs, ticket stubs | U | U
+[Creative] Book of poems, including some written by you | U | U
+[Creative] Customised mugs with quotes or images that mean something to both of you | U | U
+[Creative] Books based on their interest and needs (novel, sci-fi, travel) | U | U
+[Creative] Memory jar filled with notes and memories throughout the year | U | U
+[Creative] Personalised pens engraved with the recipient's name or initials | U | U
+[Creative] Concert tickets for a favourite band or artist perform live | U | U
+[Creative] Mini projector for movie lovers | U | U
+[Food] Homemade dinner, consider making their favourite dish | U | U
+[Food] Artisan chocolates, flavours or a brand your partner loves | U | U
+[Food] Gourmet basket including cheese, exotic fruits, vinegar | U | U
+[Food] A glass of wine or craft beers | U | U
+[Food] High-quality beans or loose leaf teas | U | U
+[Food] Specialty sweets such as macarons, marshmallows, ice-cream | U | U
+[Scent] Perfume or cologne, a fragrance that matches their personality or one they would love | U | U
+[Scent] Aromatherapy set including essential oils, a diffuser, or some scented candles | U | U
+[Scent] Scented candles with unique fragrances, consider candles that uses natural waxes | U | U
+[Scent] Scented lotions, cream, or body washes | F | U
+[Scent] Incense sticks, cones, or scented sachets for drawers and closets | U | U
+[Plant] Succulents and cacti: low-maintenance plants, suitable for those who may not have a green thumb | U | U
+[Plant] DIY terrarium kit: thoughtful choice for someone who enjoys crafts and plants | U | U
+[Plant] Garden starter kit with seeds, pots, and planting instructions | U | U
+[Flower] Red roses: Love and passion (associated with deep affection and desire) | U | U
+[Flower] Tulips: Perfect love (Red - deep love, purple - royalty and elegance) | U | U
+[Flower] Peonies: Romance, prosperity, bashful love. Also a good luck charm for a happy marriage! | U | U
+[Flower] Orchids: Love, beauty, strength, deep enduring passion | U | U
+[Flower] Lilies: White - sense of purity, red - fervent passion | U | U
+[Flower] Sunflowers: Adoration, loyalty, longevity | U | U
+[Flower] Daisies: Innocence and cheerfulness, representing new beginnings or a love that is playful and bright! | U | U
+[Flower] Carnations: Red - admiration and deep love, white - pure love and good luck | U | U
+[Flower] Gardenias: Sweet fragrance, symbol of secret love or joy | U | U
+[Flower] Lilacs: Light purple - first love, beginnings of love | U | U
+[Flower] Camellias: Admiration, perfection, deep longing (associated with eternal love) | U | U
+[Flower] Blue irises: Faith and hope, deep sentiments and sometimes stand for loyalty | U | U
+[Flower] Alstroemeria: Friendship and devotion | U | U
+[Flower] Red Chrysanthemums: Love and deep passion | U | U
+[Flower] Forget-me-nots: True love and remembrance (given in the hope the recipient will not forget the giver) | U | U
+[Flower] Red poppies: Remembrance, deep and passionate love between two people | U | U
+[Flower] Violets: Faithfulness (conveys the message "I'll always be true") | U | U
+[Flower] Anthuriums: Long-term love and happiness | U | U
+[Flower] Amaryllis: Given as a recognition of someone's beauty and loveliness | U | U
+[Flower] Ranunculus: Use to convey charm and attractiveness (wonderful gift to say "I am dazzled by your charms") | U | U
+[Beauty] Skincare products | F | U
+[Fashion] Stylish handbag | F | U
+[Accessory] Versatile silk scarf featuring a unique design or pattern | F | U
+[Scent] Artisanal Candle Collection | U | U
+[Food] A box of gourmet chocolates with a variety of flavors | U | U
+[Music] Vinyl Record Player | U | U
+[Entertainment] Netflix subscription | U | U
+[Beauty] Makeup palette | F | U
+[Scent] Perfume sampler set: A selection of mini fragrances | U | U
+[Tech] Wireless earbuds for music, podcasts, or calls on the go | U | U
+[Wellness] Yoga mat and accessories | F | U
+[Wellness] Fitness tracker to monitor fitness journey | U | U
+[Creative] Watercolour paint set with papers and brushes | U | U
+[Wellness] Bathrobe and slippers set | U | U
+[Wellness] Therapeutic massage chair | U | U
+[Wellness] Adjustable dumbbell set for home workouts | M | U
+[Wellness] Comfortable running shoes | U | U
+[Entertainment] Quality headphones or earbuds for music and gaming | M | U
+[Entertainment] Sporting event tickets | M | U
+[Entertainment] Concert tickets for their favourite band or artist | U | U
+[Beauty] Electric shavers or beard trimmers | M | U
+[Wellness] Fitness gear such as workout equipment or clothing | M | U
+[Entertainment] Collectible models such as cars, planes, or figures related to their interests | M | U
+[Creative] Art or prints especially if they are fans of specific genres or artists | U | U
+[Entertainment] Virtual reality (VR) gaming set for an immersive gaming experience | M | U
+[Creative] 3D Printer for the hobbyist or DIY enthusiasts | M | U
+[Entertainment] Language learning software subscription | U | U
+[Accessory] DIY mechanical keyboard or custom keycaps that match the recipient's style or interests | U | U
diff --git a/data/UserDetails.txt b/data/UserDetails.txt
new file mode 100644
index 0000000000..b77c64a4a6
--- /dev/null
+++ b/data/UserDetails.txt
@@ -0,0 +1 @@
+me | 100 | Other | X | S | S | N.A
\ No newline at end of file
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..321a1daf74 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://i.ibb.co/fYkRH15/Hero-Image.png) | Lim Soong En | [Github](https://github.com/soongensayo) | [Portfolio](team/soongensayo.md)
+![](https://i.ibb.co/w0Pfp4X/WIN-20240307-16-26-22-Pro.jpg) | Liu Yiting | [Github](https://github.com/liuy1103) | [Portfolio](team/liuy1103.md)
+![](https://i.ibb.co/F4N8CXc/photo-2024-03-07-17-28-20.jpg) | Dooa Shaw Peng Sean | [Github](https://github.com/seandooa) | [Portfolio](team/seandooa.md)
+
+![](https://i.ibb.co/P18rZQX/msg-826612051-262152.jpg) | Liu Ting Yu | [Github](https://github.com/tiffanyliu0220) | [Portfolio](team/tiffanyliu0220.md)
+![](https://i.ibb.co/4N09Rk2/photo-2024-04-14-23-50-18.jpg) | Teo De Liang Ryan | [Github](https://https://github.com/RyanTDL) | [Portfolio](team/ryantdl.md)
\ No newline at end of file
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..41c546f2cc 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,38 +1,312 @@
# Developer Guide
+---
+
## Acknowledgements
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+We would like to acknowledge the following third-party libraries, frameworks and sources:
+
+### Development
+
+- **[JUnit 5](https://junit.org/junit5/)**: The java testing framework.
+
+### Gradle
+
+- **[Checkstyle](https://docs.gradle.org/current/userguide/checkstyle_plugin.html)**: The Gradle plugin that ensures consistent and appropriate code style.
+- **[Shadow](https://github.com/johnrengelman/shadow)**: The Gradle plugin for creating fat JARs.
+
+---
## Design & implementation
{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+### [Implemented] Generate Idea feature
+
+#### Implementation
+
+The existing Generate Idea feature is facilitated by `GenerateIdea` by leveraging the methods `getRandomActivity()` and `getRandomFood()` in `ActivityList` and `FoodList`. It extends `Command` and implements the following operation:
+
+- `execute()` - Generates a randomised date idea consisting of 1 food and 1 dining option. Users can prompt to regenerate an idea until they are satisfied.
+
+`execute()` is responsible for creating an idea object, which has takes data from a Food object and an Activity object. For example, a date idea of going to Haji Lane for shopping and habing a meal at Zen Zen Sushi is shown in the object diagram below:
+
+
+
+Given below is an example usage scenario and how the Generate Idea mechanism behaves at each step.
+
+Step 1. The user launches the application and executes the `idea` command. The `idea` command is parsed by the `parseCommand` method in the `Parser` class, which creates a `GenerateIdeaCommand` instance.
+
+Step 2. The `execute` method of `GenerateIdea` is invoked. It retrieves a random activity and a random dining option from `ActivityList` and `FoodList` and presents it to the user.
+
+Step 3. The user is not satisfied with the proposed idea and inputs the `no` command. The loop in `execute` does not meet the exit condition and thus, generates another idea using the same process as Step 2.
+
+Step 4. The user is satisfied with the proposed idea and inputs the `yes` command. The loop in `execute` has met the exit condition and thus, the `run` method continues running allowing the user to input other commands.
+
+The following activity diagram summarises what happens when a user inputs `idea`:
+
+
+
+The following sequence diagram shows how `idea` the order in which methods in various classes are executed to produce a result to the user:
+
+
+
+The following class diagrams show the `FoodList` and `ActivityList` called upon during generation of the date idea:
+
+
+
+### [Implemented] Smart Itinerary Generation feature
+
+#### Implementation
+The Smart Itinerary Generation mechanism leverages the existing UserDetails class to retrieve user preferences and create smart itineraries. It extends the Command class with a new ```GenerateSmartItineraryCommand```. Additionally, it implements the following operations:
+- ```GenerateSmartItineraryCommand(UserDetails userDetails)``` — Initializes the command with the user's stored preferences from the UserDetails object.
+- ```execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui, Storage storage)``` — Generates a personalized itinerary by filtering food and activity options based on the user's preferences, ensures variety in the selections, and outputs the generated itinerary.
+
+These operations are detailed in the ```Parser``` class, where the ```parseCommand(String userInput, UserDetails userDetails)``` method is updated to accept the ```UserDetails``` object and return a ```GenerateSmartItineraryCommand``` when the `"smart"` command is entered by the user.
+
+Given below is an example usage scenario and how the Smart Itinerary Generation mechanism behaves at each step.
+
+Step 1. The user launches the application for the first time. Their user details, including preferred location and cuisine preferences, are collected via the ```UserDetailsCommand``` and stored in a ```UserDetails``` object.
+
+Step 2. The user enters the `"smart"` command to generate a personalized itinerary. The `"smart"` command is parsed by the ```parseCommand``` method in the ```Parser``` class, which creates a ```GenerateSmartItineraryCommand``` instance, passing the ```UserDetails``` object.
+
+Step 3. The execute method of ```GenerateSmartItineraryCommand``` is invoked. It retrieves the user's preferred location and cuisine from the ```UserDetails``` object. The method then filters the ```FoodList``` to find food options matching the user's preferences, ensures the selected food options are distinct, and randomly selects two activities from the ```ActivityList```. The generated itinerary, consisting of the selected food and activity options, is then output to the user.
+
+![Smart Itinerary Generation Sequence Diagram](images/GenerateSmartItineraryCommandSequenceDiagram.png)
+
+### [Implemented] User Details Collection
+
+#### Implementation
+The UserDetailsCommand is responsible for collecting personal information from the user, which includes name, age, gender, location, favorite cuisine, and relationship status. This command may also conditionally request an anniversary date based on the user's relationship status. The information is then utilized to enhance the application's service and recommendations.
+
+This command extends the Command class and implements the following operations:
+
+`UserDetailsCommand#execute()` — Interacts with the user via the UI to gather personal information and saves it using the `Storage` class.
+Personal details such as name, age, and gender are stored in a `UserDetails` object, which is then serialized to a file by `Storage`.
+These operations are exposed in the system as follows:
+
+The `UserDetailsCommand` is instantiated and called when the user chooses to enter or update their personal details.
+The execute method of this command interacts with other components like `Ui` for input/output and `Storage` for persisting user data.
+Below is the sequence of actions performed by the `UserDetailsCommand` to collect user information.
+
+![User Details Collection Sequence Diagram](images/UserDetailsCommandSequenceDiagram.png)
+
+Each step of the execute method interacts with the user to collect a specific piece of information. Conditional logic is applied to request the anniversary date if the relationship status warrants it. After all information is gathered, the UserDetails object's properties are set accordingly. The final step involves the Storage component saving the details, thus persisting the data for future sessions.
+
+The above sequence diagram depicts the interaction between UserDetailsCommand, the UI, and the Storage component, which highlights the flow of data collection and storage.
+
+#### Object Diagram
+Below is an object diagram representing the state of a `UserDetails` instance after a user has entered their information.
+
+![User Details Object Diagram](images/UserDetailsObjectDiagram.png)
+
+### [Implemented] History Tracking feature
+
+#### Implementation
+
+The existing History Tracking feature keeps a database of previously visited restaurants, activities and gifts. It extends the Command class with a new ```ViewHistoryCommand```. Additionally, it implements the following operation:
+- ```execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui, Storage storage, UserDetails userDetails, GiftList gifts)``` — Displays a list of all past restaurants, activities and gifts based on the user's past history.
+
+These operations are detailed in the ```Parser``` class, where the ```parseCommand(String userInput, UserDetails userDetails)``` method is updated to return a ```ViewHistoryCommand``` when the `"history"` command is entered by the user.
+
+Given below is an example usage scenario of how the History Tracking mechanism behaves at each step.
+
+Step 1. The user launches the application and executes the `idea`/`itinerary`/`smart`/`gift` command. The relevant command is parsed by the `parseCommand` method in the `Parser` class, which creates a `GenerateIdeaCommand`/ `GenerateItineraryCommand`/`GenerateSmartItineraryCommand`/`GenerateGiftCommand` instance
+
+Step 2. The `execute` method of the relevant instance is invoked. Based on the command, a random activity, dining option or gift is retrieved from `ActivityList`, `FoodList` or `GiftList` respectively, and is presented to the user
+
+Step 3. The user is not satisfied with the proposed option and inputs the `no` command. The loop in `execute` does not meet the exit condition and thus, generates another option using the same process as Step 2.
+
+Step 4. The user is satisfied with the proposed option and inputs the `yes` command. The loop in `execute` has met the exit condition and thus, the `run` method continues running allowing the user to input other commands.
+
+Step 5. Upon inputting the `yes` command, the system assumes that the user has decided to take up the proposed suggestion. The `markComplete` method of the relevant Food/Activity/Gift instance is invoked, updating the completion status of the instance from 'U'(Uncompleted) to 'C'(Completed). The `saveFood`/`saveActivity`/`saveGift` method of the `Storage` class is then invoked, which rewrites the relevant data stored in `FoodList.txt`/`ActivityList.txt`/`GiftList.txt`.
+
+Step 6. The user decides to look at past dates, and inputs the command `history`. This command is parsed by the `parseCommand` method in the `Parser` class, which creates a `ViewHistoryCommand` instance.
+
+Step 7. The `execute` method of the instance is invoked. The system iterates through each Activity instance in `ActivityList`, each Food instance in `FoodList`, and each Gift instance in `GiftList`, invoking the method `getCompletionStatus` each time. If the `getCompletionStatus` method returns the string `C`, the system registers the specific instance as having been completed and thus prints it out
+
+The following activity diagram summarises how the history database is displayed when a user inputs the command `history`:
+![View History Sequence Diagram](images/ViewHistoryCommandSequenceDiagram.png)
+
+### [Implemented] Generate Random Gift Suggestion
+
+This feature enhances the user experience by offering access to a curated list of gift ideas and enabling the generation of random gift suggestions.
+
+#### Feature Components and Interaction
+
+The feature comprise several key components.
+
+**`Gift` Class:**
+- Inherits from the `Favourites` class.
+- Adds specific attributes like `gender` and `completionStatus` to manage gift properties effectively.
+- Methods such as `markComplete()` to change the status once a gift is accepted by the user.
+
+**`GiftList` Class:**
+- Manages a collection (`ArrayList`) of gifts and keeps track of which gifts have been suggested (`HashSet`).
+- Includes functionality to retrieve a random gift that hasn't been suggested or marked as complete, enhancing the user experience by providing fresh suggestions.
+
+**`GenerateGiftCommand` Class:**
+- Executes the process of fetching and suggesting a gift from the `GiftList`.
+- Handles user interactions during the suggestion process, processing responses to either continue suggesting, accept the gift, or exit the suggestion loop.
+
+**`Ui` Class:**
+- Facilitates user interaction by displaying messages and capturing user input, which guides the flow of the gift suggestion process.
+
+**`Storage` Class:**
+- Responsible for loading and saving gift data to ensure that the `GiftList` is persistent across sessions, maintaining the state of which gifts have been suggested or accepted.
+
+**`Parser` Class:**
+- Interprets user commands and directs the application to execute specific actions based on the user input, such as initiating the gift suggestion process.
+
+
+
+**Component Interaction Flow**
+
+**1. Initialisation**
+- The `Storage` class loads existing gift data from the `GiftList.txt` file into a `GiftList` object at application startup.
+- This ensures that the application remembers which gifts have been previously suggested or marked as complete.
+
+**2. Command Parsing**
+- The `Parser` class determines when the `GenerateGiftCommand` should be activated based on user input (e.g., `gift`, `gift male`, `gift female`, or `gift unisex`).
+- It creates an instance of this command with the specified gender parameter.
+
+**3. Generating Gift Suggestions**
+- The `GenerateGiftCommand` interacts with the `GiftList` to fetch a random gift that matches the specified gender and hasn’t been completed or previously suggested.
+- If a gift is available, it is presented to the user through the `Ui` class, which asks whether the user is satisfied with the suggestion.
+
+**4. User Interaction Loop**
+- The user's response is handled in a loop:
+ - Accept (yes): The gift’s `markComplete()` method is called, and the gift is marked as completed.
+ - Reject (no): Another gift suggestion is fetched from the `GiftList`, and the process repeats.
+ - Exit (cancel): Exits the gift suggestion loop and ends the command execution.
+
+**5. Data Saving**
+- After exiting the loop (either through acceptance of a gift or cancellation), the `Storage` class updates the gift data file to reflect any changes (e.g., marking a gift as complete).
+
+
+
+#### Design Considerations
+
+- Using a class hierarchy where `Gift` extends `Favourites` allows for easy addition of new types of favourites in the future.
+- Segregating functionalities into distinct classes (`Gift`, `GiftList`, `GenerateGiftCommand`, etc.) enhances modularity, making the codebase more maintainable and scalable.
+- Random selection from the `GiftList` ensures a diverse range of suggestions, enhancing user experience by preventing repetitive recommendations.
+- Utilising a text file for storing gift data ensures simplicity and reliability, avoiding over-complication with external databases or dependencies.
+- By enabling gender-specific gift suggestions, the application can provide more targeted and relevant options.
+- At the same time, it also retains flexibility by allowing users to opt for non-gendered suggestions when preferences are not specified.
## Product scope
### Target user profile
-{Describe the target user profile}
+Couples in Singapore
### Value proposition
-{Describe the value proposition: what problem does it solve?}
+In today's fast-paced world, maintaining a healthy and exciting relationship can often be challenging for couples. Balancing work, personal responsibilities, and quality time with a partner requires effort and planning. Many couples struggle to come up with new and interesting date ideas that cater to both partners' preferences, leading to a routine that can become stale and uninspiring. This is where Flirt and Fork steps in to rejuvenate the love life of couples by offering a seamless, engaging, and personalised date planning experience.
## 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|
+|v1.0|first-time user|see a help message|know how to use the features within the app & its parameters|
+|v1.0|busy user|effortlessly generate date ideas|not waste time endlessly searching for ideas|
+|v1.0|budget conscious user|generate a date itinerary based on price|choose when to splurge and when to save|
+|v1.0|user looking for variety|view a variety of date activities|enjoy diverse date experiences|
+|v1.0|experienced user of the platform|save some of my preferred activities|reference back to them in future|
## Non-Functional Requirements
-{Give non-functional requirements}
+1. **Performance Requirements**: The application should have a rapid response time, aiming to generate personalized date ideas within a few seconds after the user inputs their preferences.
+
+2. **Usability Requirements**: Despite being a command-line interface, the app should offer a user-friendly experience. Instructions should be clear and input errors should be handled gracefully, offering suggestions for correction.
+
+3. **Security Requirements**: User data, including their preferences, location, and budget, should be securely handled.
## Glossary
-* *glossary item* - Definition
+
+Generate Idea Feature: A feature within the application that generates random date ideas consisting of food and activity suggestions.
+
+Smart Itinerary Generation Feature: A proposed feature that creates personalized itineraries based on user preferences, ensuring variety and suitability.
+
+History Tracking Feature: A proposed feature that keeps a record of past date activities, allowing users to track their date history and preferences.
+
+Gift-related Features: Features related to suggesting and managing gift ideas for special occasions or romantic gestures.
+
+Gift Class: A class representing a gift item, including its description and completion status.
+
+GiftList Class: A class managing a collection of gift items and providing methods to access and manipulate the list.
+
+GenerateGiftCommand Class: A command class responsible for generating random gift suggestions and handling user feedback.
+
+ListOptionsCommands Class: A command class that provides functionality to list different types of options, including gifts, food, and activities.
+
+ViewHistoryCommand Class: A command class that displays a history of saved gift ideas or past date activities.
+
+Storage Class: A class responsible for managing data persistence for gift items and other application data.
+
## 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}
+
+
+**Generate Idea Feature:**
+
+Launch the application.
+
+Execute the "idea" command.
+
+Verify that the application generates random date ideas consisting of food and activity suggestions.
+Test the regeneration option to ensure that users can request new ideas until satisfied.
+
+**Smart Itinerary Generation Feature (Proposed):**
+
+Launch the application.
+
+Ensure that user preferences are stored in the UserDetails object.
+
+Execute the "smart" command.
+
+Verify that the application generates a personalized itinerary based on user preferences, including food and activity options.
+
+**History Tracking Feature (Proposed):**
+
+Launch the application.
+
+Verify that past date activities and gift ideas are stored and accessible.
+
+Execute the "history" command.
+
+Verify that the application displays a list of past date activities or gift ideas.
+
+**Gift-related Features:**
+
+Ensure that the application allows users to view a curated list of gift ideas.
+
+Execute the "gift" command.
+
+Verify that the application generates random gift suggestions.
+
+Test the option to mark selected gifts as complete and view past gift ideas.
+Ensure that changes to gift selections are persisted across sessions.
+
+**Non-Functional Requirements:**
+
+Test application performance under various loads to ensure responsiveness.
+
+Verify that the application adheres to established coding standards and conventions.
+
+Test data persistence to ensure that user data is stored securely and accurately.
+
+Evaluate the user interface for consistency, usability, and accessibility.
+Check for proper error handling and logging mechanisms.
+
+**User Stories:**
+
+Test each user story scenario to ensure that the application meets the specified requirements and user expectations.
+
+**Edge Cases:**
+
+Test boundary conditions and edge cases for all features to ensure robustness and reliability.
+Test error handling for invalid inputs and unexpected scenarios.
+Ensure graceful degradation and recovery from failure conditions.
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..0566f5c77e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,6 @@
-# Duke
+# Flirt and Fork
-{Give product intro here}
+Say goodbye to the hassle of planning dates! Our app generates creative and enjoyable date itineraries that fit within your specified budget and are easily accessible from your location.
Useful links:
* [User Guide](UserGuide.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..c2987011f5 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -2,41 +2,700 @@
## Introduction
-{Give a product intro}
+It's Friday evening, the clock is ticking down the final hours at work, and all you can think about is kicking back and enjoying a great night out. But then, it hits you—it’s your 1-year anniversary and you haven’t planned anything special yet! With just three hours to go before you meet your significant other, panic starts to set in. But wait, there’s no need to worry! Introducing 'Flirt and Fork,' your personalized date night concierge.
+
+Imagine never running out of creative and exciting date night ideas again. Whether you're caught up in work or simply struggling for inspiration, 'Flirt and Fork' takes the stress out of your romantic plans. Our app generates tailor-made, delightful date itineraries with just a few clicks.
+
+Here’s what makes 'Flirt and Fork' an essential tool for anyone wanting to impress their special someone:
+
+1. **Curated Experiences**: From trendy restaurants and cozy cafes to thrilling activities and thoughtful gifts, our app curates a list of experiences that promise to make your date memorable.
+2. **Personalized Options**: We understand that uniqueness is key. That's why 'Flirt and Fork' offers customizable options to filter your date plans based on location, budget, and cuisine preferences, ensuring every date is as unique as your relationship.
+3. **Simplicity and Speed**: Forget the endless browsing and decision fatigue. Our user-friendly interface provides quick, efficient, and enjoyable planning, so you can focus more on living in the moment and less on the logistics.
+4. **Ever-Growing Database**: Our extensive and ever-expanding collection of venues and activities keeps your dating life vibrant and exciting, offering new experiences every time you use the app.
+
+Don’t let another special occasion slip by uncelebrated. Sign up for 'Flirt and Fork' today, and start crafting those perfect evenings that turn into cherished memories. Why wait? Let us help you create a night out that’s as wonderful as your significant other. Embrace the ease of planning, and give your date nights the upgrade they deserve with 'Flirt and Fork'!
+
## Quick Start
-{Give steps to get started quickly}
+1. Ensure you have Java `11` or above installed in your Computer.
+2. Download the latest version of `Flirt and Fork` from [here](https://github.com/AY2324S2-CS2113-T11-2/tp/releases).
+3. Copy the file to the folder you want to use as the _home folder_
+4. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar Flirt_and_Fork.jar` command to run the bot. The GUI should appear in a few seconds.
+5. Type the command in the command box and press Enter to execute it.
+6. Refer to the [Features](#features) section below for an overview of all valid commands.
+
+### Warning
+Do NOT manually tamper with the .txt files in the data folder - `Flirt and Fork` will refuse to cooperate with you if you choose to violate this rule.
+
+## Features
+This section provides an overview of the available commands in the application, grouped by their functionality. Detailed usage instructions for each command can be found in the [Usage](#usage) section.
+
+### Discovering Options
+Explore potential dining, activities, and gifts options.
+- `list`: View a comprehensive list of restaurants, activities, and gift ideas.
+
+### Searching Options
+Search for potential dining. activities, and gift options to add on to your favourites. Search within your favourites list as well.
+- `find`: Search the database for potential dining places, activities and gifts. Search your favourites list for specific entries as well.
+
+### Personalisation
+Tailor the application experience to your preferences for more customised suggestions.
+- `me`: Input personal details and preferences to enable personalised suggestions.
+- `itinerary`: Generate a date itinerary based on specified preferences for location and pricing.
+- `smart`: Generate a smart itinerary tailored to your personal details.
+> Complete `me` before the smart itinerary function can be enabled
+
+### Idea Generation
+Stimulate creativity with randomised suggestions for dates and gifts.
+- `idea`: Receive a randomised date idea.
+- `gift`: Receive a randomised gift suggestion.
+
+### Favourites Management
+Manage a personalised list of your favourite foods and activities.
+- `food`: Add a food option to your favourites list.
+- `activity`: Add an activity to your favourites list.
+- `favourites`: List all entries in your current favourites list.
+- `delete`: Remove an entry from your favourites list based on its entry number.
+
+### Additional Utilities
+Extra commands to facilitate application use.
+- `help`: Display help message with a summary of all valid commands at any point in time.
+- `history`: List all past marked date restaurants, activities, and gift ideas.
+
+### Exit the program
+There is only one command to terminate the program:
+- `exit`: Saves all current data and exits the program.
+
+Refer to the [Usage](#usage) section below for details of each command.
+
+## Usage
+For all the commands below, please only use English Language to test them out.
+
+### Put in your personal details: `me`
+Takes in personal details like cuisine preferences, location and anniversary date if applicable to personalise the user experience. It is also automatically run on first start-up and has to be completed before using the app. Entering `me` subsequently provide the option to update user deatils.
+
+Example of usage: `me`
+
+Expected outcome:
+
+```
+Please enter your name:
+```
+
+- Expected outcome after inputting name as `Cupid`:
+
+```
+Great! Hello there Cupid, it's my pleasure to know you!
+____________________________________________________________
+Please enter your age:
+```
+
+- Expected outcome after inputting age as `22`:
+
+```
+Wow, you're 22 years young! This might be handy information.
+____________________________________________________________
+Please enter your gender(Male/Female/Other):
+```
+
+Note: The age entered has to be an integer value between 0 and 120 inclusive.
+
+- Expected outcome after inputting gender as `Male`:
+
+```
+I see you're a Male!
+____________________________________________________________
+Where do you stay?
+
+E: East
+W: West
+C: Central
+S: South
+NE: NorthEast
+```
+
+- Expected outcome after inputting location as `NE`:
+
+```
+Thanks! Don't worry, I won't let the rest know where you stay ;)!
+____________________________________________________________
+What is your favourite cuisine?
+
+W: Western
+F: Fusion
+J: Japanese
+C: Chinese
+T: Thai
+K: Korean
+I: Italian
+S: Spanish
+```
+
+- Expected outcome after inputting cuisine as `W`:
+
+```
+Thanks, this will be useful...
+____________________________________________________________
+Please enter your relationship status:
+
+Enter 'M' if you are Married
+Enter 'R' if you are in a serious relationship
+Enter 'F' if you are having a fling
+Enter 'D' if you are dating/testing the waters
+Enter 'S' if you are single and ready to mingle
+Enter 'X' if you are single and only looking to hangout with friends
+```
+
+- Expected outcome after inputting relationship status as `R`:
+
+```
+Thanks for letting me know your relationship status! :)
+____________________________________________________________
+Lucky you! Please enter your anniversary in 'dd/mm/yyyy' format:
+```
+
+- Expected outcome after inputting anniversary date as `14/02/2024`:
+
+```
+User details saved successfully!
+Curious about what you can do? Just type 'help' for a sprinkle of tips!
+____________________________________________________________
+```
+
+Note: Anniversary option only shows up if `M`, `R` or `D` are set as the relationship status. Also, do note that the date entered cannot be later than the current date.
+
+### Display all possible commands: `help`
+Lists out all possible commands used to interact with Flirt and Fork.
+
+Example of usage: `help`
+
+Expected outcome:
+```
+I know you are excited to Flirt & Fork :) Here's how:
+
+-----------------------------------------
+| Command to type | Function of feature |
+-----------------------------------------
+1. list: Take a look at potential restaurants, activities, or gifts
+
+...
+```
+
+### Discovering Options: `list`
+Lists out all the restaurants, activities, or gifts within the curated collection.
+
+Example of usage: `list`
+
+Expected outcome:
+```
+Looking for ideas to spice up your date night?
+Choose from the following options:
+1. List out delicious dining options (type 'food')
+2. Discover exciting activities to do together (type 'activities')
+3. Unwrap joy with our curated list of gifts that'll make hearts flutter! (type 'gifts')
+4. Changed your mind? Feel free to cancel this command! (type 'cancel')
+What's your pleasure?
+```
+---
+
+- If user input `food`: Displays a list of potential restaurants or eateries.
+
+Expected outcome:
+```
+HMMMM Let's see what food is theree:
+1. 25 Degrees
+2. 49 Seats
+...
+____________________________________________________________
+To discover exciting activities, type 'activities'
+To view a curated list of gifts, type 'gifts'
+To cancel this command, type 'cancel'
+```
+---
+- If user input `activities`: Shows a list of fun and engaging activities.
+
+Expected outcome:
+```
+What are some activities you can do as a couple? Let's see..
+1. Botanic Garden Tours
+2. Gardens by the Bay
+...
+____________________________________________________________
+To list out delicious dining options, type 'food'
+To view a curated list of gifts, type 'gifts'
+To cancel this command, type 'cancel'
+```
+---
+- If user input `gifts`: Presents a selection of gift ideas.
+
+Expected outcome:
+```
+Peek into Cupid's own gift collection!
+1. [Accessory] Customised pendant necklace
+2. [Accessory] Personalised charm bracelets
+...
+____________________________________________________________
+To list out delicious dining options, type 'food'
+To discover exciting activities, type 'activities'
+To cancel this command, type 'cancel'
+```
+---
+- If user input `cancel`: Terminates and exits the current list viewing session.
+ - This command can be used at any point during the list viewing interaction if the user decides not to stop viewing.
+
+Expected outcome:
+```
+Cancelling listings...
+Cancel success!
+```
+### Find an entry: `find`
+Finds relevant entries from the food, activities, gifts database and your favourites list, based on the keyword inputted.
+
+Format: `find`
+
+Expected Outcome:
+```
+What are you searching for in this enchanting realm?
+Choose from the following options:
+1. What delicious food are you craving for today? (type 'food')
+2. What exciting activity are you interested in? (type 'activities')
+3. On the hunt for the perfect gift? What are you looking for? (type 'gifts')
+4. Finding within your own treasures? (type 'favourites')
+5. Changed your mind? Feel free to cancel this command! (type 'cancel')
+ What's on your mind?
+```
+- If user inputs 'food':
+
+Expected Outcome:
+
+```Mmmm food yes. What restaurants would you like to search for?```
+
+User then needs to input their search keyword here.
+* The `keyword` is not case-sensitive. `Japanese` will yield the same result as `japanese`.
+* The `keyword` can contain multiple words.
+
+Example of usage:
+`BBQ`
+
+Expected outcome:
+```
+Eureka! Your cupid's arrow hit the target! We found these matches for you:
+1. BBQ Smokehouse
+2. Bulgogi BBQ
+```
+- If user inputs 'activities':
+
+Expected Outcome:
+
+```Mmmm activities! What kind of activities would you like to search for?```
+
+User then needs to input their search keyword here.
+* The `keyword` is not case-sensitive. `Japanese` will yield the same result as `japanese`.
+* The `keyword` can contain multiple words.
+
+Example of usage:
+`concert`
+
+Expected outcome:
+```
+Eureka! Your cupid's arrow hit the target! We found these matches for you:
+1. Esplanade Free Concerts
+2. Victoria Theatre Concert Night
+3. Private Music Concerts
+```
+
+- If user inputs 'gifts':
+
+Expected Outcome:
+
+```Mmmm gifts! What kind of gifts would you like to search for?```
+
+User then needs to input their search keyword here.
+* The `keyword` is not case-sensitive. `Japanese` will yield the same result as `japanese`.
+* The `keyword` can contain multiple words.
+
+Example of usage:
+`ring`
+
+Expected outcome:
+```
+Eureka! Your cupid's arrow hit the target! We found these matches for you:
+1. [Accessory] Studs or dazzling chandelier earrings
+2. [Flower] Orchids: Love, beauty, strength, deep enduring passion
+3. [Accessory] Versatile silk scarf featuring a unique design or pattern
+```
+
+- If user inputs 'favourites':
+
+Expected Outcome:
-1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+```Mmmm finding your own treasures i see. What would you like to search for?```
-## Features
+User then needs to input their search keyword here.
+* The `keyword` is not case-sensitive. `Japanese` will yield the same result as `japanese`.
+* The `keyword` can contain multiple words.
-{Give detailed description of each feature}
+Example of usage:
+`bbq`
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+Expected outcome:
+```
+Eureka! Your cupid's arrow hit the target! We found these matches for you:
+1. East Coast BBQ
+```
-Format: `todo n/TODO_NAME d/DEADLINE`
+- If user input `cancel`: Terminates and exits the current searching session.
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+Expected outcome:
+```
+Cancelling findings...
+Cancel success!
+```
+### Generate a randomised date idea: `idea`
+Generates out a randomised date idea, consisting of 1 restaurant and 1 activity from our curated collection. After receiving the suggestion, users have the option to accept or request for another idea.
+
+Example of usage: `idea`
+
+Expected outcome:
+```
+You can do Sentosa Cable Car Standard Ride and have a nice meal at Northeast Nibbles
+-> Are you satisfied with the date idea? [Yes/No]
+-> Else, feel free to stop idea generation using the command 'cancel'
+```
+---
+- If user input `no`: Regenerates another date idea for users to consider.
+
+Expected outcome:
+```
+Regenerating a new date idea...
+You can do Lee Kong Chian Natural History Museum Visit and have a nice meal at Seoul Street
+-> Are you satisfied with the date idea? [Yes/No]
+-> Else, feel free to stop idea generation using the command 'cancel'
+```
+---
+- If user input `yes`: Choice will be confirmed and saved in view history for ease of reference.
+
+Expected outcome:
+```
+That's great! Enjoy your date!
+```
+
+---
+- If user input `cancel`: Terminates the current session and exits the idea generation process.
+ - This command can be used at any point during the idea suggestion interaction if the user decides not to continue or wishes to start over.
+
+Expected outcome:
+```
+Taking a break? That's okay!
+Remember, great ideas need their own time to unwrap.
+```
+
+### Generate a randomised gift suggestion: `gift`
+Generates a randomised gift idea either with or without any gender specifications.
+
+Each suggestion comes with a category tag `[Creative]`, `[Food]`, `[Accessory]`, etc. to give users a sense of what type of gift it is.
+
+Example of usage: `gift` / `gift male` / `gift female` / `gift unisex`
+
+Expected outcome:
+```
+[Creative] Personalised pens engraved with the recipient's name or initials
+-> Satisfied with the gift suggestion? [Yes/No]
+-> Changed you mind? type 'cancel' to exit this process!
+```
+
+---
+- If user input `no`: Regenerates another gift suggestion for users to consider.
+
+Expected outcome:
+```
+[Flower] Red roses: Love and passion (associated with deep affection and desire)
+-> Satisfied with the gift suggestion? [Yes/No]
+-> Changed you mind? type 'cancel' to exit this process!
+```
+---
+- If user input `yes`: Choice will be confirmed and saved in view history for ease of reference.
+
+Expected outcome:
+```
+This gift is about to make a love story even sweeter.
+```
+---
+- If user input `cancel`: Terminates the current gift suggestion session and exits the gift generation process.
+ - This command can be used at any point during the gift suggestion interaction if the user decides not to continue or wishes to start over.
+
+Expected outcome:
+```
+Taking a break? That's okay!
+Remember, great ideas need their own time to unwrap.
+```
+> **!IMPORTANT**
+>
+> Be aware that only one type of gift command can be active at a time. If you initiate a gift suggestion command (`gift`, `gift male`, `gift female`, or `gift unisex`), you must follow through with that choice.
+>
+> If during the process you wish to change the gender specification of the gift, you will need to terminate the current session by typing `cancel`.
+>
+> Once canceled, you can then start a new gift suggestion session with the desired command to specify a different gender.
+>
+> **Example scenario:**
+>
+> Initial command: `gift male`
+>
+> User response: Wishes to switch to female-oriented gifts.
+>
+> Required action:
+> 1. `cancel` to stop the current process.
+> 2. Input `gift female` to start receiving gift suggestions suitable for a female recipient.
+
+### Generate a date itinerary, based on preferred location and budget: `itinerary`
+Generates out a date itinerary, consisting of 2 restaurants and 2 activities from our curated collection.
+The itinerary selected will be based off the user's indicated location and budget.
+
+Format: `itinerary [LOCATION], [PRICE]`
+
+
+> Note: The `location` and `price` must be within the [legend](#legend).
+> Ensure the formatting is strictly followed, including the spacing and the capitalisation for location and price.
+> For example, ``itinerary E, A` will be registered, whereas `itinerary E,A` or `itinerary e, a` will result in an error.
+Example of usage: `itinerary E, A`
+
+Expected outcome:
+```
+Here is a rough itinerary for your date:
+We begin with lunch at Five Oars, followed by some fun at Changi Museum Visit.
+We proceed to have dinner at Missus, and finish the night at Punggol Promenade Nature Walk.
+
+Are you satisfied with the itinerary? [Yes/No]
+```
+---
+- If user input `no`: Application will prompt user to initiate the generation process again if they wish to try for another option.
+
+Expected outcome:
+```
+We apologise! Perhaps you could try again?
+```
+---
+- If user input `yes`: Choice will be confirmed and saved in view history for ease of reference.
+
+Expected outcome:
+```
+That's great! Enjoy your date!
+```
+
+### Generate a smart itinerary, based on your user profile: `smart`
+The smart command generates a personalised itinerary tailored to your preferences, including your favorite cuisine and preferred location. This feature takes into account the details you've provided to Flirt and Fork to recommend a combination of food and activities that align with your tastes and interests.
+
+Format: `smart`
+
+Simply enter smart to let Flirt and Fork create a custom itinerary for you. Ensure your user profile is updated with your current preferences for the best recommendations.
+
+Expected Outcome:
+
+Upon executing the smart command, Flirt & Fork will display a curated itinerary that includes two food options and two activities. The suggestions for the afternoon (lunch + afternoon activity) are specifically tailored to your default location and favourite cuisine, aiming to enhance your experience with personalized options.
+
+If Flirt & Fork successfully generates an itinerary, you'll see something like this:
+
+```
+Here is a smart itinerary for your date (the afternoon is planned around your base preferences!):
+We begin with lunch at Five Guys, followed by some fun at Museum of Ice Cream.
+We proceed to have dinner at Tolido's Expresso Nook, and finish the night at Haw Par Villa.
+```
+
+In cases where your preferences are too unique or if an unexpected error occurs, Flirt & Fork will inform you that it was unable to generate a suitable itinerary, encouraging you to try again or adjust your profile preferences:
+
+```
+I could not generate a suitable itinerary based on your personal details!
+I'm not smart enough for your default preferences, try 'itinerary' command instead!
+```
+
+Notes:
+
+- The itinerary suggestions are based on the current database of foods and activities within Flirt & Fork. For the best experience, make sure your user details are up-to-date.
+- If Flirt & Fork encounters difficulty in generating an itinerary due to overly unique preferences or other issues, you will receive a message encouraging you to adjust your preferences or try another command.
+By using the smart command, you can discover new and exciting ways to enjoy your day, tailored specifically to what you love.
+
+
+### Add a restaurant to your favourites: `food`
+Adds a new restaurant to your favourites list.
+
+Format: `food [NAME_OF_EATERY], [LOCATION], [PRICE], [CUISINE]`
+
+> Note: The `location` and `price` must be within the [legend](#legend).
+> Repeated food entries are allowed to be added to favourites. We understand that franchises like Mcdonalds and KFC do not have unique names for their respective branches, and thus we will consider them as separate entries. For example, 'food KFC, E, C, W' could be used to represent both the KFC branch at Pasir Ris Park and at East Coast Park. The responsibility is on the user to ensure that the naming convention used allows him/her to clearly differentiate the food
+> For the name of eatery, commas are not allowed. Kindly omit commas used. For example, `food Steak, Burgers and Cocktail Bar, C, S, J` should be replaced with `food Steak Burgers and Cocktail Bar, C, S, J`
+> Ensure the formatting is strictly followed, including the spacing and the capitalisation for location, price and cuisine.
+> For example, `food Omakase, C, S, J` will be registered, whereas `food Omakase,C,S,J` or 'food Omakase, c, s, j' will result in an error.
+
+Example of usage:
+`food East Coast BBQ, NE, B, J`
+`food Omakase, C, S, J`
+
+Expected outcome:
+```
+Cupid's arrow strikes! This is now in your favourites.
+East Coast BBQ
+You've collected 1 romantic treasures!
+```
+
+
+### Add an activity to your favourites: `activity`
+Adds a new activity to your favourites list.
+
+Format: `activity [NAME_OF_ACTIVITY], [LOCATION], [PRICE]`
+
+> Note: The `location` and `price` must be within the [legend](#legend).
+> Repeated activity entries are allowed to be added to favourites. We understand that common activities like cycling or swimming do not have unique names, and thus we will consider them as separate entries. For example, 'activity cycling, E, C' could be used to represent both cycling at Pasir Ris Park and cycling at East Coast Park. The responsibility is on the user to ensure that the naming convention used allows him/her to clearly differentiate the activity
+> For the name of activity, commas are not allowed. Kindly omit commas used. For example, `activity Bizad Run, 2024 Version, C, S` should be replaced with `activity Bizad Run 2024 Version, C, S`
+> Ensure the formatting is strictly followed, including the spacing and the capitalisation for location and price.
+> For example, `activity Paragliding, C, S` will be registered, whereas `activity Paragliding,C,S` or 'activity Paragliding, c, s' will result in an error.
+
+Example of usage:
+`activity Paragliding, C, S`
+`activity Bungee Jumping at Sentosa, S, P`
+
+Expected outcome:
+```
+Cupid's arrow strikes! This is now in your favourites.
+Paragliding
+You've collected 2 romantic treasures!
+```
+
+### List out all entries in your favourites list: `favourites`
+Lists out all the entries within your favourites list. Multiple similar entries can be present in the list, because of either franchising, or the sake of emphasis.
+
+Format: `favourites`
+
+Expected outcome:
+```
+Drumroll, please! Presenting the stars of your romantic sky:
+1. East Coast BBQ
+2. Paragliding
+```
+
+### Delete an entry from your favourites list: `delete`
+Deletes an entry from your favourites list, based on the entry index inputted.
+
+Format: `delete [INDEX_OF_ENTRY]`
Example of usage:
+`delete 2`
+`delete 7`
+
+Expected outcome:
+```
+Poof! Sayonara~ This favourite has been removed:
+[Activity] Paragliding
+Now you have 1 romantic treasures!
+Your journey of love and taste continues~
+```
+
+### List out all entries from your past date history: `history`
+Lists out all the previous restaurants and activities completed on previous dates. Whenever `yes` is inputted for generating an idea or itinerary, it is assumed that the user will follow that date plan and hence, the suggested locations will be saved in the history.
-`todo n/Write the rest of the User Guide d/next week`
+Format: `history`
+
+Expected outcome:
+```
+These are the activities you have marked:
+1. Singapore River Cruise
+2. Katong Food Walk
+3. Changi Museum Visit
+4. Marina Bay Sands Casino
+5. Singapore Flyer Dining
+
+These are the restaurants you have marked:
+1. Missus
+2. Five Oars
+3. Knead To Eat
+4. Idaten Udon
+5. Watami Japanese Dining
+
+These are the gifts you've marked:
+```
+Note: This feature does not work correctly as of v2.0 and will be fixed in the next release.
+
+### Exit the program: `exit`
+Exits the program.
+
+Format: `exit`
+
+Expected outcome:
+```
+Love, like a good meal, is all about timing.
+Keep your love simmering and your fork ready, see you at the next course!
+
+```
+
+## Legend
+Prices:
+* C: Cheap
+* B: Budget
+* A: Affordable
+* P: Pricey
+* S: Special Occasions Only
+
+Cuisines:
+* W: Western
+* F: Fusion
+* J: Japanese
+* C: Chinese
+* T: Thai
+* K: Korean
+* I: Italian
+* S: Spanish
+
+Locations:
+* E: East
+* W: West
+* C: Central
+* S: South
+* NE: NorthEast
+* ACC: Accessible (found in multiple places around SG)
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
## FAQ
-**Q**: How do I transfer my data to another computer?
+**Q: What is 'Flirt and Fork' and how does it help with planning dates?**
+A: 'Flirt and Fork' is a personalized date planning application that generates creative and enjoyable date itineraries tailored to your budget and location. It simplifies the process of coming up with date ideas by providing suggestions for dining, activities, and gifts based on your preferences.
-**A**: {your answer here}
+**Q: Do I need any specific software to run 'Flirt and Fork'?**
+A: Yes, you will need to have Java 11 or above installed on your computer to run 'Flirt and Fork'.
-## Command Summary
+**Q: How do I start using 'Flirt and Fork' after installation?**
+A: After downloading and running the .jar file, you can begin by typing commands into the command box. For a complete list of commands, you can type 'help' or refer to the Features and Usage sections of this guide.
+
+**Q: Can I save my favorite restaurants and activities for future reference?**
+A: Absolutely. You can add your favorite dining spots and activities to a favorites list with the 'food' and 'activity' commands. You can also find, list, and delete these favorites as needed.
+
+**Q: Is it possible to modify or delete entries from my favorites list?**
+A: Yes, you can delete any entry from your favorites list using the 'delete' command followed by the index number of the entry you wish to remove.
+
+**Q: How can I view a history of my past date plans?**
+A: You can view all your past marked date restaurants, activities, and gift ideas by using the 'history' command (for suggestions from 'idea', 'itinerary', or 'smart' commands). This feature is perfect for reminiscing or re-visiting successful date ideas.
-{Give a 'cheat sheet' of commands here}
+**Q: Will my data be saved if I close the application?**
+A: Yes, when you use the 'exit' command, 'Flirt and Fork' will automatically save your current data, ensuring that your preferences and favorites are preserved for your next session.
+
+**Q: How can I ensure the best experience with 'Flirt and Fork'?**
+A: Keep your user details updated and regularly add to your favorites list! Ensure that your location and price inputs align with the provided legend for accurate itinerary generation.
+
+**Q: What languages are available for 'Flirt and Fork'?**
+A: Only English at the moment.
+
+## Command Summary
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+* Display all possible commands: `help`
+* List out all restaurants or activities: `list`
+* Update your personal information: `me`
+* Generate a randomised date idea: `idea`
+* Generate a randomised gift idea: `gift`
+* Generate a date itinerary, based on preferred location and budget: `itinerary [LOCATION], [PRICE]`
+* Generate a smart date itinerary, based on your user profile: `smart`
+* Add a restaurant to your favourites: `food [NAME_OF_EATERY], [LOCATION], [PRICE], [CUISINE]`
+* Add an activity to your favourites: `activity [NAME_OF_ACTIVITY], [LOCATION], [PRICE]`
+* Find an entry based on a keyword: `find`
+* List out all entries in your favourites list: `favourites`
+* Delete an entry from your favourites list: `delete [INDEX_OF_ENTRY]`
+* List out all entries from your past date history: `history`
+* Exit the program: `exit`
diff --git a/docs/diagrams/GenerateIdeaActivityDiagram.puml b/docs/diagrams/GenerateIdeaActivityDiagram.puml
new file mode 100644
index 0000000000..d0eb8d3dc2
--- /dev/null
+++ b/docs/diagrams/GenerateIdeaActivityDiagram.puml
@@ -0,0 +1,22 @@
+@startuml
+!include Style.puml
+
+skinparam activity {
+ BackgroundColor LOGIC_COLOR_T1
+ BorderColor White
+ FontColor White
+}
+
+start
+:User executes command;
+:Idea with random Activity
+and Food is generated;
+Repeat
+Repeat
+:Take user input again;
+repeat while (Invalid Command?) is (yes) not (no)
+repeat while (userSatisfied?) is (no) not (yes)
+
+stop
+
+@enduml
diff --git a/docs/diagrams/GenerateIdeaCommandSequenceDiagram.puml b/docs/diagrams/GenerateIdeaCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..00db8be5f1
--- /dev/null
+++ b/docs/diagrams/GenerateIdeaCommandSequenceDiagram.puml
@@ -0,0 +1,57 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+hide footbox
+
+actor User as User
+participant ":Ui" as Ui
+participant ":GenerateIdeaCommand" as GenerateIdeaCommand
+participant ":FoodList" as FoodList
+participant ":ActivityList" as ActivityList
+participant ":Idea" as Idea
+participant ":Storage" as Storage
+
+User -> Ui: idea
+activate Ui
+Ui -> GenerateIdeaCommand: GenerateIdeaCommand()
+activate GenerateIdeaCommand
+
+loop userSatisfied != yes
+ GenerateIdeaCommand -> FoodList: getRandomFood()
+ activate FoodList
+ FoodList --> GenerateIdeaCommand
+ deactivate FoodList
+
+ GenerateIdeaCommand -> ActivityList: getRandomActivity()
+ activate ActivityList
+ ActivityList --> GenerateIdeaCommand
+ deactivate ActivityList
+
+ GenerateIdeaCommand -> Idea: Idea(Food, Activity)
+ activate Idea
+ Idea --> GenerateIdeaCommand:
+ deactivate Idea
+ GenerateIdeaCommand --> Ui:
+ deactivate GenerateIdeaCommand
+ Ui --> User: printIdea()
+ deactivate Ui
+end
+
+User -> Ui: yes
+activate Ui
+Ui -> GenerateIdeaCommand: yes
+activate GenerateIdeaCommand
+GenerateIdeaCommand -> Storage: saveFood(foods)
+activate Storage
+Storage --> GenerateIdeaCommand
+deactivate Storage
+GenerateIdeaCommand -> Storage: saveActivity(activities)
+activate Storage
+Storage --> GenerateIdeaCommand
+deactivate Storage
+GenerateIdeaCommand --> Ui:
+deactivate GenerateIdeaCommand
+destroy GenerateIdeaCommand
+Ui --> User: printDoneMessage()
+deactivate Ui
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/GenerateIdeaSequenceDiagram.puml b/docs/diagrams/GenerateIdeaSequenceDiagram.puml
new file mode 100644
index 0000000000..fb3784e37e
--- /dev/null
+++ b/docs/diagrams/GenerateIdeaSequenceDiagram.puml
@@ -0,0 +1,20 @@
+@startuml
+'https://plantuml.com/object-diagram
+skinparam linetype ortho
+
+object ":GenerateIdeaCommand" as GenerateIdeaCommand #lightgreen
+
+object "randomFood:Food" as Food #lightblue
+Food : description = "Zen Zen Sushi"
+
+object "randomActivity:Activity" as Activity #salmon
+Activity : description = "Haji Lane Shopping"
+
+object ":Idea" as Idea #orchid
+
+GenerateIdeaCommand -- Idea
+
+Idea -[norank]-> Food : dining option
+Idea -[norank]-> Activity : activity
+
+@enduml
diff --git a/docs/diagrams/GenerateSmartCommandSequenceDiagram.puml b/docs/diagrams/GenerateSmartCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..cfd11cc02f
--- /dev/null
+++ b/docs/diagrams/GenerateSmartCommandSequenceDiagram.puml
@@ -0,0 +1,20 @@
+@startuml
+hide footbox
+actor User
+participant ":UI" as UI
+participant ":GenerateSmartItineraryCommand" as Gen
+participant ":UserDetails" as UDetails
+participant ":FoodList" as Food
+participant ":ActivityList" as Act
+
+User -> UI : smart
+UI -> Gen : create(UserDetails userDetails)
+Gen -> UDetails : getUserPreferences()
+UDetails --> Gen
+Gen -> Food : getCustomisedFood(location, cuisine)
+Food --> Gen
+Gen -> Act : getRandomActivity()
+Act --> Gen
+Gen --> UI : showItinerary(Itinerary)
+UI --> User
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/GiftClassDiagram.puml b/docs/diagrams/GiftClassDiagram.puml
new file mode 100644
index 0000000000..076e4873a9
--- /dev/null
+++ b/docs/diagrams/GiftClassDiagram.puml
@@ -0,0 +1,56 @@
+@startuml
+skinparam style strictuml
+skinparam classAttributeIconSize 0
+
+class Gift {
+ - completionStatus : String
+ - gender : String
+ + Gift(name: String, gender: String, completionStatus: String)
+ + markComplete() : void
+ + getCompletionStatus() : String
+ + getGender() : String
+ + toString() : String
+}
+
+class GiftList {
+ - gifts : List
+ - suggestedGifts : Set
+ + GiftList(gifts : ArrayList)
+ + size() : int
+ + get(i : int) : Gift
+ + getRandomGift(gender : String) : Gift
+}
+
+class GenerateGiftCommand {
+ - gender : String
+ + GenerateGiftCommand(gender : String)
+ + execute(favourites : FavouritesList, foods : FoodList, activities : ActivityList,
+ ui : Ui, storage : Storage, userDetails : UserDetails, gifts : GiftList) : void
+}
+
+class Ui {
+ - scanner : Scanner
+ + errorMessage(message : String) : void
+ + readCommand() : String
+ + listGifts() : void
+}
+
+class Storage {
+ - filePath : String
+ + Storage(filePath : String)
+ + loadGift() : ArrayList
+ + saveGift(gifts : GiftList) : void
+}
+
+class Parser {
+ + parseCommand(userInput : String, userDetails : UserDetails) : Command
+ + parseGiftCommand(arguments : String) : Command
+ + parseGift(line : String) : Gift
+}
+
+Gift "1" -left- "0..*" GiftList : contains
+GenerateGiftCommand "1" -up- "1" GiftList : uses >
+GenerateGiftCommand -down- "1" Ui : interacts >
+GenerateGiftCommand -down- "1" Storage : interacts >
+Parser -right- "1" GenerateGiftCommand : creates >
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/GiftSequenceDiagram.puml b/docs/diagrams/GiftSequenceDiagram.puml
new file mode 100644
index 0000000000..b098318c35
--- /dev/null
+++ b/docs/diagrams/GiftSequenceDiagram.puml
@@ -0,0 +1,48 @@
+@startuml
+skinparam style strictuml
+
+actor User
+participant ":Ui" as UI
+participant ":Parser" as Parser
+participant ":GenerateGiftCommand" as GGC
+participant ":GiftList" as GL
+participant ":Gift" as G
+participant ":Storage" as Storage
+
+User -> UI : input: "gift"
+UI -> Parser : parseCommand("gift", userDetails)
+Parser -> GGC : new GenerateGiftCommand("any")
+activate GGC
+
+GGC -> GL : getRandomGift("any")
+GL -> G : select random
+G --> GL : return gift
+GL --> GGC : return gift
+GGC --> UI : display gift suggestion
+UI -> User : show gift and ask "Satisfied with the gift suggestion? [Yes/No]"
+
+loop until user is satisfied or cancels
+ User -> UI : input: "no"
+ UI -> GGC : continue
+ GGC -> GL : getRandomGift("any")
+ GL -> G : select random
+ G --> GL : return gift
+ GL --> GGC : return gift
+ GGC --> UI : display gift suggestion
+ UI -> User : show gift and ask "Satisfied with the gift suggestion? [Yes/No]"
+end
+
+alt user is satisfied
+ User -> UI : input: "yes"
+ UI -> GGC : user satisfied
+ GGC -> G : markComplete()
+ deactivate GGC
+else user cancels
+ User -> UI : input: "cancel"
+ UI -> GGC : handle cancellation
+ deactivate GGC
+end
+
+GGC -> Storage : saveGift(gifts)
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/Style.puml b/docs/diagrams/Style.puml
new file mode 100644
index 0000000000..a08bc22c02
--- /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
diff --git a/docs/diagrams/UserDetailsCommandSequenceDiagram.puml b/docs/diagrams/UserDetailsCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..46a7c19467
--- /dev/null
+++ b/docs/diagrams/UserDetailsCommandSequenceDiagram.puml
@@ -0,0 +1,28 @@
+@startuml
+hide footbox
+participant "u:Ui" as Ui
+participant "c:UserDetailsCommand" as UserDetailsCommand
+participant "s:Storage" as Storage
+participant "userDetails:UserDetails" as UserDetails
+
+activate UserDetailsCommand
+
+UserDetailsCommand -> Ui: showMessage("Please enter your name:")
+UserDetailsCommand <-- Ui: readCommand()
+UserDetailsCommand -> UserDetails: setName(name)
+
+UserDetailsCommand -> Ui: showMessage("Please enter your age:")
+UserDetailsCommand <-- Ui: readAge()
+UserDetailsCommand -> UserDetails: setAge(age)
+
+alt is "M" or "R" or "D"
+ UserDetailsCommand -> Ui: showMessage("Please enter your anniversary:")
+ UserDetailsCommand <-- Ui: readCommand()
+ UserDetailsCommand -> UserDetails: setAnniversary(anniversary)
+end
+
+UserDetailsCommand -> Storage: saveUserDetails(userDetails)
+UserDetailsCommand -> Ui: showMessage("User details saved successfully!")
+
+deactivate UserDetailsCommand
+@enduml
diff --git a/docs/diagrams/UserDetailsObjectDiagram.puml b/docs/diagrams/UserDetailsObjectDiagram.puml
new file mode 100644
index 0000000000..49231c7144
--- /dev/null
+++ b/docs/diagrams/UserDetailsObjectDiagram.puml
@@ -0,0 +1,13 @@
+@startuml
+
+object "userDetails:UserDetails" as UserDetails {
+ name = "Forky Flirt"
+ age = "30"
+ gender = "Male"
+ location = "C"
+ cuisine = "J"
+ status = "M"
+ anniversary = "13/04/2002"
+}
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/ViewHistoryCommandSequenceDiagram.puml b/docs/diagrams/ViewHistoryCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..96523a6ac5
--- /dev/null
+++ b/docs/diagrams/ViewHistoryCommandSequenceDiagram.puml
@@ -0,0 +1,34 @@
+actor User as User
+
+User -> Ui : history
+Ui -> ViewHistoryCommand: ViewHistoryCommand()
+
+ViewHistoryCommand -> ActivityList
+ loop activities.size()
+ ActivityList -> Activity : get()
+ Activity -> Activity : getCompletionStatus()
+
+ alt getCompletionStatus().equals("C")
+ Activity -> User : print()
+ end
+ end
+
+ViewHistoryCommand -> FoodList
+ loop foods.size()
+ FoodList -> Food : get()
+ Food -> Food : getCompletionStatus()
+
+ alt getCompletionStatus().equals("C")
+ Food -> User : print()
+ end
+ end
+
+ViewHistoryCommand -> GiftList
+ loop gifts.size()
+ GiftList -> Gift : get()
+ Gift -> Gift : getCompletionStatus()
+
+ alt getCompletionStatus().equals("C")
+ Gift -> User : print()
+ end
+ end
\ No newline at end of file
diff --git a/docs/images/FoodActivityClassDiagram.png b/docs/images/FoodActivityClassDiagram.png
new file mode 100644
index 0000000000..3553879f1d
Binary files /dev/null and b/docs/images/FoodActivityClassDiagram.png differ
diff --git a/docs/images/GenerateIdeaActivityDiagram.png b/docs/images/GenerateIdeaActivityDiagram.png
new file mode 100644
index 0000000000..52087aeb9e
Binary files /dev/null and b/docs/images/GenerateIdeaActivityDiagram.png differ
diff --git a/docs/images/GenerateIdeaCommandSequenceDiagram.png b/docs/images/GenerateIdeaCommandSequenceDiagram.png
new file mode 100644
index 0000000000..5fd820ccea
Binary files /dev/null and b/docs/images/GenerateIdeaCommandSequenceDiagram.png differ
diff --git a/docs/images/GenerateIdeaObjectDiagram.png b/docs/images/GenerateIdeaObjectDiagram.png
new file mode 100644
index 0000000000..e6cb891d95
Binary files /dev/null and b/docs/images/GenerateIdeaObjectDiagram.png differ
diff --git a/docs/images/GenerateSmartItineraryCommandSequenceDiagram.png b/docs/images/GenerateSmartItineraryCommandSequenceDiagram.png
new file mode 100644
index 0000000000..affb53809c
Binary files /dev/null and b/docs/images/GenerateSmartItineraryCommandSequenceDiagram.png differ
diff --git a/docs/images/GiftClassDiagram.png b/docs/images/GiftClassDiagram.png
new file mode 100644
index 0000000000..76dd0c1122
Binary files /dev/null and b/docs/images/GiftClassDiagram.png differ
diff --git a/docs/images/GiftSequenceDiagram.png b/docs/images/GiftSequenceDiagram.png
new file mode 100644
index 0000000000..144e445e1b
Binary files /dev/null and b/docs/images/GiftSequenceDiagram.png differ
diff --git a/docs/images/UserDetailsCommandSequenceDiagram.png b/docs/images/UserDetailsCommandSequenceDiagram.png
new file mode 100644
index 0000000000..e93f591483
Binary files /dev/null and b/docs/images/UserDetailsCommandSequenceDiagram.png differ
diff --git a/docs/images/UserDetailsObjectDiagram.png b/docs/images/UserDetailsObjectDiagram.png
new file mode 100644
index 0000000000..08d0a0e2ae
Binary files /dev/null and b/docs/images/UserDetailsObjectDiagram.png differ
diff --git a/docs/images/ViewHistoryCommandSequenceDiagram.png b/docs/images/ViewHistoryCommandSequenceDiagram.png
new file mode 100644
index 0000000000..d33fe4f26f
Binary files /dev/null and b/docs/images/ViewHistoryCommandSequenceDiagram.png differ
diff --git a/docs/team/liuy1103.md b/docs/team/liuy1103.md
new file mode 100644
index 0000000000..261d9629b6
--- /dev/null
+++ b/docs/team/liuy1103.md
@@ -0,0 +1,55 @@
+# Liu Yiting - Project Portfolio Page (PPP)
+
+## Project: Flirt & Fork
+
+_Flirt & Fork_ is a CLI-based dating application designed to simplify and enhance the dating experience, offering a unique blend of functionality and creativity in planning dates.
+
+### Summary of Contributions
+
+Given below are my contributions to the project.
+
+**New features added and enhancements to existing feature**
+
+Introduced `GenerateGiftCommand`, a gift idea generator feature aimed at assisting users who may struggle with gift-giving decisions.
+- Designed and populated the gift database with a diverse collection of gift ideas categorised by gender demographics.
+- Developed the feature logic, including algorithms to randomly generate gift suggestions [#48](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/48), [#84](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/84).
+- Enhanced the gift idea generator feature by adding gender-specific filters (`male`, `female`, and `unisex`) [#173](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/173), [#193](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/193).
+- Integrated the feature into the existing app interface with instructions.
+- Enhanced `ListOptionsCommand` to allow for continuous listing until a "cancel" command is issued, enhancing user flexibility and control [#188](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/188).
+
+**Challenges overcome**
+- Encountered initial difficulties with filtering algorithms, which were resolved through adopting the Java Stream API to efficiently filter gift options based on gender attributes.
+- Overcome hurdles related to database integration in JAR file deployment, ensuring users have access to the curated database upon downloading the application [#30](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/30).
+
+**Project management and Contributions to Team-Based Tasks**
+- Laid the foundational framework of our application, which formed the basic architecture [#9](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/9).
+ - Developed classes such as `Command`, `Parser`, `UI`, and `Storage` for the rest of the team to build upon.
+ - Developed initial implementations for various command classes including `AddActivityCommand`, `ExitCommand`, `FindOptionsCommand` etc.
+ - Crafted foundational model classes including `Favourites`, `Activity`, `FavouritesList` and `Food`, setting the data structure and functionalities for managing different data types within the application.
+- Crisis management and addressed critical last-minute database access issue discovered before our v1.0 release.
+- Identify bugs related to the database's availability among other usability concerns.
+
+**Testing** [#28](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/28), [#201](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/201), [#202](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/202)
+ - Authored JUnit tests for features implemented by me.
+ - Add additional JUnit tests for features implemented by other team members (`Parser` and `Ui` classes).
+
+**Documentation**
+ - **User Guide:** Revised and update the guide to improve usability [#52](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/52), [#187](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/187).
+ - Provided detailed step-by-step instructions supplemented with examples to facilitate user understanding.
+ - Logical organisation of content to guide users through the application's features.
+ - **Developer Guide:** Managed the documentation for the gift feature [#204](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/204).
+ - Updated the guide to include implementation and operational logic of the gift suggestion feature.
+ - Updated sequence diagram to illustrate the process flow for generating new gift suggestions.
+ - Refined class diagram to depict the structural relationships and design of the gift feature.
+
+**Code contributed**
+- Contributed over 1,000 lines of code, encompassing backend logic for new features and database integration.
+- [PRs authored by me](https://github.com/AY2324S2-CS2113-T11-2/tp/pulls?q=is%3Apr+author%3Aliuy1103+) (23+ PRs authored and merged)
+
+For a detailed overview of my contributions, please refer to my [RepoSense link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=liuy1103&breakdown=true).
+
+**Community**
+- [Team's PRs reviewed by me](https://github.com/AY2324S2-CS2113-T11-2/tp/pulls?q=is%3Apr+reviewed-by%3Aliuy1103+)
+- [Other teams' PRs reviewed by me](https://github.com/nus-cs2113-AY2324S2/tp/pulls?q=is%3Apr+reviewed-by%3Aliuy1103) - reviewed DG of 2 teams.
+- [Bug reports raised by me part I](https://github.com/liuy1103/ped/issues) - posted 4 bug reports to help other team improve their product.
+- [Bug reports raised by me part II](https://github.com/AY2324S2-CS2113-F15-2/tp/issues/created_by/liuy1103) - posted 2 bug reports to help other team improve their product.
diff --git a/docs/team/ryantdl.md b/docs/team/ryantdl.md
new file mode 100644
index 0000000000..6b1eb085ae
--- /dev/null
+++ b/docs/team/ryantdl.md
@@ -0,0 +1,52 @@
+# Project: Flirt & Fork
+
+Flirt & Fork is a command-line interface (CLI) application designed to simplify the date planning process for busy couples. Flirt & Fork offers a seamless, personalised date planning experience, that guarantees fun and engaging date ideas!
+
+# Summary of Contributions
+
+## Code contributed
+An overview of my code contributions can be found [here](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=ryantdl&breakdown=true)
+
+## Enhancements implemented
+Given below are my contributions to the project enhancements
+
+- **New Feature**: Generating a date idea
+ - What it does: generates out a random date activity and restaurant that the user can visit. No repeated activities or restaurants will be generated, so as to keep date ideas fresh and exciting
+ - Justification: This feature improves the product significantly as it addresses the main value proposition of our application, which is streamlining the date planning process. If users remain unsatisfied with the current idea, they can continuously regenerate new ideas. Moreover, as we populated our database with a sufficiently large amount of restaurant and activities, a diverse range of date ideas can be generated.
+
+- **New Feature**: Generating a date itinerary
+ - What it does: allows the user to generate out a full-day date itinerary, consisting of 2 meals and 2 activities. Based on the user's input for preferred location and pricing, our food/activity database is filtered through and a randomised itinerary is generated
+ - Justification: This feature builds upon the functionality offered by the 'idea' command. Through scanning user inputs and understanding their preferences, a more personalised date plan can be generated for them. Similar to the date idea generation feature, as we populated our database with a sufficiently large amount of restaurant and activities, a diverse range of date itineraries can be generated
+
+- **New Feature**: Tracking user's past date history
+ - What it does: automatically updates the relevant food/activity/gift entries in our database, marking them as completed. The system registers these entries as having been done, and thus will not reuse them in future idea/itinerary generations.
+ - Justification: This feature improves the product significantly as users will not have to revisit restaurants and activities previously completed. It ensures that the system can continue to provide fresh date ideas to couples
+
+
+## Documentation
+- Contributions to the User Guide
+ - Authored the introduction, providing users with a brief overview of the features and value proposition of our application.
+ - Authored the section on the `food` command.
+ - Authored the section on the `activity` command.
+ - Extended the "Legend" section and the "Command Summary" section.
+ - Updated the relevant sections based on changes due to code refactoring.
+
+- Contributions to the Developer Guide
+ - Described the `ViewHistoryCommand` and its interaction within the system, which displays and updates user's previous date itineraries within the database.
+ - Detailed the implementation of the `Food` and `Activity` classes, detailing the logic behind the classes.
+ - Added a sequence diagram for `ViewHistoryCommand` to explain its interactions with other classes during its execution.
+ - Added class diagrams for `Food` and `Activity` to describe their structure.
+ - Authored the "Product scope" section and the "Non-Functional Requirements" section to outline the features and functionalities required by our users, and how these were derived by the team
+
+## Contributions to team-based tasks
+- Project Management:
+ - Setting up thr GitHub team organisation/repository
+ - Maintaining the issue tracker
+ - Documenting product scope and requirements
+
+## Review/Mentoring contributions:
+- Provided assistance to team members when asking for help online
+- Reviewed pull requests to ensure consistency and quality of code.
+
+## Contributions beyond the project team:
+- Exchanged JAR files with team F15-2. Did extensive user testing and raised relevant issues found during testing, using the [issue tracker](https://github.com/AY2324S2-CS2113-F15-2/tp/issues).
diff --git a/docs/team/seandooa.md b/docs/team/seandooa.md
new file mode 100644
index 0000000000..24be3f701f
--- /dev/null
+++ b/docs/team/seandooa.md
@@ -0,0 +1,42 @@
+# Project: Flirt & Fork
+
+Flirt & Fork is a command-line interface (CLI) application that aims to help users plan for dates. Users will be able to create an itinerary based on specific conditions, let the app suggest one for them based on their personal details or try out something new with a completely random idea. With its extensive database of dining options, activities and gifts, Flirt & Fork will be able to provide a suitable itinerary for all users no matter their budget, preferences or location.
+# Summary of Contributions
+
+## Code contributed
+An overview of my code contributions can be found [here](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=seandooa&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-02-23&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+## Enhancements implemented
+Here are the enhancements I implemented
+
+- **Improved User Details Command**: Edited the related code to ensure that the command is run automatically at first time start up so the app will always have user data. Implemented error handling for inputting all fields of user data, ranging from strings like Gender to numbers like age. Refactored the `UserDetailsCommand` class to update the existing `UserDetails` object instead of creating a new one to fix a bug in saving to storage.
+- **Improved Generate Idea Command**: Implemented a feature where inputting `no` after an idea is generated will regenerate a new idea, and will continue until the user expresses satisfaction or decides to cancel.
+- **Base Functions of Initial Codebase**: Created the initial Food and Activity classes, the FoodList and ActivityList classes as well as their respective parsers.
+
+### Testing
+- Contributed J-Unit tests for GenerateIdeaCommand, ListOptionsCommand and UserDetailsCommand classes.
+
+### Contributions to the User Guide (UG)
+- Update various command sections with their expected outputs.
+- Authored the section describing the `me` command.
+
+### Contributions to the Developer Guide (DG)
+- Authored the section on `GenerateIdeaCommand` and elaborated on the logic of its execution.
+- Added an activity diagram for `GenerateIdeaCommand` to facilitate understanding of its description.
+- Added a sequence diagram for `GenerateIdeaCommand` to explain its interactions with other classes during its execution.
+- Added an object diagram for `GenerateIdeaCommand` to illustrate the `idea` object more clearly in relation to other objects.
+
+### Contributions to team-based tasks
+- Attempted to fix the bug in release v1.0 where the database was not saved as we did not create a resources directory yet. Bug was eventually fixed by @liuyi1103.
+- Took charge of the v2.0b release. v2.0b had a bug in the jar file which was not present when the program was run with gradle. Did the last-minute urgent bug fix and was able to publish the release before the stipulated deadline. Link to the issue can be found [here](https://github.com/AY2324S2-CS2113-T11-2/tp/issues/86). Another bug caused by us not knowing data could not be written to the resources directory once the jar file is made was also flagged out by me and slated to be fixed in the next release.
+- Helped with maintaining the issue tracker.
+
+### Review/mentoring contributions
+- Authored several PR reviews.
+- Helped some team members with learning how to use shadowJar.
+- Reply as soon as possible whenever team members are asking for assistance in the group chat, providing helpful advice or solving the problem alongside them.
+
+## Contributions beyond the project team
+- Highlighted 7-8 non-trivial bugs for the team I was assigned to during the PE dry run. This ranged from serious issues with the User Guide pdf formatting to feature flaws. Also gave advice on not merging PRs that do not pass the GitHub checks since merged PRs with failed checks could be seen on their User Guide.
+- Did [DG Review](https://github.com/nus-cs2113-AY2324S2/tp/pull/18) during tutorial.
+- Will be exchanging jar files with another team for one last round of extensive user testing before final submission. Issues raised can be found [here](https://github.com/AY2324S2-CS2113-F15-2/tp/issues/created_by/seandooa).
\ No newline at end of file
diff --git a/docs/team/soongensayo.md b/docs/team/soongensayo.md
new file mode 100644
index 0000000000..138bdd1a0e
--- /dev/null
+++ b/docs/team/soongensayo.md
@@ -0,0 +1,43 @@
+# Project Portfolio Page (PPP): soongensayo
+
+## Overview
+*Flirt and Fork* is an innovative application designed to create smart itineraries for individuals based on personal preferences and data-driven insights. The application is built to streamline the process of planning outings and events by providing curated suggestions that can be tailored to user profiles.
+
+## Summary of Contributions
+
+### Code Contributed
+You can find my code contributions in the [tP Code Dashboard](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=soongensayo&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-02-23&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other).
+
+### Enhancements Implemented
+- **UserDetailsCommand**: Implemented the command that captures user details and preferences for itinerary generation.
+- **Smart Itinerary**: Developed the logic that creates smart itineraries based on user preferences, ensuring a tailored experience.
+- **Help Command**: Crafted a comprehensive help command to assist users in navigating through the application with ease.
+
+### Contributions to the User Guide (UG)
+- Authored the section on the `me` command, providing users with instructions on how to set up their profiles.
+- Authored the section on the `help` command.
+- Authored the "Legend" section and the "FAQ" section.
+- Documented the usage of the `smart` command, guiding users on how to get personalized itinerary suggestions.
+
+### Contributions to the Developer Guide (DG)
+- Described the `UserDetailsCommand` and its interaction with the system to capture user information.
+- Elaborated on the implementation of `smart` (smart itinerary generation command), detailing the algorithm and the logic behind the feature.
+- Added UML diagrams to visualize the flow and structure of `smart` (smart itinerary generation command).
+
+### Contributions to Team-based Tasks
+- Proposed App Idea and provided the initial working database for Flirt and Fork.
+- Drafted the initial draft of the Demo video script.
+- Helped to maintain the Issue Tracker.
+- Fixed bugs discovered during PE Dry Run and Peer Testing.
+
+### Review Contributions
+- Reviewed pull requests to ensure consistency and quality of code, and gave encouraging comments. Example: [PR #152](https://github.com/AY2324S2-CS2113-T11-2/tp/pull/152#issuecomment-2045649394)
+
+### Contributions beyond project team:
+- Exchanged JAR files with team F15-2. Did extensive user testing and raised relevant issues found during testing, using the [issue tracker](https://github.com/AY2324S2-CS2113-F15-2/tp/issues).
+
+---
+
+**Reflection**: Working on Flirt Fork has been a remarkable journey where I not only applied my software engineering skills but also developed new ones. Crafting features like the `Smart Itinerary` challenged my problem-solving abilities and enhanced my understanding of user-centric design. Collaborating with the team, mentoring, and contributing to the CS2113 community were equally fulfilling experiences that underscored the importance of teamwork and knowledge sharing.
+
+---
diff --git a/docs/team/tiffanyliu0220.md b/docs/team/tiffanyliu0220.md
new file mode 100644
index 0000000000..c78ae91646
--- /dev/null
+++ b/docs/team/tiffanyliu0220.md
@@ -0,0 +1,48 @@
+# Liu Ting Yu - Project Portfolio Page (PPP)
+
+## Project: Flirt & Fork
+
+Flirt & Fork is a command-line interface (CLI) application designed to simplify the date planning process for busy couples. Flirt & Fork offers a seamless, personalised date planning experience, that guarantees fun and engaging date ideas!
+
+### Summary of Contributions
+
+### Code contributed
+An overview of my code contributions can be found [here](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=tiffanyliu0220&breakdown=true)
+
+### Enhancements implemented
+Given below are my contributions to the project enhancements
+
+- **New Feature**: Displaying the Overall Food List
+ - Displays the overall food list that we have stored in the database
+
+- **New Feature**: Displaying the Overall Activity List
+ - Displays the overall activity list that we have stored in the database
+
+- **New Feature**: Finding entries from the Food List, Activity List, Gift List from the database and from user's Favourites List
+ - Displays the matched list for food, activities, gifts and favourites
+
+### Documentation
+- Contributions to the User Guide
+ - authored the 'find' feature that I implemented along with the instructions for usage
+ - corrected spelling and grammatical errors in the UG
+ - ensured that the terminologies used were consistent
+
+- Contributions to the Developer Guide
+ - summarised the features and classes we have implemented in the Glossary
+ - added in instructions for user manual testing
+
+### Contributions to team-based tasks
+- added in error handling for classes and commands such as 'Favourites', 'FavouritesList', 'FindOptionsCommand', 'ListFavouritesCommand', etc.
+- added in JUnit Tests for favourites-related commands
+- added in JavaDoc documentation for classes such as 'Storage', 'Ui', etc.
+- added in JavaDoc documentation for commands such as 'AddActivityCommand', 'AddFoodCommand', 'DeleteFavourites', etc.
+- fixed bug issues raised in PE dry run and peer testing
+- updated/resolved the issue tracker for team bugs
+
+### Review/Mentoring contributions
+- tried to assist team members whenever I can in the group chat
+
+### Contributions beyond the project team
+- reviewed other team's TP during PE dry run and provided comments
+- reviewed other team's DG during tutorial and provided comments
+- reviewed a peer team's TP and provided comments. The issues I have raised can be found [here](https://github.com/AY2324S2-CS2113-F15-2/tp/issues/created_by/tiffanyliu0220)
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/flirtfork/Activity.java b/src/main/java/seedu/flirtfork/Activity.java
new file mode 100644
index 0000000000..74594e4fe2
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/Activity.java
@@ -0,0 +1,51 @@
+package seedu.flirtfork;
+
+/**
+ * Represents a date activity consisting of:
+ * A description, location, price and completion status.
+ * The completion status indicates whether an activity idea has been accepted and marked as complete.
+ */
+public class Activity extends Favourites {
+ protected String location;
+ protected String price;
+ protected String completionStatus;
+
+ /**
+ * Constructs an activity based on inputs.
+ *
+ * @param description The name of the activity.
+ * @param location The location of the activity.
+ * @param price The price category of the activity.
+ * @param completionStatus The status of whether the activity has been done prior.
+ */
+ public Activity(String description, String location, String price, String completionStatus) {
+ super(description);
+ this.location = location;
+ this.price = price;
+ this.completionStatus = completionStatus;
+ }
+
+ /**
+ * Returns the description of the activity.
+ * @return String description of the activity.
+ */
+ @Override
+ public String toString() {
+ return description;
+ }
+
+ /**
+ * Updates the completion status of the activity, marking it as having been completed.
+ */
+ public void markComplete() {
+ this.completionStatus = "C";
+ }
+
+ /**
+ * Returns the completion status of the activity.
+ * @return completionStatus Completion status of the activity.
+ */
+ public String getCompletionStatus() {
+ return completionStatus;
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/ActivityList.java b/src/main/java/seedu/flirtfork/ActivityList.java
new file mode 100644
index 0000000000..008e590884
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/ActivityList.java
@@ -0,0 +1,125 @@
+package seedu.flirtfork;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * Represents a list of activities.
+ */
+public class ActivityList {
+ private ArrayList activities;
+
+ /**
+ * Constructs an empty activity list.
+ */
+ public ActivityList() {
+ this.activities = new ArrayList<>();
+ }
+
+ /**
+ * Constructs an activity list with the specified activities.
+ *
+ * @param activities The list of activities.
+ */
+ public ActivityList(ArrayList activities) {
+ this.activities = activities;
+ }
+
+ /**
+ * Retrieves the activity at the specified index.
+ *
+ * @param i The index of the activity to retrieve.
+ * @return The activity at the specified index.
+ */
+ public Activity get(int i) {
+ return activities.get(i);
+ }
+
+ /**
+ * Adds a new activity to the list.
+ *
+ * @param newActivity The activity to add.
+ */
+ public void add(Activity newActivity) {
+ activities.add(newActivity);
+ }
+
+ /**
+ * Retrieves the number of activities in the list.
+ *
+ * @return The number of activities.
+ */
+ public int size() {
+ return activities.size();
+ }
+
+ /**
+ * Retrieves a random activity from the list.
+ *
+ * @return A random activity.
+ */
+ public Activity getRandomActivity() {
+ Activity randomActivity;
+ do {
+ Random random = new Random();
+ int activityIndex = random.nextInt(activities.size());
+ randomActivity = activities.get(activityIndex);
+ } while (randomActivity.completionStatus.equals("C"));
+ return randomActivity;
+ }
+
+ /**
+ * Retrieves a filtered activity based on preferred location and price.
+ *
+ * @param preferredLocation The preferred location.
+ * @param preferredPrice The preferred price.
+ * @return A filtered activity.
+ * @throws FlirtForkException If not enough activity options are available.
+ */
+ public Activity getFilteredActivity(String preferredLocation, String preferredPrice) throws FlirtForkException {
+ ArrayList filteredActivities = new ArrayList<>();
+ for (Activity eachActivity : activities) {
+ if (eachActivity.location.equals(preferredLocation) &&
+ eachActivity.price.equals(preferredPrice) && eachActivity.completionStatus.equals("U")) {
+ filteredActivities.add(eachActivity);
+ }
+ }
+
+ if (filteredActivities.size()<=1) {
+ throw new FlirtForkException("Not enough activity options");
+ } else {
+ Random random = new Random();
+ int filteredActivityIndex = random.nextInt(filteredActivities.size());
+ return filteredActivities.get(filteredActivityIndex);
+ }
+
+ }
+
+ /**
+ * Retrieves a customised activity based on user location.
+ *
+ * @param userLocation The user's location.
+ * @return A customised activity.
+ * @throws FlirtForkException If not enough activity options are available.
+ */
+ public Activity getCustomisedActivity(String userLocation) throws FlirtForkException {
+ ArrayList filteredActivities = new ArrayList<>();
+ for (Activity eachActivity : activities) {
+ if (eachActivity.location.equals(userLocation) &&
+ eachActivity.completionStatus.equals("U")) {
+ filteredActivities.add(eachActivity);
+ }
+ }
+
+ if (filteredActivities.size()<=1) {
+ throw new FlirtForkException("Not enough activity options");
+ } else {
+ Random random = new Random();
+ int filteredActivityIndex = random.nextInt(filteredActivities.size());
+ return filteredActivities.get(filteredActivityIndex);
+ }
+
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/Command.java b/src/main/java/seedu/flirtfork/Command.java
new file mode 100644
index 0000000000..ec057b23d0
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/Command.java
@@ -0,0 +1,27 @@
+package seedu.flirtfork;
+
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * Abstract class for representing a command within the application.
+ * All specific command types inherit from this class and implement the execute method,
+ * defining how they interact with the application's state.
+ */
+public abstract class Command {
+ /**
+ * Executes the specific command and modifies the application's state accordingly.
+ *
+ * @param favourites The list of user's favourite items.
+ * @param foods The list of foods available.
+ * @param activities The list of activities available.
+ * @param ui The user interface component used for all user interactions.
+ * @param storage The storage component used for data persistence.
+ * @param userDetails The details about the user necessary for some operations.
+ * @param gifts The list of gifts available.
+ * @throws FlirtForkException if there is an issue during the execution of the command.
+ */
+ public abstract void execute(FavouritesList favourites, FoodList foods, ActivityList activities,
+ Ui ui, Storage storage, UserDetails userDetails, GiftList gifts)
+ throws FlirtForkException;
+
+}
diff --git a/src/main/java/seedu/flirtfork/Favourites.java b/src/main/java/seedu/flirtfork/Favourites.java
new file mode 100644
index 0000000000..1e81b55b96
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/Favourites.java
@@ -0,0 +1,28 @@
+package seedu.flirtfork;
+
+/**
+ * Represents a favourited item consisting of a description.
+ */
+public class Favourites {
+ protected String description;
+
+ public Favourites(String description) {
+ if (description == null) {
+ throw new IllegalArgumentException("Description cannot be null");
+ }
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String toString() {
+ return description;
+ }
+
+ public String toFileFormat() {
+ return description;
+ }
+
+}
diff --git a/src/main/java/seedu/flirtfork/FavouritesList.java b/src/main/java/seedu/flirtfork/FavouritesList.java
new file mode 100644
index 0000000000..7ffb9d4d22
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/FavouritesList.java
@@ -0,0 +1,84 @@
+package seedu.flirtfork;
+
+import java.util.ArrayList;
+
+/**
+ * Represents a list of favorite items.
+ */
+public class FavouritesList {
+ private ArrayList favourites;
+
+ /**
+ * Constructs an empty list of favorite items.
+ */
+ public FavouritesList() {
+ this.favourites = new ArrayList<>();
+ }
+
+ /**
+ * Constructs a list of favorite items from an existing ArrayList.
+ *
+ * @param favourites The ArrayList containing favorite items.
+ */
+ public FavouritesList(ArrayList favourites) {
+ this.favourites = favourites;
+ }
+
+ /**
+ * Adds a favorite item to the list.
+ *
+ * @param favourite The favorite item to add.
+ */
+ public void addFavourite(Favourites favourite) {
+ favourites.add(favourite);
+ }
+
+ /**
+ * Deletes a favorite item from the list at the specified index.
+ *
+ * @param index The index of the favorite item to delete.
+ * @throws IndexOutOfBoundsException if the index is out of bounds.
+ */
+ public void deleteFavourite(int index) throws IndexOutOfBoundsException{
+ if (index >= 0 && index < favourites.size()) {
+ favourites.remove(index);
+ } else {
+ throw new IndexOutOfBoundsException("Index is out of bounds: " + index);
+ }
+ }
+
+ /**
+ * Retrieves the list of favorite items.
+ *
+ * @return The ArrayList containing favorite items.
+ */
+ public ArrayList getFavourites() {
+ return favourites;
+ }
+
+ /**
+ * Checks if the list of favorite items is empty.
+ *
+ * @return true if the list is empty, false otherwise.
+ */
+ public boolean isEmpty() {
+ return favourites.isEmpty();
+ }
+
+ /**
+ * Formats the list of favorite items into a string.
+ *
+ * @return A formatted string representation of the list of favorite items.
+ */
+ public String getFormattedFavourites() {
+ if(favourites.isEmpty()) {
+ return "A blank canvas! Let's fill it with some love~";
+ } else {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < favourites.size(); i++) {
+ sb.append((i + 1)).append(". ").append(favourites.get(i).toString()).append("\n");
+ }
+ return sb.toString().trim();
+ }
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/FlirtFork.java b/src/main/java/seedu/flirtfork/FlirtFork.java
new file mode 100644
index 0000000000..587a738e11
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/FlirtFork.java
@@ -0,0 +1,96 @@
+package seedu.flirtfork;
+
+import seedu.flirtfork.commands.ExitCommand;
+import seedu.flirtfork.commands.UserDetailsCommand;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * The main class that initializes and runs the FlirtFork application.
+ * It sets up the environment, loads necessary data,
+ * and handles the main interaction loop where commands are processed.
+ */
+public class FlirtFork {
+ private static final String FILE_PATH = "./data/FlirtFork.txt";
+
+ private static final String HORIZONTAL = "____________________________________________________________";
+ private static FoodList foods;
+ private FavouritesList favourites;
+ private ActivityList activities;
+ private Ui ui;
+ private Storage storage;
+ private UserDetails userDetails;
+ private GiftList gifts;
+
+ /**
+ * Constructor for FlirtFork class.
+ *
+ * @param filePath The file path to store data.
+ */
+ public FlirtFork(String filePath) {
+ ui = new Ui();
+ storage = new Storage(filePath);
+ try {
+ favourites = storage.loadFavourites();
+ userDetails = storage.loadUserDetails();
+
+ if (userDetails.getName().equals("NOT SET")) {
+ foods = new FoodList(storage.loadFoodFirstTime());
+ activities = new ActivityList(storage.loadActivityFirstTime());
+ gifts = new GiftList(storage.loadGiftFirstTime());
+ } else {
+ foods = new FoodList(storage.loadFood());
+ activities = new ActivityList(storage.loadActivity());
+ gifts = new GiftList(storage.loadGift());
+ }
+ } catch (FileNotFoundException e) {
+ ui.errorMessage("File not found. Starting with an empty task list :)");
+ favourites = new FavouritesList(new ArrayList<>());
+ }
+ }
+
+
+ /**
+ * Starts the application's main loop.
+ * It handles user input, processes commands, and continues until an exit command is called.
+ * Depending on whether the user details are set, it may prompt for initial setup.
+ */
+ public void run() {
+ if (userDetails.getName().equals("NOT SET")) {
+ ui.firstSetUpMessage();
+ UserDetailsCommand userDetailsCommand = new UserDetailsCommand();
+ userDetailsCommand.execute(favourites, foods, activities, ui, storage, userDetails, gifts);
+ } else {
+ ui.greetingMessage(userDetails.getAnniversary());
+ }
+
+ boolean isExit = false;
+ while(!isExit) {
+ String userInput = ui.readCommand();
+ try {
+ Command command = Parser.parseCommand(userInput, userDetails);
+ command.execute(favourites, foods, activities, ui, storage, userDetails, gifts);
+ if(command instanceof ExitCommand) {
+ isExit = true;
+ }
+ System.out.println(HORIZONTAL);
+ } catch (FlirtForkException e) {
+ ui.errorMessage(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Main entry point of the application.
+ * It creates an instance of FlirtFork and starts the application.
+ *
+ * @param args The command line arguments passed to the application.
+ */
+ public static void main(String[] args) {
+ FlirtFork flirtFork = new FlirtFork(FILE_PATH);
+ assert foods.get(0).toString().equals("25 Degrees") : "first entry in food database must be 25 degrees";
+ flirtFork.run();
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/Food.java b/src/main/java/seedu/flirtfork/Food.java
new file mode 100644
index 0000000000..3107a1f792
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/Food.java
@@ -0,0 +1,54 @@
+package seedu.flirtfork;
+
+/**
+ * Represents a restaurant location consisting of:
+ * A description, location, price, cuisine and completion status.
+ * The completion status indicates whether a restaurant has been accepted and marked as complete.
+ */
+public class Food extends Favourites {
+ protected String location;
+ protected String price;
+ protected String cuisine;
+ protected String completionStatus;
+
+ /**
+ * Constructs a restaurant location based on inputs.
+ *
+ * @param description The name of the restaurant.
+ * @param location The location of the restaurant.
+ * @param price The price category of the restaurant.
+ * @param cuisine The cuisine category of the restaurant.
+ * @param completionStatus The status of whether the restaurant has been visited prior.
+ */
+ public Food(String description, String location, String price, String cuisine, String completionStatus) {
+ super(description);
+ this.location = location;
+ this.price = price;
+ this.cuisine = cuisine;
+ this.completionStatus = completionStatus;
+ }
+
+ /**
+ * Returns the Name of the restaurant.
+ * @return description Name of the restaurant.
+ */
+ @Override
+ public String toString() {
+ return (description);
+ }
+
+ /**
+ * Updates the completion status of the restaurant, marking it as having been visited.
+ */
+ public void markComplete() {
+ this.completionStatus = "C";
+ }
+
+ /**
+ * Returns the completion status of the restaurant.
+ * @return completionStatus Completion status of the restaurant.
+ */
+ public String getCompletionStatus() {
+ return completionStatus;
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/FoodList.java b/src/main/java/seedu/flirtfork/FoodList.java
new file mode 100644
index 0000000000..a4938fe6bd
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/FoodList.java
@@ -0,0 +1,139 @@
+package seedu.flirtfork;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * Represents a list of food options.
+ */
+public class FoodList {
+ private ArrayList foods;
+
+ /**
+ * Constructs an empty food list.
+ */
+ public FoodList() {
+ this.foods = new ArrayList<>();
+ }
+
+ /**
+ * Constructs a food list with the specified food options.
+ *
+ * @param foods The list of food options.
+ */
+ public FoodList(ArrayList foods) {
+ this.foods = foods;
+ }
+
+ /**
+ * Retrieves the number of food options in the list.
+ *
+ * @return The number of food options.
+ */
+ public int size() {
+ return foods.size();
+ }
+
+ /**
+ * Retrieves the food option at the specified index.
+ *
+ * @param i The index of the food option to retrieve.
+ * @return The food option at the specified index.
+ */
+ public Food get(int i) {
+ return foods.get(i);
+ }
+
+ /**
+ * Adds a new food option to the list.
+ *
+ * @param newFood The food option to add.
+ */
+ public void add(Food newFood) {
+ foods.add(newFood);
+ }
+
+ /**
+ * Retrieves a random food option from the list.
+ *
+ * @return A random food option.
+ */
+ public Food getRandomFood() {
+ Food randomFood;
+ do {
+ Random random = new Random();
+ int foodIndex = random.nextInt(foods.size());
+ randomFood = foods.get(foodIndex);
+ } while (randomFood.completionStatus.equals("C"));
+ return randomFood;
+ }
+
+ /**
+ * Retrieves a filtered food option based on preferred location and price.
+ *
+ * @param preferredLocation The preferred location.
+ * @param preferredPrice The preferred price.
+ * @return A filtered food option.
+ * @throws FlirtForkException If not enough food options are available.
+ */
+ public Food getFilteredFood(String preferredLocation, String preferredPrice) throws FlirtForkException{
+ ArrayList filteredFoods = new ArrayList<>();
+ for (Food eachFood : foods) {
+ if (eachFood.location.equals(preferredLocation) &&
+ eachFood.price.equals(preferredPrice) && eachFood.completionStatus.equals("U")) {
+ filteredFoods.add(eachFood);
+ }
+ }
+
+ if (filteredFoods.size()<=1) {
+ throw new FlirtForkException("Not enough food options");
+ } else {
+ Random random = new Random();
+ int filteredFoodIndex = random.nextInt(filteredFoods.size());
+ return filteredFoods.get(filteredFoodIndex);
+ }
+ }
+
+ /**
+ * Retrieves a customised food option based on preferred location and cuisine.
+ *
+ * @param preferredLocation The preferred location.
+ * @param preferredCuisine The preferred cuisine.
+ * @return A customised food option.
+ * @throws FlirtForkException If not enough food options are available.
+ */
+ public Food getCustomisedFood(String preferredLocation, String preferredCuisine) throws FlirtForkException{
+ ArrayList filteredFoods = new ArrayList<>();
+ for (Food eachFood : foods) {
+ if (eachFood.location.equals(preferredLocation) &&
+ eachFood.cuisine.equals(preferredCuisine) && eachFood.completionStatus.equals("U")) {
+ filteredFoods.add(eachFood);
+ }
+ }
+
+ if (filteredFoods.size()<=1) {
+ throw new FlirtForkException("Not enough food options");
+ } else {
+ Random random = new Random();
+ int filteredFoodIndex = random.nextInt(filteredFoods.size());
+ return filteredFoods.get(filteredFoodIndex);
+ }
+ }
+
+
+ public ArrayList findFood(String keyword) {
+ ArrayList matchingFoods = new ArrayList<>();
+
+ for(Food food : foods) {
+ String description = food.getDescription();
+ if (description.contains(keyword)) { //need to make sure the search is not case-sensitive
+ matchingFoods.add(food);
+ }
+ }
+ return matchingFoods;
+ }
+}
+
+
diff --git a/src/main/java/seedu/flirtfork/Gift.java b/src/main/java/seedu/flirtfork/Gift.java
new file mode 100644
index 0000000000..b29fcbcc2c
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/Gift.java
@@ -0,0 +1,56 @@
+package seedu.flirtfork;
+
+/**
+ * A gift item with a description, gender classification, and completion status.
+ * The completion status indicates whether a gift has been accepted and marked as complete.
+ */
+public class Gift extends Favourites {
+
+ protected String completionStatus;
+ protected String gender; // M, F, U, any
+
+ /**
+ * Constructs a Gift with a name, gender, and completion status.
+ *
+ * @param name Name or description of the gift.
+ * @param gender Gender classification of the gift (M, F, U, or any).
+ * @param completionStatus Completion status of the gift.
+ */
+ public Gift(String name, String gender, String completionStatus) {
+ super(name);
+ this.gender = gender;
+ this.completionStatus = completionStatus;
+ }
+
+ /**
+ * Returns the description of the gift.
+ * @return String description of the gift.
+ */
+ @Override
+ public String toString() {
+ return (description);
+ }
+
+ /**
+ * Updates the completion status of the gift, marking it as having been completed.
+ */
+ public void markComplete() {
+ this.completionStatus = "C";
+ }
+
+ /**
+ * Returns the completion status of the gift.
+ * @return completionStatus Completion status of the gift.
+ */
+ public String getCompletionStatus() {
+ return completionStatus;
+ }
+
+ /**
+ * Returns the gender classification of the gift.
+ * @return gender gender classification of the gift.
+ */
+ public String getGender() {
+ return gender;
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/GiftList.java b/src/main/java/seedu/flirtfork/GiftList.java
new file mode 100644
index 0000000000..5e13a0b56c
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/GiftList.java
@@ -0,0 +1,58 @@
+package seedu.flirtfork;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+/**
+ * Manages a collection of gifts, including operations to get a random gift
+ * and track which gifts have been suggested using a hashset.
+ */
+public class GiftList {
+ private ArrayList gifts;
+ private HashSet suggestedGifts = new HashSet<>();
+
+ /**
+ * Constructs a GiftList with an initial list of gifts.
+ *
+ * @param gifts The initial list of gifts.
+ */
+ public GiftList(ArrayList gifts) {
+ this.gifts = gifts;
+ }
+
+ public int size() {
+ return gifts.size();
+ }
+
+ public Gift get(int i) {
+ return gifts.get(i);
+ }
+
+ /**
+ * Retrieves a random gift from the list that matches the specified gender
+ * and has not been suggested or marked as complete.
+ * If all gifts have been suggested, it clears the record of suggested gifts and returns null.
+ *
+ * @param gender The gender filter for the gift suggestions (M, F, U, or any).
+ * @return A random, unsuggested gift that matches the gender filter, or null if all have been suggested.
+ */
+ public Gift getRandomGift(String gender) {
+ List filteredGifts = gifts.stream()
+ .filter(g -> (gender.equals("any") || g.getGender().equalsIgnoreCase(gender))
+ && !g.getCompletionStatus().equals("C") && !suggestedGifts.contains(g))
+ .collect(Collectors.toList());
+
+ if (filteredGifts.isEmpty()) {
+ suggestedGifts.clear();
+ return null;
+ }
+
+ Random random = new Random();
+ Gift randomGift = filteredGifts.get(random.nextInt(filteredGifts.size()));
+ suggestedGifts.add(randomGift);
+ return randomGift;
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/Idea.java b/src/main/java/seedu/flirtfork/Idea.java
new file mode 100644
index 0000000000..263002f2de
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/Idea.java
@@ -0,0 +1,50 @@
+package seedu.flirtfork;
+
+/**
+ * Represents a potential date idea to be adopted by the user.
+ * Each date idea consist of 1 food option, and 1 activity option.
+ */
+public class Idea {
+ private Food food;
+ private Activity activity;
+
+ /**
+ * Constructs a date idea with a specific food option and activity option.
+ *
+ * @param food The selected food option retrieved.
+ * @param activity The selected activity option retrieved.
+ */
+ public Idea(Food food, Activity activity) {
+ this.food = food;
+ this.activity = activity;
+ }
+
+ /**
+ * Updates the date idea with a specific food option.
+ * Overrides and replaces the existing food option.
+ *
+ * @param food The selected food option chosen.
+ */
+ public void setFood(Food food) {
+ this.food = food;
+ }
+
+ /**
+ * Updates the date idea with a specific activity option.
+ * Overrides and replaces the existing activity option.
+ *
+ * @param activity The selected activity option chosen.
+ */
+ public void setActivity(Activity activity) {
+ this.activity = activity;
+ }
+
+ /**
+ * Returns the description of the date idea.
+ * @return String description of the activity and food.
+ */
+ @Override
+ public String toString() {
+ return "You can do " + activity + " and have a nice meal at " + food;
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/Itinerary.java b/src/main/java/seedu/flirtfork/Itinerary.java
new file mode 100644
index 0000000000..f108523f82
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/Itinerary.java
@@ -0,0 +1,39 @@
+package seedu.flirtfork;
+
+/**
+ * Represents a potential date itinerary to be adopted by the user.
+ * Each date idea consist of 2 food options, and 2 activity options.
+ * The food and activity categories will be based on user 'price' and 'location' inputs.
+ */
+public class Itinerary {
+ private Food food1;
+ private Food food2;
+ private Activity activity1;
+ private Activity activity2;
+
+ /**
+ * Constructs a date itinerary with 2 specific food options and 2 activity options.
+ *
+ * @param food1 The first selected food option retrieved.
+ * @param food2 The second selected food option retrieved.
+ * @param activity1 The first selected activity option retrieved.
+ * @param activity2 The second selected activity option retrieved.
+ */
+ public Itinerary(Food food1, Food food2, Activity activity1, Activity activity2) {
+ this.food1 = food1;
+ this.food2 = food2;
+ this.activity1 = activity1;
+ this.activity2 = activity2;
+ }
+
+ /**
+ * Returns the description of the date itinerary.
+ * @return String description of the first activity and food, followed by the second activity and food.
+ */
+ @Override
+ public String toString() {
+ return "Here is a rough itinerary for your date: \n" +
+ "We begin with lunch at " + food1 + ", followed by some fun at " + activity1 + ".\n" +
+ "We proceed to have dinner at " + food2 + ", and finish the night at " + activity2 + ".\n";
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/Parser.java b/src/main/java/seedu/flirtfork/Parser.java
new file mode 100644
index 0000000000..6f75ec4fdf
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/Parser.java
@@ -0,0 +1,243 @@
+package seedu.flirtfork;
+
+import seedu.flirtfork.commands.AddActivityCommand;
+import seedu.flirtfork.commands.AddFoodCommand;
+import seedu.flirtfork.commands.DeleteFavouritesCommand;
+import seedu.flirtfork.commands.ListFavouritesCommand;
+import seedu.flirtfork.commands.ExitCommand;
+import seedu.flirtfork.commands.FindOptionsCommand;
+import seedu.flirtfork.commands.GenerateGiftCommand;
+import seedu.flirtfork.commands.GenerateIdeaCommand;
+import seedu.flirtfork.commands.GenerateItineraryCommand;
+import seedu.flirtfork.commands.GenerateSmartItineraryCommand;
+import seedu.flirtfork.commands.HelpCommand;
+import seedu.flirtfork.commands.ListOptionsCommand;
+import seedu.flirtfork.commands.UserDetailsCommand;
+import seedu.flirtfork.commands.ViewHistoryCommand;
+
+import seedu.flirtfork.exceptions.FlirtForkEmptyException;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * The Parser class is responsible for interpreting user input and converting it into a Command
+ * that the application can execute.
+ * It checks the command type, validates and processes any arguments,
+ * and returns the appropriate Command object to perform the requested action.
+ */
+public class Parser {
+ private static final String HORIZONTAL = "____________________________________________________________";
+
+ /**
+ * Parses the user input into a command based on the first word of the input and any subsequent arguments.
+ *
+ * @param userInput The raw input string from the user.
+ * @param userDetails The UserDetails object containing specific data about the user.
+ * @return Command The command to be executed.
+ * @throws FlirtForkException if there is an error parsing the input or if the input is invalid.
+ */
+ public static Command parseCommand(String userInput, UserDetails userDetails) throws FlirtForkException {
+ if (userInput.trim().isEmpty()) {
+ throw new FlirtForkException("OOPS! Input cannot be empty! \n" + HORIZONTAL);
+ }
+
+ assert userDetails != null : "Input should not be empty";
+
+ String commandType = userInput.split(" ")[0];
+ String arguments = userInput.contains(" ") ? userInput.substring(userInput.indexOf(" ") + 1) : "";
+
+ switch (commandType) {
+ case "food":
+ if (arguments.trim().isEmpty()) {
+ throw new FlirtForkException("OOPS! Food description cannot be empty! \n" + HORIZONTAL);
+ }
+ return new AddFoodCommand(arguments);
+ case "activity":
+ if (arguments.trim().isEmpty()) {
+ throw new FlirtForkEmptyException();
+ }
+ return new AddActivityCommand(arguments);
+ case "favourites":
+ if (!arguments.trim().isEmpty()) {
+ throw new FlirtForkException("OOPS! The 'favourites' command does not require " +
+ "any additional arguments. \n" + HORIZONTAL);
+ }
+ return new ListFavouritesCommand();
+ case "delete":
+ try {
+ int index = Integer.parseInt(arguments) - 1;
+ if (index < 0) {
+ throw new FlirtForkException("OOPS! Index must be greater than 0! \n" +
+ HORIZONTAL);
+ }
+ return new DeleteFavouritesCommand(index);
+ } catch (NumberFormatException e) {
+ throw new FlirtForkException("OOPS! Invalid format, " +
+ "please specify the index of the entry correctly! \n" + HORIZONTAL);
+ }
+ case "find":
+ if (!arguments.trim().isEmpty()) {
+ throw new FlirtForkException("OOPS! Don't need kanchiong! Just type 'find' first! \n" + HORIZONTAL);
+ }
+ return new FindOptionsCommand();
+ case "itinerary":
+ if (arguments.trim().isEmpty()) {
+ throw new FlirtForkEmptyException();
+ } else if (arguments.split(" ").length!=2) {
+ throw new FlirtForkException("Please follow the format: 'itinerary LOCATION, PRICE'");
+ } else {
+ return new GenerateItineraryCommand(arguments);
+ }
+ case "smart":
+ if (!arguments.trim().isEmpty()) {
+ throw new FlirtForkException("To generate smart itinerary, please only send 'smart'.");
+ }
+ return new GenerateSmartItineraryCommand(userDetails);
+ case "idea":
+ if (!arguments.trim().isEmpty()) {
+ throw new FlirtForkException("To generate date ideas, please only send 'idea'.");
+ }
+ return new GenerateIdeaCommand();
+ case "gift":
+ return parseGiftCommand(arguments);
+ case "exit":
+ if (!arguments.trim().isEmpty()) {
+ throw new FlirtForkException("If you wish to exit, please only send 'exit'. \n" + HORIZONTAL);
+ }
+ return new ExitCommand();
+ case "help":
+ if (!arguments.trim().isEmpty()) {
+ throw new FlirtForkException("Just 'help' is enough :) \n" + HORIZONTAL);
+ }
+ return new HelpCommand();
+ case "me":
+ if (!arguments.trim().isEmpty()) {
+ throw new FlirtForkException("Don't be ambitious! Just 'me' is enough! \n" + HORIZONTAL);
+ }
+ return new UserDetailsCommand();
+ case "history":
+ if (!arguments.trim().isEmpty()) {
+ throw new FlirtForkException("Okay, just type 'history' as it is to view your past. \n" + HORIZONTAL);
+ }
+ return new ViewHistoryCommand();
+ case "list":
+ if (!arguments.trim().isEmpty()) {
+ throw new FlirtForkException("Only 'list' is required to view the list! \n" + HORIZONTAL);
+ }
+ return new ListOptionsCommand();
+ default:
+ throw new FlirtForkException("Love is a language we all speak, but we didn't quite catch that. \n" +
+ "Beware of spelling and unnecessary capitalization! Try again? \n" + HORIZONTAL);
+ }
+ }
+
+ /**
+ * Parses the command arguments to determine the gender-specific gift command to be generated.
+ *
+ * @param arguments The arguments part of the user input for gift-related commands.
+ * @return Command A new GenerateGiftCommand with the specified gender criteria.
+ * @throws FlirtForkException if the arguments are invalid or improperly formatted.
+ */
+ private static Command parseGiftCommand(String arguments) throws FlirtForkException {
+ String giftGender = "any"; // Default to any gender
+
+ if (!arguments.trim().isEmpty()) {
+ String[] argsSplit = arguments.trim().split("\\s+");
+
+ if (argsSplit.length == 1) {
+ String genderArg = argsSplit[0].toLowerCase();
+ switch (genderArg) {
+ case "male":
+ giftGender = "M";
+ break;
+ case "female":
+ giftGender = "F";
+ break;
+ case "unisex":
+ giftGender = "U";
+ break;
+ default:
+ throw new FlirtForkException("Invalid argument for gift command! \n" +
+ "For a random gift, type 'gift'. \n" +
+ "For a more gender-specific randomisation, type only one of 'gift male', " +
+ "'gift female', or 'gift unisex'.");
+ }
+ } else {
+ throw new FlirtForkException("Too many arguments for gift command! \n" +
+ "For a random gift, type 'gift'. \n" +
+ "For a more gender-specific randomisation, type only one of 'gift male', " +
+ "'gift female', or 'gift unisex'.");
+ }
+ }
+
+ // giftGender remains "any"
+ return new GenerateGiftCommand(giftGender);
+ }
+
+ /**
+ * Parses a string representation of favourites into a Favourites object.
+ *
+ * @param line The string representation of favourites.
+ * @return The corresponding Favourites object.
+ */
+ public static Favourites parseFavourites(String line) {
+ String[] parts = line.split(" \\| ");
+ Favourites favourite = null;
+
+ if ("F".equals(parts[0]) && parts.length >= 2) {
+ favourite = new Food(parts[0], parts[1], parts[2], parts[3], parts[4]);
+ } else if ("A".equals(parts[0]) && parts.length >= 2) {
+ favourite = new Activity(parts[0], parts[1], parts[2], parts[3]);
+ } else {
+ assert false; // Throws AssertionError if favourite not created yet
+ }
+
+ return favourite;
+ }
+
+ /**
+ * Parses a line from the FoodList.txt file and constructs a Food object from it.
+ *
+ * @param line The line from the food file.
+ * @return Food A new Food object based on the parsed line.
+ */
+ public static Food parseFood(String line) {
+ String[] parts = line.split(" \\| ");
+ Food food;
+ food = new Food(parts[0], parts[1], parts[2], parts[3], parts[4]);
+ return food;
+ }
+
+ /**
+ * Parses a line from the ActivityList.txt file and constructs an Activity object from it.
+ *
+ * @param line The line from the activities file.
+ * @return Activity A new Activity object based on the parsed line.
+ */
+ public static Activity parseActivity(String line) {
+ String[] parts = line.split(" \\| ");
+ Activity activity;
+ activity = new Activity(parts[0], parts[1], parts[2], parts[3]);
+ return activity;
+ }
+
+ /**
+ * Parses a line from the GiftList.txt file and constructs a Gift object from it.
+ * Ensures that each line includes a description, completion status, and gender marker.
+ *
+ * @param line The line from the gifts file.
+ * @return Gift A new Gift object based on the parsed line.
+ * @throws FlirtForkException if the gift data is incomplete or improperly formatted.
+ */
+ public static Gift parseGift(String line) throws FlirtForkException {
+ String[] parts = line.split(" \\| ");
+ if (parts.length < 3) {
+ throw new FlirtForkException("Gift data is incomplete. " +
+ "Each line must include a description, completion status, and gender marker.");
+ }
+ Gift gift = new Gift(parts[0], parts[1], parts[2]);
+ return gift;
+ }
+
+}
+
+
diff --git a/src/main/java/seedu/flirtfork/SmartItinerary.java b/src/main/java/seedu/flirtfork/SmartItinerary.java
new file mode 100644
index 0000000000..d9b9918d62
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/SmartItinerary.java
@@ -0,0 +1,40 @@
+package seedu.flirtfork;
+
+/**
+ * Represents a potential date itinerary to be adopted by the user.
+ * Each date idea consist of 2 food options, and 2 activity options.
+ * The food and activity categories will be based on the initial user information entered by user during setup.
+ * Utilises the 'price' and 'location' inputs.
+ */
+public class SmartItinerary {
+ private Food food1;
+ private Food food2;
+ private Activity activity1;
+ private Activity activity2;
+
+ /**
+ * Constructs a date itinerary with 2 specific food options and 2 activity options.
+ *
+ * @param food1 The first selected food option retrieved.
+ * @param food2 The second selected food option retrieved.
+ * @param activity1 The first selected activity option retrieved.
+ * @param activity2 The second selected activity option retrieved.
+ */
+ public SmartItinerary(Food food1, Food food2, Activity activity1, Activity activity2) {
+ this.food1 = food1;
+ this.food2 = food2;
+ this.activity1 = activity1;
+ this.activity2 = activity2;
+ }
+
+ /**
+ * Returns the description of the date itinerary.
+ * @return String description of the first activity and food, followed by the second activity and food.
+ */
+ @Override
+ public String toString() {
+ return "Here is a smart itinerary for your date (the afternoon is planned around your base preferences!): \n" +
+ "We begin with lunch at " + food1 + ", followed by some fun at " + activity1 + ".\n" +
+ "We proceed to have dinner at " + food2 + ", and finish the night at " + activity2 + ".\n";
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/Storage.java b/src/main/java/seedu/flirtfork/Storage.java
new file mode 100644
index 0000000000..0c0721e80e
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/Storage.java
@@ -0,0 +1,335 @@
+package seedu.flirtfork;
+
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.io.InputStream;
+
+/**
+ * Manages the storage of user details and application data such as favourites, food items, activities,
+ * and gift suggestions. This class provides functionality to load data from and save data to external files.
+ */
+public class Storage {
+ private static final String USER_DETAILS_FILE = "./data/UserDetails.txt";
+ private static final String FAVOURITES_DETAILS_FILE = "./data/Favourites.txt";
+ private static final String FOOD_DETAILS_FILE = "./data/FoodList.txt";
+ private static final String ACTIVITIES_DETAILS_FILE = "./data/ActivityList.txt";
+ private static final String GIFTS_DETAILS_FILE = "./data/GiftList.txt";
+ private String filePath;
+
+ /**
+ * Constructs a new Storage object to handle file operations for the specified file path.
+ *
+ * @param filePath The file path where data will be loaded from and saved to.
+ */
+ public Storage(String filePath) {
+ this.filePath = filePath;
+ }
+
+ /**
+ * Loads the list of favourite items from the file.
+ *
+ * @return The list of favourite items.
+ * @throws FileNotFoundException If the file is not found.
+ */
+ public FavouritesList loadFavourites() throws FileNotFoundException {
+ try {
+ ArrayList loadedFavourites = new ArrayList<>();
+ File file = new File(FAVOURITES_DETAILS_FILE);
+ if (file.exists()) {
+ Scanner scanner = new Scanner(file);
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ Favourites favourites = new Favourites(line);
+ loadedFavourites.add(favourites);
+ }
+ scanner.close();
+ return new FavouritesList(loadedFavourites);
+ }
+ } catch (FileNotFoundException e) {
+ System.out.println("OOPS! No saved tasks found, starting with an empty list ~");
+ }
+ return new FavouritesList();
+ }
+
+ /**
+ * Saves the list of favourite items to the file.
+ *
+ * @param favourites The list of favourite items to save.
+ * @throws IOException If an I/O error occurs.
+ */
+ public void saveFavourites(ArrayList favourites) throws IOException {
+ try {
+ File file = new File(FAVOURITES_DETAILS_FILE);
+ File parentDir = file.getParentFile();
+
+ if (!parentDir.exists()) {
+ parentDir.mkdirs();
+ }
+
+ FileWriter writer = new FileWriter(file);
+ for (Favourites favourite : favourites) {
+ writer.write(favourite.toFileFormat() + "\n");
+ }
+ writer.close();
+ } catch (IOException e) {
+ System.out.println("OOPS! An error occurred while saving tasks.");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Saves the user details to a file.
+ *
+ * @param userDetails The user details to save.
+ */
+ public void saveUserDetails(UserDetails userDetails) {
+ try {
+ File file = new File(USER_DETAILS_FILE);
+ File parentDir = file.getParentFile();
+ if (!parentDir.exists()) {
+ parentDir.mkdirs();
+ }
+ FileWriter writer = new FileWriter(file);
+ writer.write(userDetails.toFileFormat());
+ writer.close();
+ } catch (IOException e) {
+ System.out.println("OOPS! An error occurred while saving user details.");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Loads the user details from a file.
+ *
+ * @return The loaded user details.
+ */
+ public UserDetails loadUserDetails() {
+ File file = new File(USER_DETAILS_FILE);
+ if (!file.exists()) {
+ System.out.println("OOPS! No saved user details found.");
+ return new UserDetails();
+ }
+
+ try (Scanner scanner = new Scanner(file)) {
+ if (!scanner.hasNextLine()) {
+ return new UserDetails();
+ }
+ String line = scanner.nextLine();
+ String[] details = line.split(" \\| ");
+ return new UserDetails(details[0], details[1], details[2], details[3], details[4], details[5], details[6]);
+ } catch (FileNotFoundException e) {
+ System.out.println("OOPS! An error occurred while loading user details.");
+ return new UserDetails();
+ }
+ }
+
+ /**
+ * Loads the list of food items from the file.
+ *
+ * @return The list of food items.
+ * @throws FileNotFoundException If the file is not found.
+ */
+ public ArrayList loadFood() throws FileNotFoundException {
+ ArrayList loadedFoods = new ArrayList<>();
+ try {
+ File file = new File(FOOD_DETAILS_FILE);
+ if (file.exists()) {
+ Scanner scanner = new Scanner(file);
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ loadedFoods.add(Parser.parseFood(line));
+ }
+ scanner.close();
+ }
+ } catch (FileNotFoundException e) {
+ System.out.println("OOPS! No saved tasks found, starting with an empty list ~");
+ }
+ return loadedFoods;
+ }
+
+ /**
+ * Loads the initial food list from a file packaged with the application.
+ *
+ * @return The list of food items loaded from the file.
+ * @throws FileNotFoundException If the food list file is not found.
+ */
+ public ArrayList loadFoodFirstTime() throws FileNotFoundException {
+ ArrayList loadedFoods = new ArrayList<>();
+ InputStream is = getClass().getClassLoader().getResourceAsStream("FoodList.txt");
+ if (is == null) {
+ throw new FileNotFoundException("Food list file not found");
+ }
+ try (Scanner scanner = new Scanner(is)) {
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ // Parse the line into a Food object and add it to the list
+ loadedFoods.add(Parser.parseFood(line));
+ }
+ }
+ return loadedFoods;
+ }
+
+ /**
+ * Saves the list of food items to the file.
+ *
+ * @param foods The list of food items to save.
+ */
+ public void saveFood(FoodList foods) {
+ try (FileWriter writer = new FileWriter(FOOD_DETAILS_FILE)) {
+ for (int i = 0; i < foods.size(); i++) {
+ Food oneFood = foods.get(i);
+ writer.write(oneFood.description + " | " + oneFood.location + " | " +
+ oneFood.price + " | " + oneFood.cuisine + " | " + oneFood.completionStatus + "\n");
+ }
+ writer.close();
+ } catch (IOException e) {
+ System.out.println("OOPS! An error occurred while saving tasks.");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Loads the initial activity list from a file packaged with the application.
+ *
+ * @return The list of activities loaded from the file.
+ * @throws FileNotFoundException If the activity list file is not found.
+ */
+ public ArrayList loadActivityFirstTime() throws FileNotFoundException {
+ ArrayList loadedActivities = new ArrayList<>();
+ InputStream is = getClass().getClassLoader().getResourceAsStream("ActivityList.txt");
+ if (is == null) {
+ throw new FileNotFoundException("Activity list file not found");
+ }
+ try (Scanner scanner = new Scanner(is)) {
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ // Parse the line into an Activity object and add it to the list
+ loadedActivities.add(Parser.parseActivity(line));
+ }
+ }
+ return loadedActivities;
+ }
+
+ /**
+ * Loads the activity list from a file.
+ *
+ * @return The list of activities loaded from the file.
+ * @throws FileNotFoundException If the activity list file is not found.
+ */
+ public ArrayList loadActivity() throws FileNotFoundException {
+ ArrayList loadedActivities = new ArrayList<>();
+ try {
+ File file = new File(ACTIVITIES_DETAILS_FILE);
+ if (file.exists()) {
+ Scanner scanner = new Scanner(file);
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ loadedActivities.add(Parser.parseActivity(line));
+ }
+ scanner.close();
+ }
+ } catch (FileNotFoundException e) {
+ System.out.println("OOPS! No saved tasks found, starting with an empty list ~");
+ }
+ return loadedActivities;
+ }
+
+ /**
+ * Saves the activity list to a file.
+ *
+ * @param activities The activity list to save.
+ */
+ public void saveActivity(ActivityList activities) {
+ try (FileWriter writer = new FileWriter(ACTIVITIES_DETAILS_FILE)) {
+ for (int i = 0; i < activities.size(); i++) {
+ Activity oneActivity = activities.get(i);
+ writer.write(oneActivity.description + " | " + oneActivity.location + " | " +
+ oneActivity.price + " | " + oneActivity.completionStatus + "\n");
+ }
+ writer.close();
+ } catch (IOException e) {
+ System.out.println("OOPS! An error occurred while saving tasks.");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Loads the gift list from a resource file during the initial setup.
+ * This method is typically called when the application is run for the first time.
+ *
+ * @return An ArrayList of Gift objects loaded from the resource file.
+ * @throws FileNotFoundException If the resource file cannot be found.
+ */
+ public ArrayList loadGiftFirstTime() throws FileNotFoundException {
+ ArrayList loadedGift = new ArrayList<>();
+ InputStream is = getClass().getClassLoader().getResourceAsStream("GiftList.txt");
+ if (is == null) {
+ throw new FileNotFoundException("Gift list file not found");
+ }
+ try (Scanner scanner = new Scanner(is)) {
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ try {
+ loadedGift.add(Parser.parseGift(line));
+ } catch (FlirtForkException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return loadedGift;
+ }
+
+ /**
+ * Loads the gift list from an external file, typically called when the application is started.
+ *
+ * @return An ArrayList of Gift objects loaded from the file.
+ * @throws FileNotFoundException If the file cannot be found and thus cannot be loaded.
+ */
+ public ArrayList loadGift() throws FileNotFoundException {
+ ArrayList loadGifts = new ArrayList<>();
+ try {
+ File file = new File(GIFTS_DETAILS_FILE);
+ if (file.exists()) {
+ Scanner scanner = new Scanner(file);
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ try {
+ loadGifts.add(Parser.parseGift(line));
+ } catch (FlirtForkException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ scanner.close();
+ }
+ } catch (FileNotFoundException e) {
+ System.out.println("OOPS! No saved tasks found, starting with an empty list ~");
+ }
+ return loadGifts;
+ }
+
+ /**
+ * Saves the current list of gifts to an external file. This method is called when the application
+ * is closing or when the user chooses to save their changes.
+ *
+ * @param gifts The GiftList containing all gift objects to be saved.
+ */
+ public void saveGift(GiftList gifts) {
+ try (FileWriter writer = new FileWriter(GIFTS_DETAILS_FILE)) {
+ for (int i = 0; i < gifts.size(); i++) {
+ Gift oneGift = gifts.get(i);
+ writer.write(oneGift.description + " | " + oneGift.getGender() +
+ " | " + oneGift.completionStatus + "\n");
+ }
+ writer.close();
+ } catch (IOException e) {
+ System.out.println("OOPS! An error occurred while saving tasks.");
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/Ui.java b/src/main/java/seedu/flirtfork/Ui.java
new file mode 100644
index 0000000000..072a33a4ff
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/Ui.java
@@ -0,0 +1,457 @@
+package seedu.flirtfork;
+
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Handles all user interfaces for FlirtFork, providing methods to display messages,
+ * read user commands, and show lists of options like food, activities, and gifts.
+ */
+public class Ui {
+ private static final String BOT_NAME = "Flirt and Fork";
+ private static final String HORIZONTAL = "____________________________________________________________";
+ private Scanner scanner;
+
+ /**
+ * Constructs a Ui object with a new Scanner for reading user input from the console.
+ */
+ public Ui() {
+ this.scanner = new Scanner(System.in);
+ }
+
+ /**
+ * Displays a greeting message to the user, optionally including the anniversary date if available.
+ *
+ * @param anniversary The user's anniversary date or "N.A" if not provided.
+ */
+ public void greetingMessage(String anniversary) {
+ System.out.println(HORIZONTAL);
+ System.out.println("Hungry for love? You've come to the right place.\n" +
+ "Welcome to " + BOT_NAME + " - where Cupid meets the chef!\n\n" +
+ "If you ever feel lost, just type 'help' for a guiding star!");
+ if (!anniversary.equals("N.A")) {
+ System.out.println("\nRemember, your anniversary is on " + anniversary + " :)");
+ }
+ System.out.println(HORIZONTAL);
+ }
+
+ /**
+ * Displays a first setup message to the user when starting the application for the first time.
+ */
+ public void firstSetUpMessage() {
+ System.out.println(HORIZONTAL);
+ System.out.println("Hungry for love? You've come to the right place.\n" +
+ "Welcome to " + BOT_NAME + " - where Cupid meets the chef!");
+ System.out.println("Before we start, I would like to know you better!");
+ System.out.println(HORIZONTAL);
+ }
+
+ /**
+ * Displays an exit message to the user when they choose to terminate the application.
+ */
+ public void exitMessage() {
+ System.out.println("Love, like a good meal, is all about timing.\n" +
+ "Keep your love simmering and your fork ready, " +
+ "see you at the next course!");
+ }
+
+ /**
+ * Displays the help message with a list of available commands and their functions.
+ */
+ public void helpMessage() {
+ System.out.println(
+ "----------------------------------------- \n" +
+ "| Command to type | Function of feature | \n" +
+ "----------------------------------------- \n" +
+ "1. list: Take a look at potential restaurants, activities, or gifts \n\n" +
+ "2. me: Let me know more about yourself \n\n" +
+ "3. idea: Generate a randomised date idea \n\n" +
+ "4. itinerary LOCATION, PRICE: Generate a suitable date itinerary based on your preferences \n\n" +
+ "5. smart: Generate a smart itinerary, based on your user profile \n\n" +
+ "6. gift: Generate a randomised gift idea \n\n" +
+ "7. food NAME_OF_EATERY, LOCATION, PRICE, CUISINE: Add a restaurant to your favourites \n\n" +
+ "8. activity NAME_OF_ACTIVITY, LOCATION, PRICE: Add an activity to your favourites \n\n" +
+ "9. favourites: List out all your favourites \n\n" +
+ "10. delete INDEX_OF_ENTRY: Delete an entry from your favourites \n\n" +
+ "11. find: enter menu to find an entry, based off keyword \n\n" +
+ "12. history: List out all your past date locations and restaurants \n\n" +
+ "13. exit: Exit the program \n\n" +
+
+ "LEGEND (prices):\n" +
+ "C: Cheap\n" +
+ "B: Budget\n" +
+ "A: Affordable\n" +
+ "P: Pricey\n" +
+ "S: Special Occasions Only\n\n" +
+
+ "LEGEND (cuisines):\n" +
+ "W: Western\n" +
+ "F: Fusion\n" +
+ "J: Japanese\n" +
+ "C: Chinese\n" +
+ "T: Thai\n" +
+ "K: Korean\n" +
+ "I: Italian\n" +
+ "S: Spanish\n\n" +
+
+ "LEGEND (locations):\n" +
+ "E: East\n" +
+ "W: West\n" +
+ "C: Central\n" +
+ "S: South\n" +
+ "NE: NorthEast\n" +
+ "ACC: Accessible (found in multiple places around SG)"
+ );
+ }
+
+ /**
+ * Displays an error message to the user.
+ *
+ * @param message The error message to be displayed.
+ */
+ public void errorMessage(String message) {
+ System.out.println(message);
+ }
+
+ /**
+ * Reads the user's command input.
+ *
+ * @return The user's command input as a String.
+ */
+ public String readCommand() {
+ return scanner.nextLine();
+ }
+
+ /**
+ * Displays a single favourite item.
+ *
+ * @param favourite The favourite item to be displayed.
+ */
+ public void showFavourite(String favourite) {
+ System.out.println(favourite);
+ }
+
+ /**
+ * Lists all favourites in a formatted manner.
+ *
+ * @param favouritesList The list of favourites to be displayed.
+ */
+ public void listFavourites(FavouritesList favouritesList) {
+ System.out.println("Drumroll, please! Presenting the stars of your romantic sky: ");
+ System.out.println(favouritesList.getFormattedFavourites());
+ }
+
+ /**
+ * Displays the search results for user's favourite items based on the given keyword.
+ * If matches are found, it lists all the matching favourites.
+ * Otherwise, it prompts the user to try another search term.
+ *
+ * @param matchingFavourites A list of Favourites entries that match the search keyword.
+ */
+ public void showMatchingFavourites(ArrayList matchingFavourites) {
+ if (matchingFavourites.isEmpty()) {
+ System.out.println("No treasures found this time. Let's try another clue?");
+ } else {
+ System.out.println("Eureka! Your cupid's arrow hit the target! We found these matches for you:");
+ for (int i = 0; i < matchingFavourites.size(); i++) {
+ System.out.println((i + 1) + ". " + matchingFavourites.get(i));
+ }
+ }
+ }
+
+ /**
+ * Displays the search results for food items based on the given keyword.
+ * If matches are found, it lists all the matching foods.
+ * Otherwise, it prompts the user to try another search term.
+ *
+ * @param matchingFoods A list of Food entries that match the search keyword.
+ */
+ public void showMatchingFoods(ArrayList matchingFoods) {
+ if (matchingFoods.isEmpty()) {
+ System.out.println("No food found this time:( Let's try another clue?");
+ } else {
+ System.out.println("Eureka! Your cupid's arrow hit the target! We found these matches for you:");
+ for (int i = 0; i < matchingFoods.size(); i++) {
+ System.out.println((i + 1) + ". " + matchingFoods.get(i));
+ }
+ }
+ }
+
+ /**
+ * Displays the search results for activities items based on the given keyword.
+ * If matches are found, it lists all the matching activities.
+ * Otherwise, it prompts the user to try another search term.
+ *
+ * @param matchingActivities A list of Activity entries that match the search keyword.
+ */
+ public void showMatchingActivities(ArrayList matchingActivities) {
+ if (matchingActivities.isEmpty()) {
+ System.out.println("No activity found this time:( Let's try another clue?");
+ } else {
+ System.out.println("Eureka! Your cupid's arrow hit the target! We found these matches for you:");
+ for (int i = 0; i < matchingActivities.size(); i++) {
+ System.out.println((i + 1) + ". " + matchingActivities.get(i));
+ }
+ }
+ }
+
+ /**
+ * Displays the search results for gift items based on the given keyword.
+ * If matches are found, it lists all the matching gifts.
+ * Otherwise, it prompts the user to try another search term.
+ *
+ * @param matchingGifts A list of Gift entries that match the search keyword.
+ */
+ public void showMatchingGifts(ArrayList matchingGifts) {
+ if (matchingGifts.isEmpty()) {
+ System.out.println("No gifts found this time:( Let's try another clue?");
+ } else {
+ System.out.println("Eureka! Your cupid's arrow hit the target! We found these matches for you:");
+ for (int i = 0; i < matchingGifts.size(); i++) {
+ System.out.println((i + 1) + ". " + matchingGifts.get(i));
+ }
+ }
+ }
+
+ public static void listFood() {
+ System.out.println("HMMMM Let's see what food is there: ");
+ }
+
+ public static void listActivities() {
+ System.out.println("What are some activities you can do as a couple? Let's see.." );
+ }
+
+ public static void listGifts() {
+ System.out.println("Peek into Cupid's own gift collection!" );
+ }
+
+ /**
+ * Displays the list command menu to the user, providing options for listing foods,
+ * activities, and gifts, or cancelling the command.
+ */
+ public static void listCommand() {
+ System.out.println("Looking for ideas to spice up your date night?");
+ System.out.println("Choose from the following options:");
+ System.out.println("1. List out delicious dining options (type 'food')");
+ System.out.println("2. Discover exciting activities to do together (type 'activities')");
+ System.out.println("3. Unwrap joy with our curated list of gifts that'll make hearts flutter! (type 'gifts')");
+ System.out.println("4. Changed your mind? Feel free to cancel this command! (type 'cancel')");
+ System.out.println("What's your pleasure?");
+ System.out.println(HORIZONTAL);
+ }
+
+ public static void findCommand(){
+ System.out.println("What are you searching for in this enchanting realm?");
+ System.out.println("Choose from the following options:");
+ System.out.println("1. What delicious food are you craving for today? (type 'food')");
+ System.out.println("2. What exciting activity are you interested in? (type 'activities')");
+ System.out.println("3. On the hunt for the perfect gift? What are you looking for? (type 'gifts')");
+ System.out.println("4. Finding within your own treasures? (type 'favourites')");
+ System.out.println("5. Changed your mind? Feel free to cancel this command! (type 'cancel')");
+ System.out.println("What's on your mind?");
+ System.out.println(HORIZONTAL);
+ }
+
+ public void ideaSatisfiedErrorMessage() {
+ System.out.println("Sorry, I didn't quite understand that :(");
+ System.out.println("Please enter either yes or no or cancel the generation process");
+ System.out.println(HORIZONTAL);
+ }
+
+ /**
+ * Displays a general message to the user.
+ *
+ * @param message The message to be displayed.
+ */
+ public void showMessage(String message) {
+ System.out.println(message);
+ }
+
+ /**
+ * Reads and validates the user's location input.
+ *
+ * @return The user's validated location input (E, W, C, S, or NE).
+ */
+ public String readUserLocation() {
+ String input = readCommand();
+ boolean isValid;
+
+ isValid = input.equals("E") || input.equals("W") || input.equals("C")
+ || input.equals("S") || input.equals("NE");
+
+ while (!isValid) {
+ showMessage("Invalid location, please enter E, W, C, S or NE.");
+ input = readCommand();
+ isValid = input.equals("E") || input.equals("W") || input.equals("C")
+ || input.equals("S") || input.equals("NE");
+ }
+ return input;
+ }
+
+ /**
+ * Reads and validates the user's age input.
+ *
+ * @return The user's validated age input as a String.
+ */
+ public String readAge() {
+ int age;
+ while (true) {
+ try {
+ String input = readCommand();
+ age = Integer.parseInt(input);
+ if ((age == 0 && input.length() == 1) || (age < 0)) {
+ showMessage("Age cannot be negative or zero. Please enter a valid age.");
+ } else if (input.startsWith("0")) {
+ showMessage("Age cannot start with '0'. Please enter a valid age.");
+ } else if (age >= 120) {
+ showMessage("I don't think you're older than the oldest person alive...re-enter your age!");
+ } else {
+ return input; // Valid age
+ }
+ } catch (NumberFormatException e) {
+ showMessage("Invalid input. Please enter a valid integer age.");
+ }
+ }
+ }
+
+ /**
+ * Reads and validates the user's name input, ensuring it's not empty and doesn't contain numbers.
+ *
+ * @return The user's validated name input as a String.
+ */
+ public String readName() {
+ while (true) {
+ String name = readCommand();
+ if (name.trim().isEmpty()) {
+ showMessage("Name cannot be empty. Please enter a valid name.");
+ } else if (name.matches(".*\\d+.*")) {
+ showMessage("Name cannot contain numbers. Please re-enter without numbers.");
+ } else {
+ return name;
+ }
+ }
+ }
+
+ /**
+ * Reads and validates the user's cuisine preference input.
+ *
+ * @return The user's validated cuisine preference input (W, F, J, C, T, K, I, or S).
+ */
+ public String readUserCuisine() {
+ String input = readCommand();
+ boolean isValid;
+
+ isValid = input.equals("W") || input.equals("F") || input.equals("J") || input.equals("C")
+ || input.equals("T") ||input.equals("K") || input.equals("I") || input.equals("S");
+
+ while (!isValid) {
+ showMessage("Please enter your preferences in this format.\n");
+ showMessage("W: Western");
+ showMessage("F: Fusion");
+ showMessage("J: Japanese");
+ showMessage("C: Chinese");
+ showMessage("T: Thai");
+ showMessage("K: Korean");
+ showMessage("I: Italian");
+ showMessage("S: Spanish");
+ input = readCommand();
+ isValid = input.equals("W") || input.equals("F") || input.equals("J") || input.equals("C")
+ || input.equals("T") ||input.equals("K") || input.equals("I") || input.equals("S");
+ }
+ return input;
+ }
+
+ /**
+ * Reads and validates the user's relationship status input.
+ *
+ * @return The user's validated relationship status input (M, R, F, D, S, or X).
+ */
+ public String readUserStatus() {
+ String input = readCommand();
+ boolean isValid;
+
+ isValid = input.equals("M") || input.equals("R") || input.equals("F")
+ || input.equals("D") || input.equals("S") ||input.equals("X");
+
+ while(!isValid) {
+ showMessage("Please enter your status in this format.\n");
+ showMessage("Enter 'M' if you are Married");
+ showMessage("Enter 'R' if you are in a serious relationship");
+ showMessage("Enter 'F' if you are having a fling");
+ showMessage("Enter 'D' if you are dating/testing the waters");
+ showMessage("Enter 'S' if you are single and ready to mingle");
+ showMessage("Enter 'X' if you are single and only looking to hangout with friends");
+ input = readCommand();
+ isValid = input.equals("M") || input.equals("R") || input.equals("F")
+ || input.equals("D") || input.equals("S") ||input.equals("X");
+ }
+ return input;
+ }
+
+ /**
+ * Reads and validates the user's gender input.
+ *
+ * @return The user's validated gender input (Male, Female, or Other).
+ */
+ public String readUserGender() {
+ String input = readCommand();
+ boolean isValid;
+
+ isValid = input.equals("Male") || input.equals("Female") || input.equals("Other");
+
+ while(!isValid) {
+ showMessage("Please enter your gender in this format strictly. Please adhere to the capitalisation\n");
+ showMessage("'Male' if you are a male");
+ showMessage("'Female' if you are a female");
+ showMessage("'Other' if its none of the above");
+
+ input = readCommand();
+ isValid = input.equals("Male") || input.equals("Female") || input.equals("Other");
+ }
+ return input;
+ }
+
+ /**
+ * Reads and validates the user's anniversary date input.
+ * The date must be in this format: dd/mm/yyyy
+ *
+ * @return The user's validated anniversary date input (String).
+ */
+ public String readAnniversaryDate() {
+ String input = readCommand();
+ while (!isValidDate(input)) {
+ input = readCommand();
+ }
+ return input;
+ }
+
+ /**
+ * Checks if the given input string represents a valid date in the format 'dd/mm/yyyy'.
+ *
+ * @param input The input string to be validated as a date.
+ * @return true if the input string represents a valid date, false otherwise.
+ */
+ public boolean isValidDate(String input) {
+ if (!input.matches("\\d{2}/\\d{2}/\\d{4}")) {
+ showMessage("Please enter a valid date in dd/mm/yyyy format.");
+ return false;
+ }
+ SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
+ dateFormat.setLenient(false);
+ try {
+ Date inputDate = dateFormat.parse(input);
+ Date currentDate = new Date();
+ if (inputDate.after(currentDate)) {
+ showMessage("Do note that the date cannot be later than the current date!");
+ return false;
+ }
+ return true;
+ } catch (ParseException e) {
+ showMessage("Please enter a valid date in dd/mm/yyyy format.");
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/UserDetails.java b/src/main/java/seedu/flirtfork/UserDetails.java
new file mode 100644
index 0000000000..b176000a82
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/UserDetails.java
@@ -0,0 +1,208 @@
+package seedu.flirtfork;
+
+/**
+ * Represents the user's personal details within the application.
+ * This class stores information such as the user's name, age, gender,
+ * relationship status,
+ * location, favorite cuisine, and anniversary date if applicable. It provides
+ * getters and
+ * setters for each field, allowing for encapsulated access and modification of
+ * these details.
+ */
+public class UserDetails {
+ private String name;
+ private String age;
+ private String gender;
+ private String status;
+ private String location;
+ private String cuisine;
+ private String anniversary;
+
+ /**
+ * Constructs a new UserDetails instance with default values for all fields.
+ * Default values are placeholder strings indicating that the details have not
+ * been set.
+ */
+ public UserDetails() {
+ this.name = "NOT SET";
+ this.age = "N.A";
+ this.gender = "N.A";
+ this.status = "N.A";
+ this.location = "N.A";
+ this.cuisine = "N.A";
+ this.anniversary = "N.A";
+ }
+
+ /**
+ * Constructs a new UserDetails instance with specified values for all fields.
+ *
+ * @param name The user's name.
+ * @param age The user's age.
+ * @param gender The user's gender.
+ * @param status The user's relationship status.
+ * @param location The user's preferred location.
+ * @param cuisine The user's favorite cuisine.
+ * @param anniversary The user's anniversary date, if applicable.
+ */
+ public UserDetails(String name, String age, String gender, String status, String location,
+ String cuisine, String anniversary) {
+ this.name = name;
+ this.age = age;
+ this.gender = gender;
+ this.status = status;
+ this.location = location;
+ this.cuisine = cuisine;
+ this.anniversary = anniversary;
+ }
+
+ /**
+ * Returns the user's name.
+ *
+ * @return The name of the user.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the user's name.
+ *
+ * @param name The new name of the user.
+ */
+ public void setName(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Name cannot be null.");
+ }
+ ;
+ this.name = name;
+ }
+
+ /**
+ * Returns the user's relationship status.
+ *
+ * @return The relationship status of the user.
+ */
+ public String getStatus() {
+ return status;
+ }
+
+ /**
+ * Sets the user's relationship status.
+ *
+ * @param status The relationship status of the user.
+ */
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ /**
+ * Returns the user's age.
+ *
+ * @return The age of the user.
+ */
+ public String getAge() {
+ return age;
+ }
+
+ /**
+ * Sets the user's age.
+ *
+ * @param age The age of the user.
+ */
+ public void setAge(String age) {
+ try {
+ if (Integer.parseInt(age) < 0) {
+ throw new IllegalArgumentException("Age cannot be negative.");
+ }
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Age must be a valid integer.");
+ }
+ this.age = age;
+ }
+
+ /**
+ * Returns the user's gender.
+ *
+ * @return The gender of the user.
+ */
+ public String getGender() {
+ return gender;
+ }
+
+ /**
+ * Sets the user's gender.
+ *
+ * @param gender The gender of the user.
+ */
+ public void setGender(String gender) {
+ if (!(gender.equals("Male") || gender.equals("Female") || gender.equals("Other"))) {
+ throw new IllegalArgumentException("Gender must be 'Male', 'Female', or 'Other'.");
+ }
+ this.gender = gender;
+ }
+
+ /**
+ * Returns the user's favourite cuisine.
+ *
+ * @return The favourite cuisine of the user.
+ */
+ public String getCuisine() {
+ return cuisine;
+ }
+
+ /**
+ * Sets the user's favourite cuisine.
+ *
+ * @param cuisine The favourite cuisine of the user.
+ */
+ public void setCuisine(String cuisine) {
+ this.cuisine = cuisine;
+ }
+
+ /**
+ * Returns the user's address location.
+ *
+ * @return The address location of the user.
+ */
+ public String getLocation() {
+ return location;
+ }
+
+ /**
+ * Sets the user's address location.
+ *
+ * @param location The address location of the user.
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Returns the user's anniversary date.
+ *
+ * @return The anniversary date of the user.
+ */
+ public String getAnniversary() {
+ return anniversary;
+ }
+
+ /**
+ * Sets the user's anniversary date.
+ *
+ * @param anniversary The anniversary date of the user.
+ */
+ public void setAnniversary(String anniversary) {
+ this.anniversary = anniversary;
+ }
+
+ /**
+ * Converts the user's details into a string format suitable for file storage.
+ * The details are concatenated with " | " as a delimiter.
+ *
+ * @return A string representation of the user's details in file storage format.
+ */
+ public String toFileFormat() {
+ return name + " | " + age + " | " + gender + " | " + status + " | " + location + " | " +
+ cuisine + " | " + anniversary;
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/commands/AddActivityCommand.java b/src/main/java/seedu/flirtfork/commands/AddActivityCommand.java
new file mode 100644
index 0000000000..fab3b2a69d
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/AddActivityCommand.java
@@ -0,0 +1,83 @@
+package seedu.flirtfork.commands;
+
+import java.util.Arrays;
+
+import seedu.flirtfork.Activity;
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * Command to add an activity to the activity list and favourites.
+ */
+public class AddActivityCommand extends Command {
+ private String description;
+
+ /**
+ * Constructs an AddActivityCommand with the specified description.
+ *
+ * @param description The description of the activity to add.
+ */
+ public AddActivityCommand(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Executes the list favourites command.
+ *
+ * @param favourites The list of favourites.
+ * @param foods The list of food options.
+ * @param activities The list of activity options.
+ * @param ui The user interface.
+ * @param storage The storage component.
+ * @param userDetails The user details.
+ * @param gifts The list of gift options.
+ * @throws FlirtForkException If an error occurs during execution.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui,
+ Storage storage, UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+
+ String[] prices = {"C", "B", "A", "P", "S"};
+ String[] locations = {"E", "W", "C", "S", "NE", "ACC"};
+ String[] inputCommands = description.split(", ");
+ try {
+ String inputtedDescription = inputCommands[0];
+ String inputtedLocation = inputCommands[1];
+ String inputtedPrice = inputCommands[2];
+
+ // Used to verify correct inputs
+ if (!Arrays.asList(prices).contains(inputtedPrice)){
+ System.out.println("Invalid price/format given! Perhaps you could try again?");
+ System.out.println("Please type in the command 'help' to view our Legend for reference");
+ } else if (!Arrays.asList(locations).contains(inputtedLocation)) {
+ System.out.println("Invalid location/format given! Perhaps you could try again?");
+ System.out.println("Please type in the command 'help' to view our Legend for reference");
+ } else {
+ Activity newActivity = new Activity(inputtedDescription, inputtedLocation, inputtedPrice, "U");
+
+ // Add to favourites database
+ favourites.addFavourite(newActivity);
+ System.out.println("Cupid's arrow strikes! This is now in your favourites. \n" + newActivity);
+ ui.showFavourite("You've collected " + favourites.getFavourites().size() + " romantic treasures!");
+
+ // Add to activity database
+ activities.add(newActivity);
+ storage.saveActivity(activities);
+ }
+ } catch (IndexOutOfBoundsException e) {
+ System.out.println("Hmm... you may have entered the wrong number of fields. How about you try again?");
+ System.out.println("Follow this format: 'activity NAME_OF_ACTIVITY, LOCATION, PRICE'");
+ }
+ }
+}
+
+
+
+
diff --git a/src/main/java/seedu/flirtfork/commands/AddFoodCommand.java b/src/main/java/seedu/flirtfork/commands/AddFoodCommand.java
new file mode 100644
index 0000000000..3a8054025d
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/AddFoodCommand.java
@@ -0,0 +1,86 @@
+package seedu.flirtfork.commands;
+
+import java.util.Arrays;
+
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.Food;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * Command to add a food option to favourites.
+ */
+public class AddFoodCommand extends Command {
+ private String description;
+
+ /**
+ * Constructs an AddFoodCommand with the specified description.
+ *
+ * @param description The description of the food option to add.
+ */
+ public AddFoodCommand(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Executes the list favourites command.
+ *
+ * @param favourites The list of favourites.
+ * @param foods The list of food options.
+ * @param activities The list of activity options.
+ * @param ui The user interface.
+ * @param storage The storage component.
+ * @param userDetails The user details.
+ * @param gifts The list of gift options.
+ * @throws FlirtForkException If an error occurs during execution.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui,
+ Storage storage, UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+
+
+ String[] prices = {"C", "B", "A", "P", "S"};
+ String[] locations = {"E", "W", "C", "S", "NE", "ACC"};
+ String[] cuisines = {"W", "F", "J", "C", "T", "K", "I", "S"};
+ String[] inputCommands = description.split(", ");
+ // To deal with descriptions with multiple spaces: "Bistro C C W" versus "Bistro Bar C C W"
+ try {
+ String inputtedDescription = inputCommands[0];
+ String inputtedLocation = inputCommands[1];
+ String inputtedPrice = inputCommands[2];
+ String inputtedCuisine = inputCommands[3];
+
+ // Used to verify correct inputs
+ if (!Arrays.asList(prices).contains(inputtedPrice)){
+ System.out.println("Invalid price/format given! Perhaps you could try again?");
+ System.out.println("Please type in the command 'help' to view our Legend for reference");
+ } else if (!Arrays.asList(locations).contains(inputtedLocation)) {
+ System.out.println("Invalid location/format given! Perhaps you could try again?");
+ System.out.println("Please type in the command 'help' to view our Legend for reference");
+ } else if (!Arrays.asList(cuisines).contains(inputtedCuisine)) {
+ System.out.println("Invalid cuisine/format given! Perhaps you could try again?");
+ System.out.println("Please type in the command 'help' to view our Legend for reference");
+ } else {
+ Food newFood = new Food(inputtedDescription, inputtedLocation, inputtedPrice, inputtedCuisine, "U");
+
+ // Add to favourites database
+ favourites.addFavourite(newFood);
+ System.out.println("Cupid's arrow strikes! This is now in your favourites. \n" + newFood);
+ ui.showFavourite("You've collected " + favourites.getFavourites().size() + " romantic treasures!");
+
+ // Add to food database
+ foods.add(newFood);
+ storage.saveFood(foods);
+ }
+ } catch (IndexOutOfBoundsException e) {
+ System.out.println("Hmm... you may have entered the wrong number of fields. How about you try again?");
+ System.out.println("Follow this format: 'food NAME_OF_EATERY, LOCATION, PRICE, CUISINE'");
+ }
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/commands/DeleteFavouritesCommand.java b/src/main/java/seedu/flirtfork/commands/DeleteFavouritesCommand.java
new file mode 100644
index 0000000000..63f842e33a
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/DeleteFavouritesCommand.java
@@ -0,0 +1,59 @@
+package seedu.flirtfork.commands;
+
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.Favourites;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * Command to delete a favourite item from the favourites list.
+ */
+public class DeleteFavouritesCommand extends Command {
+ private final int index;
+
+ /**
+ * Constructs a DeleteFavouritesCommand with the specified index.
+ *
+ * @param index The index of the favourite item to delete.
+ */
+ public DeleteFavouritesCommand(int index) {
+ this.index = index;
+ }
+
+ /**
+ * Executes the list favourites command.
+ *
+ * @param favourites The list of favourites.
+ * @param foods The list of food options.
+ * @param activities The list of activity options.
+ * @param ui The user interface.
+ * @param storage The storage component.
+ * @param userDetails The user details.
+ * @param gifts The list of gift options.
+ * @throws FlirtForkException If an error occurs during execution.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui,
+ Storage storage, UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+ try {
+ if (index >= 0 && index < favourites.getFavourites().size()) {
+ Favourites removedFavourite = favourites.getFavourites().get(index);
+ favourites.deleteFavourite(index);
+ ui.showFavourite("Poof! Sayonara~ This favourite has been removed: \n" + removedFavourite);
+ ui.showFavourite("Now you have " + favourites.getFavourites().size() + " romantic treasures!\n" +
+ "Your journey of love and taste continues~");
+ } else {
+ throw new FlirtForkException("Index out of bounds: " + index);
+ }
+ } catch (FlirtForkException e) {
+ ui.errorMessage("This number's playing hard to get. How about trying one that is in range?");
+ }
+ }
+}
+
diff --git a/src/main/java/seedu/flirtfork/commands/ExitCommand.java b/src/main/java/seedu/flirtfork/commands/ExitCommand.java
new file mode 100644
index 0000000000..738fdb252f
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/ExitCommand.java
@@ -0,0 +1,49 @@
+package seedu.flirtfork.commands;
+
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+import java.io.IOException;
+
+/**
+ * A command that is executed to safely exit the application.
+ * This command handles the saving of all current application data,
+ * including favourites, foods, activities, and gifts to ensure no data loss occurs upon exit.
+ */
+public class ExitCommand extends Command {
+ /**
+ * Executes the command to exit the application.
+ * It first saves all relevant data to their respective files and then displays an exit message.
+ * If any errors occur during the save operation, an error message is displayed.
+ *
+ * @param favourites The list of favourite items to save.
+ * @param foods The list of food items to save.
+ * @param activities The list of activities to save.
+ * @param ui The user interface to handle interactions.
+ * @param storage The storage component used for data persistence.
+ * @param userDetails The user details.
+ * @param gifts The list of gifts to save.
+ * @throws FlirtForkException If an error occurs that affects the execution flow.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui,
+ Storage storage, UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+
+ try {
+ storage.saveFavourites(favourites.getFavourites());
+ storage.saveActivity(activities);
+ storage.saveFood(foods);
+ storage.saveGift(gifts);
+ ui.exitMessage();
+ } catch (IOException e) {
+ ui.errorMessage("Yikes! Our love potion spilled and couldn't save your data! " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/commands/FindOptionsCommand.java b/src/main/java/seedu/flirtfork/commands/FindOptionsCommand.java
new file mode 100644
index 0000000000..8b2abeb0e6
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/FindOptionsCommand.java
@@ -0,0 +1,161 @@
+package seedu.flirtfork.commands;
+
+import seedu.flirtfork.Activity;
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.Favourites;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.Food;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.Gift;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+import java.util.ArrayList;
+import java.util.Scanner;
+
+/**
+ * Command to find options based on user input.
+ */
+public class FindOptionsCommand extends Command {
+
+ private static final String HORIZONTAL = "____________________________________________________________";
+ private String optionType;
+
+ public FindOptionsCommand() {
+ }
+
+ /**
+ * Executes the command to find options based on user input.
+ *
+ * @param favourites The list of favourites.
+ * @param foods The list of food options.
+ * @param activities The list of activity options.
+ * @param ui The user interface.
+ * @param storage The storage component.
+ * @param userDetails The user details.
+ * @param gifts The list of gift options.
+ * @throws FlirtForkException If an error occurs during execution.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities,
+ Ui ui, Storage storage, UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+
+ Scanner scanner = new Scanner(System.in);
+ String optionType;
+ Ui.findCommand();
+
+ while (true) {
+ optionType = scanner.nextLine().toLowerCase();
+
+ switch (optionType) {
+ case "food":
+ System.out.println("Mmmm food yes. What restaurants would you like to search for?");
+ String foodKeyword = scanner.nextLine().toLowerCase();
+ ArrayList matchingFood = findFood(foodKeyword, foods);
+ ui.showMatchingFoods(matchingFood);
+ System.out.println(HORIZONTAL);
+ System.out.println("To find exciting activities, type 'activities'");
+ System.out.println("To hunt for gifts, type 'gifts'");
+ System.out.println("To search for your treasures, type 'favourites'");
+ System.out.println("To cancel this command, type 'cancel'");
+ break;
+ case "activities":
+ System.out.println("Mmmm activities! What kind of activities would you like to search for?");
+ String activityKeyword = scanner.nextLine().toLowerCase();
+ ArrayList matchingActivities = findActivities(activityKeyword, activities);
+ ui.showMatchingActivities(matchingActivities);
+ System.out.println(HORIZONTAL);
+ System.out.println("To find delicious food, type 'food'");
+ System.out.println("To hunt for gifts, type 'gifts'");
+ System.out.println("To search for your treasures, type 'favourites'");
+ System.out.println("To cancel this command, type 'cancel'");
+ break;
+ case "gifts":
+ System.out.println("Mmmm gifts! What kind of gifts would you like to search for?");
+ String giftKeyword = scanner.nextLine().toLowerCase();
+ ArrayList matchingGifts = findGifts(giftKeyword, gifts);
+ ui.showMatchingGifts(matchingGifts);
+ System.out.println(HORIZONTAL);
+ System.out.println("To find delicious food, type 'food'");
+ System.out.println("To find exciting activities, type 'activities'");
+ System.out.println("To search for your treasures, type 'favourites'");
+ System.out.println("To cancel this command, type 'cancel'");
+ break;
+ case "favourites":
+ System.out.println("Mmmm finding your own treasures i see. What would you like to search for?");
+ String favouritesKeyword = scanner.nextLine().toLowerCase();
+ ArrayList matchingFavourites = findFavourites(favouritesKeyword, favourites);
+ ui.showMatchingFavourites(matchingFavourites);
+ System.out.println(HORIZONTAL);
+ System.out.println("To find delicious food, type 'food'");
+ System.out.println("To find exciting activities, type 'activities'");
+ System.out.println("To hunt for gifts, type 'gifts'");
+ System.out.println("To cancel this command, type 'cancel'");
+ break;
+ case "cancel":
+ System.out.println("Cancelling findings...\n" +
+ "Cancel success!");
+ return;
+ default:
+ System.out.println("Invalid option! "
+ + "Please choose 'food', 'activities', 'gifts', 'favourites' or 'cancel'.");
+ }
+ }
+ }
+
+ private ArrayList findFood(String keyword, FoodList foods) {
+ ArrayList matchingSaves = new ArrayList<>();
+
+ for(int i = 0; i < foods.size(); i++) {
+ Food food = foods.get(i);
+ String description = food.getDescription().toLowerCase();
+ if (description.contains(keyword.toLowerCase())) {
+ matchingSaves.add(food);
+ }
+ }
+ return matchingSaves;
+ }
+
+ private ArrayList findActivities(String keyword, ActivityList activities) {
+ ArrayList matchingSaves = new ArrayList<>();
+
+ for(int i = 0; i < activities.size(); i++) {
+ Activity activity = activities.get(i);
+ String description = activity.getDescription().toLowerCase();
+ if (description.contains(keyword.toLowerCase())) {
+ matchingSaves.add(activity);
+ }
+ }
+ return matchingSaves;
+ }
+
+ private ArrayList findGifts(String keyword, GiftList gifts) {
+ ArrayList matchingSaves = new ArrayList<>();
+
+ for(int i = 0; i < gifts.size(); i++) {
+ Gift gift = gifts.get(i);
+ String description = gift.getDescription().toLowerCase();
+ int categoryIndex = description.indexOf("]") + 1;
+ if (description.substring(categoryIndex).contains(keyword.toLowerCase())) {
+ matchingSaves.add(gift);
+ }
+ }
+ return matchingSaves;
+ }
+
+ private ArrayList findFavourites(String keyword, FavouritesList favourites) {
+ ArrayList matchingSaves = new ArrayList<>();
+
+ for(Favourites favourite: favourites.getFavourites()) {
+ String description = favourite.getDescription().toLowerCase();
+ if (description.contains(keyword.toLowerCase())) {
+ matchingSaves.add(favourite);
+ }
+ }
+ return matchingSaves;
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/commands/GenerateGiftCommand.java b/src/main/java/seedu/flirtfork/commands/GenerateGiftCommand.java
new file mode 100644
index 0000000000..04b4c79307
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/GenerateGiftCommand.java
@@ -0,0 +1,90 @@
+package seedu.flirtfork.commands;
+
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.Gift;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * This command generates a random gift suggestion based on the gender specified.
+ * It allows users to continually receive gift suggestions until they are satisfied,
+ * or choose to cancel the process.
+ */
+public class GenerateGiftCommand extends Command {
+ private static final String HORIZONTAL = "____________________________________________________________";
+ private final String gender;
+
+ /**
+ * Constructs a new GenerateGiftCommand with the specified gender.
+ *
+ * @param gender The gender filter for the gift suggestions (male, female, unisex, or any).
+ */
+ public GenerateGiftCommand(String gender) {
+ this.gender = gender;
+ }
+
+ /**
+ * Executes the gift suggestion process. Prompts the user with randomly generated
+ * gift suggestions based on the specified gender filter until they are satisfied
+ * or choose to cancel the process.
+ *
+ * @param favourites List of user's favourite items.
+ * @param foods List of food items available.
+ * @param activities List of activities available.
+ * @param ui UI interface to interact with the user.
+ * @param storage The storage handler for saving and retrieving data.
+ * @param userDetails The user details for personalized suggestions.
+ * @param gifts The list of gifts available.
+ * @throws FlirtForkException If an error occurs during the execution of the command.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui,
+ Storage storage, UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+ String userSatisfied;
+
+ Gift gift = gifts.getRandomGift(this.gender);
+ System.out.println(gift);
+ System.out.println("-> Satisfied with the gift suggestion? [Yes/No]");
+ System.out.println("-> Changed you mind? type 'cancel' to exit this process!");
+ System.out.println(HORIZONTAL);
+
+ do {
+ userSatisfied = ui.readCommand().toLowerCase();
+ if (userSatisfied.equals("yes")) {
+ System.out.println("This gift is about to make a love story even sweeter.");
+ gift.markComplete();
+ break;
+ } else if (userSatisfied.equals("no")) {
+ gift = gifts.getRandomGift(this.gender);
+ if (gift == null) {
+ System.out.println("Wow! You've seen all our ideas! \n" +
+ "Sometimes, the best gifts are the ones you come up with yourself.\n" +
+ "We believe in you!");
+ System.out.println("");
+ System.out.println("Exiting gift generation process...");
+ System.out.println("Exit success!");
+ break;
+ }
+ System.out.println("Regenerating a new gift idea...");
+ System.out.println(gift);
+ System.out.println("-> Satisfied with the gift suggestion? [Yes/No]");
+ System.out.println("-> Changed you mind? type 'cancel' to exit this process!");
+ System.out.println(HORIZONTAL);
+ } else if (userSatisfied.equals("cancel")) {
+ System.out.println("Taking a break? That's okay! \n" +
+ "Remember, great ideas need their own time to unwrap.");
+ break;
+ } else {
+ ui.ideaSatisfiedErrorMessage();
+ }
+ } while (true);
+
+ storage.saveGift(gifts);
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/commands/GenerateIdeaCommand.java b/src/main/java/seedu/flirtfork/commands/GenerateIdeaCommand.java
new file mode 100644
index 0000000000..a6f9ee58df
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/GenerateIdeaCommand.java
@@ -0,0 +1,81 @@
+package seedu.flirtfork.commands;
+
+import seedu.flirtfork.Activity;
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.Food;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Idea;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+
+/**
+ * Represents a command to generate a date idea for the user based on a random combination of food and activity.
+ * The user can choose whether they are satisfied with the idea or not.
+ * If the user is satisfied, the chosen food and activity items are marked as completed.
+ * If the user is not satisfied, the command runs again and another idea is regenerated.
+ * The user is allowed to cancel the command if idea generation is not satisfactory
+ */
+public class GenerateIdeaCommand extends Command {
+ private static final String HORIZONTAL = "____________________________________________________________";
+
+ /**
+ * Generates a date idea consisting of a random food item and a random activity.
+ * Asks the user if they are satisfied with the idea. If satisfied, marks the chosen food
+ * and activity as complete and saves the changes to storage. If not satisfied, prompts
+ * the user to try again.
+ *
+ * @param favourites The user's list of favourite items (not directly used in this method).
+ * @param foods The list of food items.
+ * @param activities The list of activities.
+ * @param ui The user interface to interact with the user for input and output.
+ * @param storage The storage handler to save changes to food and activity lists.
+ * @param userDetails The UserDetails object containing user details (not directly used in this method).
+ * @param gifts The list of gifts (not directly used in this method).
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui,
+ Storage storage, UserDetails userDetails, GiftList gifts) {
+ String userSatisfied;
+
+
+ Food food = foods.getRandomFood();
+ Activity activity = activities.getRandomActivity();
+ Idea idea = new Idea(food, activity);
+ System.out.println(idea);
+ System.out.println("-> Are you satisfied with the date idea? [Yes/No]");
+ System.out.println("-> Else, feel free to stop idea generation using the command 'cancel'");
+ System.out.println(HORIZONTAL);
+
+ while (true) {
+ userSatisfied = ui.readCommand().toLowerCase();
+ if (userSatisfied.equalsIgnoreCase("yes")) {
+ System.out.println("That's great! Enjoy your date!");
+ food.markComplete();
+ activity.markComplete();
+ storage.saveFood(foods);
+ storage.saveActivity(activities);
+ return;
+ } else if (userSatisfied.equalsIgnoreCase("no")) {
+ System.out.println("Regenerating a new date idea...");
+ food = foods.getRandomFood();
+ activity = activities.getRandomActivity();
+ idea.setFood(food);
+ idea.setActivity(activity);
+ System.out.println(idea);
+ System.out.println("-> Are you satisfied with the date idea? [Yes/No]");
+ System.out.println("-> Else, feel free to stop idea generation using the command 'cancel'");
+ System.out.println(HORIZONTAL);
+ } else if (userSatisfied.equalsIgnoreCase("cancel")) {
+ System.out.println("Taking a break? That's okay! \n" +
+ "Remember, great ideas need their own time to unwrap.");
+ break;
+ } else {
+ ui.ideaSatisfiedErrorMessage();
+ }
+ }
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/commands/GenerateItineraryCommand.java b/src/main/java/seedu/flirtfork/commands/GenerateItineraryCommand.java
new file mode 100644
index 0000000000..c5200ebbab
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/GenerateItineraryCommand.java
@@ -0,0 +1,115 @@
+package seedu.flirtfork.commands;
+
+import java.util.Arrays;
+import seedu.flirtfork.Activity;
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.Food;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Itinerary;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * Represents a command to generate a date itinerary for the user based on a specified category of food and activity.
+ * The user can choose whether they are satisfied with the itinerary or not.
+ * If the user is satisfied, the chosen food and activity items are marked as completed
+ * If the user is not satisfied, the command stops and the user is prompted to try again
+ */
+public class GenerateItineraryCommand extends Command {
+ private String description;
+
+ public GenerateItineraryCommand(String description){
+ this.description = description;
+ }
+
+ /**
+ * Generates a date itinerary consisting of 2 food items and 2 activities.
+ * Food and activites generated are based on the chosen location and price category specified by the user
+ * Asks the user if they are satisfied with the idea. If satisfied, marks the chosen food
+ * and activity as complete and saves the changes to storage. If not satisfied, prompts
+ * the user to try again.
+ *
+ * @param favourites The user's list of favourite items (not directly used in this method).
+ * @param foods The list of food items.
+ * @param activities The list of activities.
+ * @param ui The user interface to interact with the user for input and output.
+ * @param storage The storage handler to save changes to food and activity lists.
+ * @param userDetails The UserDetails object containing user details (not directly used in this method).
+ * @param gifts The list of gifts (not directly used in this method).
+ * @throws FlirtForkException If an error occurs during the execution of the command.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui,
+ Storage storage, UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+ String userSatisfied;
+ Food food1;
+ Food food2;
+ Activity activity1;
+ Activity activity2;
+
+ String[] prices = {"C", "B", "A", "P", "S"};
+ String[] locations = {"E", "W", "C", "S", "NE", "ACC"};
+
+ try {
+ String[] splitDescription = description.split(", ");
+ String preferredLocation = splitDescription[0];
+ String preferredPrice = splitDescription[1];
+
+ // Verify whether price/location entered correctly
+ if (!Arrays.asList(prices).contains(preferredPrice)){
+ System.out.println("Invalid price/format given! Perhaps you could try again?");
+ System.out.println("Please type in the command 'help' to view our Legend for reference");
+ return;
+ } else if (!Arrays.asList(locations).contains(preferredLocation)) {
+ System.out.println("Invalid location/format given! Perhaps you could try again?");
+ System.out.println("Please type in the command 'help' to view our Legend for reference");
+ return;
+ }
+
+ food1 = foods.getFilteredFood(preferredLocation, preferredPrice);
+ activity1 = activities.getFilteredActivity(preferredLocation, preferredPrice);
+ // Ensure activity1 and activity2 are different
+ do {
+ food2 = foods.getFilteredFood(preferredLocation, preferredPrice);
+ } while (food2.getDescription().equals(food1.getDescription()));
+ // Ensure food1 and food2 are different
+ do {
+ activity2 = activities.getFilteredActivity(preferredLocation, preferredPrice);
+ } while (activity2.getDescription().equals(activity1.getDescription()));
+ Itinerary itinerary = new Itinerary(food1, food2, activity1, activity2);
+ System.out.println(itinerary);
+
+ System.out.println("Are you satisfied with the itinerary? [Yes/No]");
+ userSatisfied = ui.readCommand().toLowerCase();
+ if (userSatisfied.equals("yes")) {
+ System.out.println("That's great! Enjoy your date!");
+ food1.markComplete();
+ food2.markComplete();
+ activity1.markComplete();
+ activity2.markComplete();
+ storage.saveFood(foods);
+ storage.saveActivity(activities);
+ return;
+ } else if (userSatisfied.equals("no")) {
+ System.out.println("We apologise! Perhaps you could try again?");
+ } else {
+ System.out.println("Sorry, I did not understand that command! Stopping itinerary generation...");
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Hmm... you may have entered the wrong number of fields. How about you try again?");
+ System.out.println("Follow this format: 'itinerary LOCATION, PRICE'");
+ } catch (IllegalArgumentException e) {
+ System.out.println("We could not generate a suitable itinerary based on your inputs! Sorry!!");
+ } catch (FlirtForkException e) {
+ System.out.println("Hmm.. we ran out of suitable ideas based on your current budget/location!");
+ System.out.println("Perhaps you could try a different location or budget?");
+ System.out.println("Feel free to add more to our database using the 'food'/'activity' command!");
+ }
+ }
+}
+
diff --git a/src/main/java/seedu/flirtfork/commands/GenerateSmartItineraryCommand.java b/src/main/java/seedu/flirtfork/commands/GenerateSmartItineraryCommand.java
new file mode 100644
index 0000000000..bfb227786b
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/GenerateSmartItineraryCommand.java
@@ -0,0 +1,111 @@
+package seedu.flirtfork.commands;
+
+import seedu.flirtfork.Activity;
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.Food;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.SmartItinerary;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * Represents a command to generate a smart itinerary based on user preferences.
+ * This command considers the user's preferred location and cuisine to create
+ * a personalized itinerary that includes food and activities.
+ */
+public class GenerateSmartItineraryCommand extends Command {
+ private String preferredLocation;
+ private String preferredCuisine;
+
+ /**
+ * Creates a GenerateSmartItineraryCommand with specified user details.
+ *
+ * @param userDetails The details of the user including preferred location and
+ * cuisine.
+ */
+ public GenerateSmartItineraryCommand(UserDetails userDetails) {
+ if (userDetails == null) {
+ throw new IllegalArgumentException("User details are required at command creation.");
+ }
+ this.preferredLocation = userDetails.getLocation();
+ this.preferredCuisine = userDetails.getCuisine();
+ }
+
+ /**
+ * Executes the smart itinerary generation process. It selects food and
+ * activities
+ * based on the user's preferences and creates an itinerary. If the preferences
+ * are too unique or if any error occurs, it logs a severe error and informs the
+ * user.
+ *
+ * @param favourites The list of favourite items of the user.
+ * @param foods The list of available foods.
+ * @param activities The list of available activities.
+ * @param ui The user interface to interact with the user.
+ * @param storage The storage handler to save or load data.
+ * @param userDetails The details of the user, including preferences.
+ * @param gifts The list of gifts available for the user.
+ * @throws FlirtForkException If an error occurs during the execution of the command.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui, Storage storage,
+ UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+
+ String userSatisfied;
+ Food food1;
+ Food food2;
+ Activity activity1;
+ Activity activity2;
+
+ try {
+ food1 = foods.getCustomisedFood(preferredLocation, preferredCuisine);
+ activity1 = activities.getCustomisedActivity(preferredLocation);
+ // Ensure activity1 and activity2 are different
+ do {
+ food2 = foods.getRandomFood();
+ } while (food2.getDescription().equals(food1.getDescription()));
+ // Ensure food1 and food2 are different
+ do {
+ activity2 = activities.getRandomActivity();
+ } while (activity2.getDescription().equals(activity1.getDescription()));
+ SmartItinerary itinerary = new SmartItinerary(food1, food2, activity1, activity2);
+ System.out.println(itinerary);
+
+ System.out.println("Are you satisfied with the date idea? [Yes/No]");
+
+ while (true) {
+ userSatisfied = ui.readCommand().toLowerCase();
+ if (userSatisfied.equalsIgnoreCase("yes")) {
+ System.out.println("That's great! Enjoy your date!");
+ food1.markComplete();
+ food2.markComplete();
+ activity1.markComplete();
+ activity2.markComplete();
+ storage.saveFood(foods);
+ storage.saveActivity(activities);
+ return;
+ } else if (userSatisfied.equalsIgnoreCase("no")) {
+ System.out.println("I apologise for not being smart enough. Please try again next time!");
+ return;
+ } else {
+ ui.ideaSatisfiedErrorMessage();
+ }
+ }
+
+ } catch (IllegalArgumentException e) {
+ System.out.println(preferredCuisine);
+ System.out.println(preferredLocation);
+ System.out.println("You are too unique of an individual for smart itineraries! Sorry!!");
+ } catch (FlirtForkException e) {
+ System.out.println("I could not generate a suitable itinerary based on your personal details!");
+ System.out.println("I'm not smart enough for your default preferences, try 'itinerary' command instead!");
+ }
+
+ }
+
+}
diff --git a/src/main/java/seedu/flirtfork/commands/HelpCommand.java b/src/main/java/seedu/flirtfork/commands/HelpCommand.java
new file mode 100644
index 0000000000..5840112e6a
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/HelpCommand.java
@@ -0,0 +1,43 @@
+package seedu.flirtfork.commands;
+
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * Represents a command to display help messages to the user. This command is used
+ * to guide users on how to use the application, providing a brief overview of
+ * available commands and functionalities.
+ */
+public class HelpCommand extends Command {
+ /**
+ * Executes the process of displaying help messages to the user. This method calls
+ * upon the UI component to show predefined help messages, offering guidance and
+ * instructions for using the application effectively.
+ *
+ * @param favourites The list of user's favourite items, not used in this command.
+ * @param foods The list of food items available, not used in this command.
+ * @param activities The list of available activities, not used in this command.
+ * @param ui The user interface component responsible for interactions.
+ * @param storage The storage handler for saving or retrieving data, not used in this command.
+ * @param userDetails The details of the user, not used in this command.
+ * @param gifts The list of gifts available, not used in this command.
+ * @throws FlirtForkException If any application-specific error occurs.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui,
+ Storage storage, UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+ if (ui == null) {
+ throw new FlirtForkException("UI component must not be null.");
+ }
+ System.out.println("I know you are excited to Flirt & Fork :) Here's how: \n");
+ ui.helpMessage();
+ ui.showMessage("Send me 'help' if you're new!");
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/commands/ListFavouritesCommand.java b/src/main/java/seedu/flirtfork/commands/ListFavouritesCommand.java
new file mode 100644
index 0000000000..975974e4ce
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/ListFavouritesCommand.java
@@ -0,0 +1,53 @@
+package seedu.flirtfork.commands;
+
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * Command to list the user's favourites.
+ */
+public class ListFavouritesCommand extends Command {
+ /**
+ * Executes the list favourites command.
+ *
+ * @param favourites The list of favourites.
+ * @param foods The list of food options.
+ * @param activities The list of activity options.
+ * @param ui The user interface.
+ * @param storage The storage component.
+ * @param userDetails The user details.
+ * @param gifts The list of gift options.
+ * @throws FlirtForkException If an error occurs during execution.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui,
+ Storage storage, UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+ if (favourites == null) {
+ throw new FlirtForkException("Favourites list is null.");
+ }
+
+ if (ui == null) {
+ throw new FlirtForkException("UI is null.");
+ }
+
+ try {
+ if (favourites.isEmpty()) {
+ ui.showFavourite("No treasures found this time. Let's fill it with some love!");
+ } else {
+ ui.listFavourites(favourites);
+ }
+ } catch (Exception e) {
+ FlirtForkException exception = new FlirtForkException("An error occurred while listing favourites: "
+ + e.getMessage());
+ exception.initCause(e);
+ throw exception;
+ }
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/commands/ListOptionsCommand.java b/src/main/java/seedu/flirtfork/commands/ListOptionsCommand.java
new file mode 100644
index 0000000000..7e57ff5349
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/ListOptionsCommand.java
@@ -0,0 +1,121 @@
+package seedu.flirtfork.commands;
+
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+import java.util.Scanner;
+
+/**
+ * Handles the command that allows users to list various options such as foods, activities, and gifts.
+ * Users can navigate through these options until they decide to cancel.
+ */
+public class ListOptionsCommand extends Command {
+ private static final String HORIZONTAL = "____________________________________________________________";
+ private String optionType;
+
+ public ListOptionsCommand() {
+ }
+
+ /**
+ * Executes the listing of different options like foods, activities, or gifts based on the user's choices.
+ * Continues to prompt the user until the 'cancel' command is issued.
+ *
+ * @param favourites List of user's favourite items, not used in this command.
+ * @param foods List of food items available for listing.
+ * @param activities List of activities available for listing.
+ * @param ui The UI interface to interact with the user.
+ * @param storage The storage handler, not used in this command.
+ * @param userDetails The user details, not used in this command.
+ * @param gifts The list of gifts available for listing.
+ * @throws FlirtForkException If an invalid option type is provided by the user.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities,
+ Ui ui, Storage storage, UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+
+ Scanner scanner = new Scanner(System.in);
+ String optionType;
+ Ui.listCommand();
+
+ while (true) {
+
+ optionType = scanner.nextLine().toLowerCase();
+
+ switch (optionType) {
+ case "food":
+ printFoodList(ui, foods);
+ System.out.println(HORIZONTAL);
+ System.out.println("To discover exciting activities, type 'activities'");
+ System.out.println("To view a curated list of gifts, type 'gifts'");
+ System.out.println("To cancel this command, type 'cancel'");
+ break;
+ case "activities":
+ printActivityList(ui, activities);
+ System.out.println(HORIZONTAL);
+ System.out.println("To list out delicious dining options, type 'food'");
+ System.out.println("To view a curated list of gifts, type 'gifts'");
+ System.out.println("To cancel this command, type 'cancel'");
+ break;
+ case "gifts":
+ printGiftList(ui, gifts);
+ System.out.println(HORIZONTAL);
+ System.out.println("To list out delicious dining options, type 'food'");
+ System.out.println("To discover exciting activities, type 'activities'");
+ System.out.println("To cancel this command, type 'cancel'");
+ break;
+ case "cancel":
+ System.out.println("Cancelling listings... \n" +
+ "Cancel success!");
+ return;
+ default:
+ System.out.println("Invalid option! Please choose 'food', 'activities', 'gifts' or 'cancel'.");
+ }
+ }
+ }
+
+ /**
+ * Prints the list of food items.
+ *
+ * @param ui The user interface.
+ * @param foods The list of foods to print.
+ */
+ private void printFoodList(Ui ui, FoodList foods) {
+ ui.listFood();
+ for (int i = 0; i < foods.size(); i++) {
+ System.out.println(i + 1 + ". " + foods.get(i));
+ }
+ }
+
+ /**
+ * Prints the list of activities.
+ *
+ * @param ui The user interface.
+ * @param activities The list of activities to print.
+ */
+ private void printActivityList(Ui ui, ActivityList activities) {
+ ui.listActivities();
+ for (int i = 0; i < activities.size(); i++) {
+ System.out.println(i + 1 + ". " + activities.get(i));
+ }
+ }
+
+ /**
+ * Prints the list of gifts.
+ *
+ * @param ui The user interface.
+ * @param gifts The list of gifts to print.
+ */
+ private void printGiftList(Ui ui, GiftList gifts) {
+ ui.listGifts();
+ for (int i = 0; i < gifts.size(); i++) {
+ System.out.println(i + 1 + ". " + gifts.get(i));
+ }
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/commands/UserDetailsCommand.java b/src/main/java/seedu/flirtfork/commands/UserDetailsCommand.java
new file mode 100644
index 0000000000..0fc95340d2
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/UserDetailsCommand.java
@@ -0,0 +1,110 @@
+package seedu.flirtfork.commands;
+
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+
+/**
+ * Represents a command to collect and store user details. This command prompts the user
+ * for personal information such as name, age, gender, location, favourite cuisine, and
+ * relationship status. Some details, like an anniversary date, are conditionally requested
+ * based on the user's relationship status. The collected information is used to personalize
+ * the application's services and recommendations.
+ */
+public class UserDetailsCommand extends Command {
+
+ private static final String HORIZONTAL = "____________________________________________________________";
+
+ /**
+ * Executes the user detail collection process. This method interacts with the user
+ * through the provided UI to gather personal information. It then saves these details
+ * to the application's storage. Information collected includes name, age, gender,
+ * location, favourite cuisine, relationship status, and potentially the anniversary date.
+ * This information is essential for customizing the application experience and recommendations.
+ *
+ * @param favouritesList The user's list of favourite items (not directly used in this method).
+ * @param foodList The list of food items (not directly used in this method).
+ * @param activityList The list of activities (not directly used in this method).
+ * @param ui The user interface to interact with the user for input and output.
+ * @param storage The storage handler to save user details.
+ * @param userDetails The UserDetails object to be populated with the user's information.
+ * @param gifts The list of gifts (not directly used in this method).
+ */
+ @Override
+ public void execute(FavouritesList favouritesList, FoodList foodList,
+ ActivityList activityList, Ui ui, Storage storage, UserDetails userDetails, GiftList gifts) {
+ ui.showMessage("Please enter your name:");
+ String name = ui.readName();
+ ui.showMessage("Great! Hello there " + name + ", it's my pleasure to know you!");
+ System.out.println(HORIZONTAL);
+
+ ui.showMessage("Please enter your age:");
+ String age = ui.readAge();
+ ui.showMessage("Wow, you're " + age + " years young! This might be handy information.");
+ System.out.println(HORIZONTAL);
+
+ ui.showMessage("Please enter your gender(Male/Female/Other):");
+ String gender = ui.readUserGender();
+ ui.showMessage("I see you're a " + gender + "!");
+ System.out.println(HORIZONTAL);
+
+ ui.showMessage("Where do you stay?\n");
+ ui.showMessage("E: East");
+ ui.showMessage("W: West");
+ ui.showMessage("C: Central");
+ ui.showMessage("S: South");
+ ui.showMessage("NE: NorthEast");
+ String location = ui.readUserLocation();
+ ui.showMessage("Thanks! Don't worry, I won't let the rest know where you stay ;)!");
+ System.out.println(HORIZONTAL);
+
+ ui.showMessage("What is your favourite cuisine?\n");
+ ui.showMessage("W: Western");
+ ui.showMessage("F: Fusion");
+ ui.showMessage("J: Japanese");
+ ui.showMessage("C: Chinese");
+ ui.showMessage("T: Thai");
+ ui.showMessage("K: Korean");
+ ui.showMessage("I: Italian");
+ ui.showMessage("S: Spanish");
+ String cuisine = ui.readUserCuisine();
+ ui.showMessage("Thanks, this will be useful...");
+ System.out.println(HORIZONTAL);
+
+ ui.showMessage("Please enter your relationship status:\n");
+ ui.showMessage("Enter 'M' if you are Married");
+ ui.showMessage("Enter 'R' if you are in a serious relationship");
+ ui.showMessage("Enter 'F' if you are having a fling");
+ ui.showMessage("Enter 'D' if you are dating/testing the waters");
+ ui.showMessage("Enter 'S' if you are single and ready to mingle");
+ ui.showMessage("Enter 'X' if you are single and only looking to hangout with friends");
+ String status = ui.readUserStatus();
+ ui.showMessage("Thanks for letting me know your relationship status! :)");
+ System.out.println(HORIZONTAL);
+
+ if (status.equals("M") || status.equals("R") || status.equals("D")) {
+ String anniversary;
+ ui.showMessage("Lucky you! Please enter your anniversary in 'dd/mm/yyyy' format:");
+ anniversary = ui.readAnniversaryDate();
+ userDetails.setAnniversary(anniversary);
+ }
+
+ userDetails.setName(name);
+ userDetails.setAge(age);
+ userDetails.setGender(gender);
+ userDetails.setStatus(status);
+ userDetails.setLocation(location);
+ userDetails.setCuisine(cuisine);
+ // Save the user details to a file using the Storage class
+ storage.saveUserDetails(userDetails);
+
+ ui.showMessage("User details saved successfully!");
+ ui.showMessage("Curious about what you can do? Just type 'help' for a sprinkle of tips! ");
+ System.out.println(HORIZONTAL);
+ }
+}
diff --git a/src/main/java/seedu/flirtfork/commands/ViewHistoryCommand.java b/src/main/java/seedu/flirtfork/commands/ViewHistoryCommand.java
new file mode 100644
index 0000000000..65af2552d3
--- /dev/null
+++ b/src/main/java/seedu/flirtfork/commands/ViewHistoryCommand.java
@@ -0,0 +1,68 @@
+package seedu.flirtfork.commands;
+
+import seedu.flirtfork.Activity;
+import seedu.flirtfork.ActivityList;
+import seedu.flirtfork.Command;
+import seedu.flirtfork.FavouritesList;
+import seedu.flirtfork.Food;
+import seedu.flirtfork.FoodList;
+import seedu.flirtfork.Gift;
+import seedu.flirtfork.GiftList;
+import seedu.flirtfork.Storage;
+import seedu.flirtfork.Ui;
+import seedu.flirtfork.UserDetails;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+/**
+ * Command to view the user's history.
+ */
+public class ViewHistoryCommand extends Command {
+ /**
+ * Executes the list options command based on the specified option type.
+ *
+ * @param favourites The list of favourites.
+ * @param foods The list of foods.
+ * @param activities The list of activities.
+ * @param ui The user interface.
+ * @param storage The storage component.
+ * @param userDetails The user details.
+ * @param gifts The list of gifts.
+ * @throws FlirtForkException If an invalid option type is provided.
+ */
+ @Override
+ public void execute(FavouritesList favourites, FoodList foods, ActivityList activities, Ui ui,
+ Storage storage, UserDetails userDetails, GiftList gifts) throws FlirtForkException {
+ int foodCount = 0;
+ int activityCount = 0;
+ int giftCount = 0;
+
+ System.out.println("These are the activities you have marked:");
+ for (int i=0; i {
+ activities.get(2);
+ });
+ }
+
+ @Test
+ void get_validList_correctActivityRetrieved() {
+ String description = "Dancing";
+ Activity myActivity = new Activity(description, "C", "C", "U");
+ ActivityList activities = new ActivityList();
+ activities.add(myActivity);
+ assertEquals(description, activities.get(0).toString());
+ }
+
+ @Test
+ void getFilteredActivity_noAvailableOptions_throwsFlirtForkException() {
+ ActivityList activities = new ActivityList();
+ Activity myActivity1 = new Activity("Dancing", "C", "C", "U");
+ Activity myActivity2 = new Activity("Skydiving", "C", "C", "U");
+ activities.add(myActivity1);
+ activities.add(myActivity2);
+ assertThrows(FlirtForkException.class, () -> {
+ activities.getFilteredActivity("W", "C");
+ });
+ }
+}
diff --git a/src/test/java/seedu/flirtfork/ActivityTest.java b/src/test/java/seedu/flirtfork/ActivityTest.java
new file mode 100644
index 0000000000..7508209c66
--- /dev/null
+++ b/src/test/java/seedu/flirtfork/ActivityTest.java
@@ -0,0 +1,63 @@
+package seedu.flirtfork;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class ActivityTest {
+
+ @Test
+ public void generateActivity_nullDescripton_throwsException() {
+ String description = null;
+ String location = "C";
+ String price = "C";
+
+ Exception exception = assertThrows(IllegalArgumentException.class, () -> {
+ new Activity(description, location, price, "U");
+ });
+ assertTrue(exception.getMessage().contains("Description cannot be null"));
+ }
+
+ @Test
+ void getDescription_validDescription_expectSameDescription() {
+ String description = "Skydiving";
+ String location = "C";
+ String price = "C";
+ try {
+ Activity myActivity = new Activity(description, location, price, "U");
+ assertEquals(description, myActivity.getDescription());
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e.getMessage());
+ }
+ }
+
+ @Test
+ void toString_validDescription_expectSameDescription() {
+ String description = "Skydiving";
+ String location = "C";
+ String price = "C";
+ try {
+ Activity myActivity = new Activity(description, location, price, "U");
+ assertEquals(description, myActivity.toString());
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void markComplete_validCallToMarkComplete_expectCompleted() {
+ String description = "C";
+ String location = "C";
+ String price = "C";
+ try {
+ Activity myActivity = new Activity(description, location, price, "U");
+ assertEquals("U", myActivity.getCompletionStatus());
+ myActivity.markComplete();
+ assertEquals("C", myActivity.getCompletionStatus());
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/seedu/flirtfork/FavouritesListTest.java b/src/test/java/seedu/flirtfork/FavouritesListTest.java
new file mode 100644
index 0000000000..ba69f4f14b
--- /dev/null
+++ b/src/test/java/seedu/flirtfork/FavouritesListTest.java
@@ -0,0 +1,43 @@
+package seedu.flirtfork;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class FavouritesListTest {
+
+ @Test
+ void addFavourite_validFavourite_addedToFavouritesList() {
+ Favourites favourite = new Favourites("Test Favourite");
+ FavouritesList favouritesList = new FavouritesList();
+ favouritesList.addFavourite(favourite);
+ assertTrue(favouritesList.getFavourites().contains(favourite));
+ }
+
+ @Test
+ void deleteFavourite_validIndex_favouriteDeletedFromFavouritesList() {
+ Favourites favourite = new Favourites("Test Favourite");
+ FavouritesList favouritesList = new FavouritesList();
+ favouritesList.addFavourite(favourite);
+ favouritesList.deleteFavourite(0);
+ assertFalse(favouritesList.getFavourites().contains(favourite));
+ }
+
+ @Test
+ void getFormattedFavourites_emptyFavouritesList_returnsBlankCanvasMessage() {
+ FavouritesList favouritesList = new FavouritesList();
+ assertEquals("A blank canvas! Let's fill it with some love~", favouritesList.getFormattedFavourites());
+ }
+
+ @Test
+ void getFormattedFavourites_nonEmptyFavouritesList_returnsFormattedString() {
+ FavouritesList favouritesList = new FavouritesList();
+ favouritesList.addFavourite(new Favourites("Favourite 1"));
+ favouritesList.addFavourite(new Favourites("Favourite 2"));
+ String expected = "1. Favourite 1\n2. Favourite 2";
+ assertEquals(expected, favouritesList.getFormattedFavourites());
+ }
+
+}
diff --git a/src/test/java/seedu/flirtfork/FavouritesTest.java b/src/test/java/seedu/flirtfork/FavouritesTest.java
new file mode 100644
index 0000000000..00aa57a0a9
--- /dev/null
+++ b/src/test/java/seedu/flirtfork/FavouritesTest.java
@@ -0,0 +1,43 @@
+package seedu.flirtfork;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+class FavouritesTest {
+ //methodBeingTested_conditionToTest_ExpectedOutcome
+ @Test
+ void getDescription_validDescription_expectSameDescription() {
+ String description = "Test Favourite";
+ try {
+ Favourites favourite = new Favourites(description);
+ assertEquals(description, favourite.getDescription());
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e.getMessage());
+ }
+
+ }
+
+ @Test
+ void toString_validDescription_expectSameDescription() {
+ String description = "Test Favourite";
+ try {
+ Favourites favourite = new Favourites(description);
+ assertEquals(description, favourite.toString());
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e.getMessage());
+ }
+ }
+
+ @Test
+ void toFileFormat_validDescription_expectSameDescription() {
+ String description = "Test Favourite";
+ try {
+ Favourites favourite = new Favourites(description);
+ assertEquals(description, favourite.toFileFormat());
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/seedu/flirtfork/FlirtForkTest.java b/src/test/java/seedu/flirtfork/FlirtForkTest.java
new file mode 100644
index 0000000000..b0d44d03bd
--- /dev/null
+++ b/src/test/java/seedu/flirtfork/FlirtForkTest.java
@@ -0,0 +1,210 @@
+package seedu.flirtfork;
+
+import org.junit.jupiter.api.Test;
+import seedu.flirtfork.commands.GenerateIdeaCommand;
+import seedu.flirtfork.commands.GenerateItineraryCommand;
+import seedu.flirtfork.commands.GenerateSmartItineraryCommand;
+import seedu.flirtfork.commands.HelpCommand;
+import seedu.flirtfork.exceptions.FlirtForkException;
+import java.util.NoSuchElementException;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class FlirtForkTest {
+ private static final String FILE_PATH = "./data/FlirtFork.txt";
+ private Ui ui = new Ui();
+ private Storage storage = new Storage(FILE_PATH);
+ private FavouritesList favourites;
+ private FoodList foods;
+ private ActivityList activities;
+ private UserDetails userDetails;
+ private GiftList gifts;
+
+ public void sampleTest() {
+ assertTrue(true);
+ }
+
+ @Test
+ public void testGenerateIdeaCommand() {
+ try {
+ favourites = storage.loadFavourites();
+ foods = new FoodList(storage.loadFood());
+ activities = new ActivityList(storage.loadActivity());
+ userDetails = storage.loadUserDetails();
+ } catch (FileNotFoundException e) {
+ ui.errorMessage("File not found. Starting with an empty task list :)");
+ favourites = new FavouritesList(new ArrayList<>());
+ }
+ GenerateIdeaCommand generateIdeaCommand = new GenerateIdeaCommand();
+
+ // Backup original system
+ InputStream sysInBackup = System.in;
+ PrintStream sysOutBackup = System.out;
+
+ // Provide the simulated input
+ String inputData = "Yes";
+ ByteArrayInputStream in = new ByteArrayInputStream(inputData.getBytes());
+
+ // Capture System.out output
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintStream printStream = new PrintStream(outputStream);
+ System.setOut(printStream);
+
+ try {
+ generateIdeaCommand.execute(favourites, foods, activities, ui, storage, userDetails, gifts);
+ } catch (NoSuchElementException e) {
+ System.setIn(in);
+ String output = outputStream.toString();
+ assertTrue(output.contains("You can do"));
+ assertTrue(output.contains("and have a nice meal at"));
+ assertTrue(output.length() > 36);
+ } finally {
+ System.setIn(sysInBackup);
+ System.setOut(sysOutBackup);
+ }
+ }
+
+ @Test
+ public void generateItineraryCommand_validInputs_success() {
+ try {
+ favourites = storage.loadFavourites();
+ foods = new FoodList(storage.loadFood());
+ activities = new ActivityList(storage.loadActivity());
+ } catch (FileNotFoundException e) {
+ ui.errorMessage("File not found. Starting with an empty task list :)");
+ favourites = new FavouritesList(new ArrayList<>());
+ }
+ GenerateItineraryCommand generateItineraryCommand = new GenerateItineraryCommand("C C");
+
+ // Backup original system
+ InputStream sysInBackup = System.in;
+ PrintStream sysOutBackup = System.out;
+
+ // Provide the simulated input
+ String inputData = "Yes";
+ ByteArrayInputStream in = new ByteArrayInputStream(inputData.getBytes());
+
+ // Capture System.out output
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintStream printStream = new PrintStream(outputStream);
+ System.setOut(printStream);
+
+ try {
+ generateItineraryCommand.execute(favourites, foods, activities, ui, storage, userDetails, gifts);
+ } catch (NoSuchElementException e) {
+ System.setIn(in);
+ String output = outputStream.toString();
+ assertTrue(output.contains("Here is a rough itinerary for your date:"));
+ } catch (FlirtForkException e) {
+ ui.errorMessage(e.getMessage());
+ } finally {
+ System.setIn(sysInBackup);
+ System.setOut(sysOutBackup);
+ }
+ }
+
+ @Test
+ public void generateItineraryCommand_invalidInputs_errorMessagePrinted() {
+ try {
+ favourites = storage.loadFavourites();
+ foods = new FoodList(storage.loadFood());
+ activities = new ActivityList(storage.loadActivity());
+ } catch (FileNotFoundException e) {
+ ui.errorMessage("File not found. Starting with an empty task list :)");
+ favourites = new FavouritesList(new ArrayList<>());
+ }
+ GenerateItineraryCommand generateItineraryCommand = new GenerateItineraryCommand("THW GDBE");
+ try {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintStream printStream = new PrintStream(outputStream);
+ System.setOut(printStream);
+ generateItineraryCommand.execute(favourites, foods, activities, ui, storage, userDetails, gifts);
+ String output = outputStream.toString();
+ assertTrue(output.contains("Follow this format: 'itinerary LOCATION, PRICE'"));
+ System.setOut(System.out);
+ } catch (FlirtForkException e) {
+ ui.errorMessage(e.getMessage());
+ }
+ }
+
+ @Test
+ public void helpCommand_uiIsNull_throwsFlirtForkException() {
+ // Arrange
+ HelpCommand helpCommand = new HelpCommand();
+ Ui ui = null; // Simulating the UI being null to trigger the error condition.
+
+ // Act and Assert
+ FlirtForkException thrown = assertThrows(FlirtForkException.class, () -> {
+ helpCommand.execute(null, null, null, ui, null, null, null);
+ }, "UI component must not be null.");
+
+ assertTrue(thrown.getMessage().contains("UI component must not be null."));
+ }
+
+ @Test
+ void userDetailsSettingInvalidAgeThrowsException() {
+ UserDetails userDetails = new UserDetails();
+ Exception exceptionNegative = assertThrows(IllegalArgumentException.class, () -> {
+ userDetails.setAge("-1");
+ });
+ assertEquals("Age cannot be negative.", exceptionNegative.getMessage());
+
+ Exception exceptionNotInteger = assertThrows(IllegalArgumentException.class, () -> {
+ userDetails.setAge("twenty");
+ });
+ assertEquals("Age must be a valid integer.", exceptionNotInteger.getMessage());
+ }
+
+ @Test
+ void userDetailsSettingValidAgeUpdatesValue() {
+ UserDetails userDetails = new UserDetails();
+ String validAge = "25";
+ userDetails.setAge(validAge);
+ assertEquals(validAge, userDetails.getAge());
+ }
+
+ @Test
+ void userDetailsSettingInvalidGenderThrowsException() {
+ UserDetails userDetails = new UserDetails();
+ Exception exception = assertThrows(IllegalArgumentException.class, () -> {
+ userDetails.setGender("Alien");
+ });
+ assertEquals("Gender must be 'Male', 'Female', or 'Other'.", exception.getMessage());
+ }
+
+ @Test
+ void userDetailsSettingValidGenderUpdatesValue() {
+ UserDetails userDetails = new UserDetails();
+ String validGender = "Male";
+ userDetails.setGender(validGender);
+ assertEquals(validGender, userDetails.getGender());
+
+ validGender = "Female";
+ userDetails.setGender(validGender);
+ assertEquals(validGender, userDetails.getGender());
+
+ validGender = "Other";
+ userDetails.setGender(validGender);
+ assertEquals(validGender, userDetails.getGender());
+ }
+
+ @Test
+ public void generateSmartItineraryCommand_nullUserDetails_throwsException() {
+ Exception exception = assertThrows(IllegalArgumentException.class, () -> {
+ new GenerateSmartItineraryCommand(null);
+ });
+ assertTrue(exception.getMessage().contains("User details are required"));
+ }
+
+
+
+}
diff --git a/src/test/java/seedu/flirtfork/FoodListTest.java b/src/test/java/seedu/flirtfork/FoodListTest.java
new file mode 100644
index 0000000000..2e2abbdc3c
--- /dev/null
+++ b/src/test/java/seedu/flirtfork/FoodListTest.java
@@ -0,0 +1,47 @@
+package seedu.flirtfork;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+public class FoodListTest {
+
+ @Test
+ void addFood_validFood_addedToFoodList() {
+ Food myFood = new Food("Bistro Bar", "C", "C", "C", "U");
+ FoodList foods = new FoodList();
+ foods.add(myFood);
+ assertEquals(1, foods.size());
+ }
+
+ @Test
+ void get_emptyList_throwsIndexOutOfBoundsException() {
+ FoodList foods = new FoodList();
+ assertThrows(IndexOutOfBoundsException.class, () -> {
+ foods.get(2);
+ });
+ }
+
+ @Test
+ void get_validList_correctFoodRetrieved() {
+ String description = "Bistro Bar";
+ Food myFood = new Food("Bistro Bar", "C", "C", "C", "U");
+ FoodList foods = new FoodList();
+ foods.add(myFood);
+ assertEquals(description, foods.get(0).toString());
+ }
+
+ @Test
+ void getFilteredFood_noAvailableOptions_throwsFlirtForkException() {
+ FoodList foods = new FoodList();
+ Food myFood1 = new Food("Hwangs", "C", "C", "C", "U");
+ Food myFood2 = new Food("Udon Bar", "C", "C", "C", "U");
+ foods.add(myFood1);
+ foods.add(myFood2);
+ assertThrows(FlirtForkException.class, () -> {
+ foods.getFilteredFood("W", "C");
+ });
+ }
+}
diff --git a/src/test/java/seedu/flirtfork/FoodTest.java b/src/test/java/seedu/flirtfork/FoodTest.java
new file mode 100644
index 0000000000..4fbbb4ad3c
--- /dev/null
+++ b/src/test/java/seedu/flirtfork/FoodTest.java
@@ -0,0 +1,68 @@
+package seedu.flirtfork;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class FoodTest {
+
+ @Test
+ public void generateFood_nullDescripton_throwsException() {
+ String description = null;
+ String location = "C";
+ String price = "C";
+ String cuisine = "C";
+
+ Exception exception = assertThrows(IllegalArgumentException.class, () -> {
+ new Food(description, location, price, cuisine, "U");
+ });
+ assertTrue(exception.getMessage().contains("Description cannot be null"));
+ }
+
+ @Test
+ void getDescription_validDescription_expectSameDescription() {
+ String description = "Bistro Bar";
+ String location = "C";
+ String price = "C";
+ String cuisine = "C";
+ try {
+ Food myFood = new Food(description, location, price, cuisine, "U");
+ assertEquals(description, myFood.getDescription());
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e.getMessage());
+ }
+ }
+
+ @Test
+ void toString_validDescription_expectSameDescription() {
+ String description = "Bistro Bar";
+ String location = "C";
+ String price = "C";
+ String cuisine = "C";
+ try {
+ Food myFood = new Food(description, location, price, cuisine, "U");
+ assertEquals(description, myFood.toString());
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void markComplete_validCallToMarkComplete_expectCompleted() {
+ String description = "C";
+ String location = "C";
+ String price = "C";
+ String cuisine = "C";
+
+ try {
+ Food myFood = new Food(description, location, price, cuisine, "U");
+ assertEquals("U", myFood.getCompletionStatus());
+ myFood.markComplete();
+ assertEquals("C", myFood.getCompletionStatus());
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/seedu/flirtfork/ParserTest.java b/src/test/java/seedu/flirtfork/ParserTest.java
new file mode 100644
index 0000000000..4e0258b6c3
--- /dev/null
+++ b/src/test/java/seedu/flirtfork/ParserTest.java
@@ -0,0 +1,168 @@
+package seedu.flirtfork;
+
+import org.junit.jupiter.api.Test;
+import seedu.flirtfork.commands.AddActivityCommand;
+import seedu.flirtfork.commands.AddFoodCommand;
+import seedu.flirtfork.commands.DeleteFavouritesCommand;
+import seedu.flirtfork.commands.ExitCommand;
+import seedu.flirtfork.commands.ListFavouritesCommand;
+import seedu.flirtfork.commands.UserDetailsCommand;
+import seedu.flirtfork.commands.HelpCommand;
+import seedu.flirtfork.commands.GenerateGiftCommand;
+import seedu.flirtfork.commands.ListOptionsCommand;
+import seedu.flirtfork.exceptions.FlirtForkException;
+
+import org.junit.jupiter.api.BeforeEach;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+class ParserTest {
+
+ private UserDetails userDetails;
+
+ @BeforeEach
+ void setUp() {
+ // Create a dummy UserDetails object for testing
+ userDetails = new UserDetails("John Doe", "25", "Male", "Single", "Central",
+ "Italian", "14/2/2024");
+ }
+
+ // 3 part format
+ //methodBeingTested_conditionToTest_ExpectedOutcome
+ @Test
+ void parseCommand_emptyInput_expectException() {
+ // Test to ensure that exception is thrown for empty input
+ assertThrows(FlirtForkException.class, () -> {
+ Parser.parseCommand("", userDetails);
+ }, "Empty input should throw FlirtForkException");
+ }
+
+ @Test
+ void parseCommand_exitCommand_expectExitMessage() {
+ try {
+ Command result = Parser.parseCommand("exit", userDetails);
+ assertTrue(result instanceof ExitCommand, "Result should be an instance of ExitCommand");
+ } catch (FlirtForkException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ void parseCommand_addFoodCommand_expectAddFoodCommand() {
+ try {
+ Command result = Parser.parseCommand("food sushi express", userDetails);
+ assertTrue(result instanceof AddFoodCommand, "Expected AddFoodCommand for 'food' input.");
+ } catch (Exception e) {
+ fail("Exception should not be thrown for valid 'food' input.");
+ }
+ }
+
+ @Test
+ void parseCommand_foodCommandWithEmptyArguments_expectException() {
+ // Test food command without arguments throws an exception
+ assertThrows(FlirtForkException.class, () -> {
+ Parser.parseCommand("food", userDetails);
+ }, "Food command without description should throw FlirtForkException");
+ }
+
+ @Test
+ void parseCommand_addActivityCommandInput_expectAddActivityCommand() {
+ try {
+ Command result = Parser.parseCommand("activity karaoke", userDetails);
+ assertTrue(result instanceof AddActivityCommand, "Expected AddActivityCommand for 'activity' input.");
+ } catch (Exception e) {
+ fail("Exception should not be thrown for valid 'activity' input.");
+ }
+ }
+
+ @Test
+ void parseCommand_userDetailsCommandInput_expectUserDetailsCommand() {
+ try {
+ Command result = Parser.parseCommand("me", userDetails);
+ assertTrue(result instanceof UserDetailsCommand, "Expected UserDetailsCommand for 'me' input.");
+ } catch (Exception e) {
+ fail("Exception should not be thrown for valid 'me' input.");
+ }
+ }
+
+ @Test
+ void parseCommand_invalidUserDetailsCommand_expectException() {
+ // Test that an invalid command for user details throws FlirtForkException
+ assertThrows(FlirtForkException.class, () -> {
+ Parser.parseCommand("undefined 123", userDetails);
+ }, "Undefined command should throw FlirtForkException");
+ }
+
+ @Test
+ void parseCommand_listFavouritesCommandInput_expectListFavouritesCommand() {
+ try {
+ Command result = Parser.parseCommand("favourites", userDetails);
+ assertTrue(result instanceof ListFavouritesCommand,
+ "Expected ListFavouritesCommand for 'favourites' input.");
+ } catch (Exception e) {
+ fail("Exception should not be thrown for valid 'favourites' input.");
+ }
+ }
+
+ @Test
+ void parseCommand_deleteFavouritesCommandInputWithValidIndex_expectDeleteFavouritesCommand() {
+ try {
+ Command result = Parser.parseCommand("delete 1", userDetails);
+ assertTrue(result instanceof DeleteFavouritesCommand,
+ "Expected DeleteFavouritesCommand for 'delete' input with a valid index.");
+ } catch (Exception e) {
+ fail("Exception should not be thrown for valid 'delete' input with a valid index.");
+ }
+ }
+
+ @Test
+ void parseCommand_helpCommand_expectHelpCommand() {
+ // Test for correct parsing of the help command
+ assertDoesNotThrow(() -> {
+ Command result = Parser.parseCommand("help", userDetails);
+ assertTrue(result instanceof HelpCommand, "Expected HelpCommand for 'help'.");
+ });
+ }
+
+ @Test
+ void parseCommand_giftDefault_expectGenerateGiftCommandWithAny() {
+ try {
+ Command result = Parser.parseCommand("gift", userDetails);
+ assertTrue(result instanceof GenerateGiftCommand);
+ } catch (FlirtForkException e) {
+ fail("Should not throw an exception for 'gift'");
+ }
+ }
+
+ @Test
+ void parseCommand_giftWithInvalidArgument_expectException() {
+ // Test that an invalid gender argument for gift command throws FlirtForkException
+ String invalidGender = "invalid_gender";
+ String userInput = "gift " + invalidGender;
+ assertThrows(FlirtForkException.class, () -> Parser.parseCommand(userInput, userDetails),
+ "An invalid gender argument should throw a FlirtForkException.");
+ }
+
+ @Test
+ void parseCommand_giftWithMultipleArguments_expectException() {
+ // Test that multiple arguments for gift command throws FlirtForkException
+ String userInput = "gift male female";
+ assertThrows(FlirtForkException.class, () -> Parser.parseCommand(userInput, userDetails),
+ "Multiple arguments should throw a FlirtForkException.");
+ }
+
+ @Test
+ void parseCommand_listInput_expectListOptionsCommand() {
+ String userInput = "list";
+ try {
+ Command result = Parser.parseCommand(userInput, userDetails);
+ assertTrue(result instanceof ListOptionsCommand, "Expected ListOptionsCommand for 'list' input.");
+ } catch (FlirtForkException e) {
+ fail("No exception should be thrown for valid 'list' input.");
+ }
+ }
+
+}
diff --git a/src/test/java/seedu/flirtfork/UiTest.java b/src/test/java/seedu/flirtfork/UiTest.java
new file mode 100644
index 0000000000..f87beddb4b
--- /dev/null
+++ b/src/test/java/seedu/flirtfork/UiTest.java
@@ -0,0 +1,108 @@
+package seedu.flirtfork;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class UiTest {
+
+ @Test
+ public void readAnniversaryDate_validInput() {
+ String input = "14/02/2024";
+ InputStream in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ Ui ui = new Ui();
+
+ String result = ui.readAnniversaryDate();
+
+ assertEquals(input, result);
+ }
+
+ @Test
+ public void readUserGender_validMaleInput() {
+ String input = "Male";
+ InputStream in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ Ui ui = new Ui();
+
+ String result = ui.readUserGender();
+
+ assertEquals("Male", result);
+ }
+
+ @Test
+ public void readUserGender_validFemaleInput() {
+ String input = "Female";
+ InputStream in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ Ui ui = new Ui();
+
+ String result = ui.readUserGender();
+
+ assertEquals("Female", result);
+ }
+
+ @Test
+ public void readUserLocation_validInput() {
+ String input = "C";
+ InputStream in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ Ui ui = new Ui();
+
+ String result = ui.readUserLocation();
+
+ assertEquals("C", result);
+ }
+
+ @Test
+ public void readAge_validInput() {
+ String input = "25";
+ InputStream in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ Ui ui = new Ui();
+
+ String result = ui.readAge();
+
+ assertEquals("25", result);
+ }
+
+ @Test
+ public void readName_validInput() {
+ String input = "John Doe";
+ InputStream in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ Ui ui = new Ui();
+
+ String result = ui.readName();
+
+ assertEquals("John Doe", result);
+ }
+
+ @Test
+ public void readUserCuisine_validInput() {
+ String input = "J";
+ InputStream in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ Ui ui = new Ui();
+
+ String result = ui.readUserCuisine();
+
+ assertEquals("J", result);
+ }
+
+ @Test
+ public void readUserStatus_validInput() {
+ String input = "S";
+ InputStream in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ Ui ui = new Ui();
+
+ String result = ui.readUserStatus();
+
+ assertEquals("S", result);
+ }
+
+}
diff --git a/src/test/java/seedu/flirtfork/commands/ListOptionsCommandTest.java b/src/test/java/seedu/flirtfork/commands/ListOptionsCommandTest.java
new file mode 100644
index 0000000000..ce1d8e3274
--- /dev/null
+++ b/src/test/java/seedu/flirtfork/commands/ListOptionsCommandTest.java
@@ -0,0 +1,44 @@
+package seedu.flirtfork.commands;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+class ListOptionsCommandTest {
+
+ private final PrintStream standardOut = System.out;
+ private final InputStream standardIn = System.in;
+ private ByteArrayOutputStream outputStream;
+
+ @BeforeEach
+ void setUp() {
+ outputStream = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(outputStream));
+ }
+
+ @AfterEach
+ void tearDown() {
+ System.setIn(standardIn);
+ System.setOut(standardOut);
+ }
+
+ @Test
+ void invalidOptionType_expectUserInputPrompt() {
+ String simulatedUserInput = "food\n";
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(simulatedUserInput.getBytes());
+ System.setIn(inputStream);
+
+ ListOptionsCommand listOptionsCommand = new ListOptionsCommand();
+ System.out.flush();
+
+ String output = outputStream.toString();
+ assertFalse(output.contains("Invalid option! Please choose 'food', 'activities', 'gifts' or 'cancel'."));
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..ba04630971 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +1,9 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
-What is your name?
-Hello James Gosling
+____________________________________________________________
+Hungry for love? You've come to the right place.
+Welcome to Flirt and Fork - where Cupid meets the chef!
+Before we start, I would like to know you better!
+____________________________________________________________
+Please enter your name:
+Great! Hello there exit, it's my pleasure to know you!
+____________________________________________________________
+Please enter your age:
diff --git a/text-ui-test/data/FlirtFork.txt b/text-ui-test/data/FlirtFork.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..ae3bc0a936 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +1 @@
-James Gosling
\ No newline at end of file
+exit
\ No newline at end of file
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 25ac7a2989..470052dec1 100644
--- a/text-ui-test/runtest.bat
+++ b/text-ui-test/runtest.bat
@@ -16,4 +16,7 @@ java -jar %jarloc% < ..\..\text-ui-test\input.txt > ..\..\text-ui-test\ACTUAL.TX
cd ..\..\text-ui-test
+findstr /v /c:"OOPS!" ACTUAL.TXT > ACTUAL_FILTERED.TXT
+move /y ACTUAL_FILTERED.TXT ACTUAL.TXT
+
FC ACTUAL.TXT EXPECTED.TXT >NUL && ECHO Test passed! || Echo Test failed!
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
index 1dcbd12021..3ca00ae8d3 100755
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -10,6 +10,9 @@ cd text-ui-test
java -jar $(find ../build/libs/ -mindepth 1 -print -quit) < input.txt > ACTUAL.TXT
+grep -v "OOPS!" ACTUAL.TXT > ACTUAL_FILTERED.TXT
+mv ACTUAL_FILTERED.TXT ACTUAL.TXT
+
cp EXPECTED.TXT EXPECTED-UNIX.TXT
dos2unix EXPECTED-UNIX.TXT ACTUAL.TXT
diff EXPECTED-UNIX.TXT ACTUAL.TXT