1+ import com .google .gson .Gson ;
2+ import com .google .gson .GsonBuilder ;
3+ import com .google .gson .reflect .TypeToken ;
4+
5+ import java .io .*;
6+ import java .lang .reflect .Type ;
7+ import java .util .*;
8+ import java .util .stream .Collectors ;
9+
10+ /**
11+ * TaskManager handles all task operations including CRUD, file I/O, and filtering
12+ */
13+ public class TaskManager {
14+ private static final String TASKS_FILE = "tasks.json" ;
15+ private List <Task > tasks ;
16+ private Gson gson ;
17+
18+ public TaskManager () {
19+ this .tasks = new ArrayList <>();
20+ this .gson = new GsonBuilder ().setPrettyPrinting ().create ();
21+ loadTasks ();
22+ }
23+
24+ /**
25+ * Adds a new task with auto-categorization
26+ *
27+ * @param title The task title
28+ * @return The created task
29+ */
30+ public Task addTask (String title ) {
31+ if (title == null || title .trim ().isEmpty ()) {
32+ throw new IllegalArgumentException ("Task title cannot be empty" );
33+ }
34+
35+ String category = AICategorizer .categorizeTask (title );
36+ Task task = new Task (title .trim (), category );
37+ tasks .add (task );
38+ saveTasks ();
39+
40+ System .out .println ("\n ✅ Task added successfully!" );
41+ System .out .println ("📝 Title: " + task .getTitle ());
42+ System .out .println ("🏷️ Category: " + task .getCategoryEmoji () + " " + task .getCategory ());
43+ System .out .println ("🎯 Confidence: " + String .format ("%.0f%%" , AICategorizer .getConfidenceScore (title , category ) * 100 ));
44+ System .out .println (AICategorizer .getSuggestion (title ));
45+
46+ return task ;
47+ }
48+
49+ /**
50+ * Adds a task with manual category override
51+ *
52+ * @param title The task title
53+ * @param category The manual category
54+ * @return The created task
55+ */
56+ public Task addTask (String title , String category ) {
57+ if (title == null || title .trim ().isEmpty ()) {
58+ throw new IllegalArgumentException ("Task title cannot be empty" );
59+ }
60+
61+ if (!isValidCategory (category )) {
62+ category = AICategorizer .categorizeTask (title );
63+ System .out .println ("⚠️ Invalid category provided. Auto-categorized as: " + category );
64+ }
65+
66+ Task task = new Task (title .trim (), category );
67+ tasks .add (task );
68+ saveTasks ();
69+
70+ System .out .println ("\n ✅ Task added with manual category!" );
71+ System .out .println ("📝 Title: " + task .getTitle ());
72+ System .out .println ("🏷️ Category: " + task .getCategoryEmoji () + " " + task .getCategory ());
73+
74+ return task ;
75+ }
76+
77+ /**
78+ * Views all tasks with optional filtering
79+ */
80+ public void viewAllTasks () {
81+ if (tasks .isEmpty ()) {
82+ System .out .println ("\n 📭 No tasks found. Add some tasks to get started!" );
83+ return ;
84+ }
85+
86+ System .out .println ("\n 📋 ALL TASKS" );
87+ System .out .println ("=" .repeat (50 ));
88+
89+ for (int i = 0 ; i < tasks .size (); i ++) {
90+ System .out .printf ("%d. %s\n " , i + 1 , tasks .get (i ).toString ());
91+ }
92+
93+ printTaskSummary ();
94+ }
95+
96+ /**
97+ * Views tasks by category
98+ *
99+ * @param category The category to filter by
100+ */
101+ public void viewTasksByCategory (String category ) {
102+ List <Task > filteredTasks = tasks .stream ()
103+ .filter (task -> task .getCategory ().equalsIgnoreCase (category ))
104+ .collect (Collectors .toList ());
105+
106+ if (filteredTasks .isEmpty ()) {
107+ System .out .println ("\n 📭 No tasks found in category: " + category );
108+ return ;
109+ }
110+
111+ System .out .println ("\n 📋 TASKS IN CATEGORY: " + category .toUpperCase ());
112+ System .out .println ("=" .repeat (50 ));
113+
114+ for (int i = 0 ; i < filteredTasks .size (); i ++) {
115+ System .out .printf ("%d. %s\n " , i + 1 , filteredTasks .get (i ).toString ());
116+ }
117+ }
118+
119+ /**
120+ * Views only pending tasks
121+ */
122+ public void viewPendingTasks () {
123+ List <Task > pendingTasks = tasks .stream ()
124+ .filter (task -> !task .isCompleted ())
125+ .collect (Collectors .toList ());
126+
127+ if (pendingTasks .isEmpty ()) {
128+ System .out .println ("\n 🎉 Congratulations! No pending tasks. You're all caught up!" );
129+ return ;
130+ }
131+
132+ System .out .println ("\n ⏳ PENDING TASKS" );
133+ System .out .println ("=" .repeat (50 ));
134+
135+ for (int i = 0 ; i < pendingTasks .size (); i ++) {
136+ System .out .printf ("%d. %s\n " , i + 1 , pendingTasks .get (i ).toString ());
137+ }
138+ }
139+
140+ /**
141+ * Views only completed tasks
142+ */
143+ public void viewCompletedTasks () {
144+ List <Task > completedTasks = tasks .stream ()
145+ .filter (Task ::isCompleted )
146+ .collect (Collectors .toList ());
147+
148+ if (completedTasks .isEmpty ()) {
149+ System .out .println ("\n 📭 No completed tasks yet. Complete some tasks to see them here!" );
150+ return ;
151+ }
152+
153+ System .out .println ("\n ✅ COMPLETED TASKS" );
154+ System .out .println ("=" .repeat (50 ));
155+
156+ for (int i = 0 ; i < completedTasks .size (); i ++) {
157+ System .out .printf ("%d. %s\n " , i + 1 , completedTasks .get (i ).toString ());
158+ }
159+ }
160+
161+ /**
162+ * Marks a task as complete by index
163+ *
164+ * @param index The task index (1-based)
165+ * @return true if successful, false otherwise
166+ */
167+ public boolean completeTask (int index ) {
168+ if (index < 1 || index > tasks .size ()) {
169+ System .out .println ("❌ Invalid task number. Please enter a number between 1 and " + tasks .size ());
170+ return false ;
171+ }
172+
173+ Task task = tasks .get (index - 1 );
174+ if (task .isCompleted ()) {
175+ System .out .println ("ℹ️ Task is already completed: " + task .getTitle ());
176+ return false ;
177+ }
178+
179+ task .markComplete ();
180+ saveTasks ();
181+
182+ System .out .println ("\n 🎉 Task completed successfully!" );
183+ System .out .println ("✅ " + task .getTitle ());
184+
185+ return true ;
186+ }
187+
188+ /**
189+ * Deletes a task by index
190+ *
191+ * @param index The task index (1-based)
192+ * @return true if successful, false otherwise
193+ */
194+ public boolean deleteTask (int index ) {
195+ if (index < 1 || index > tasks .size ()) {
196+ System .out .println ("❌ Invalid task number. Please enter a number between 1 and " + tasks .size ());
197+ return false ;
198+ }
199+
200+ Task task = tasks .get (index - 1 );
201+ tasks .remove (index - 1 );
202+ saveTasks ();
203+
204+ System .out .println ("\n 🗑️ Task deleted successfully!" );
205+ System .out .println ("❌ " + task .getTitle ());
206+
207+ return true ;
208+ }
209+
210+ /**
211+ * Searches tasks by keyword
212+ *
213+ * @param keyword The search keyword
214+ */
215+ public void searchTasks (String keyword ) {
216+ if (keyword == null || keyword .trim ().isEmpty ()) {
217+ System .out .println ("❌ Search keyword cannot be empty" );
218+ return ;
219+ }
220+
221+ List <Task > matchingTasks = tasks .stream ()
222+ .filter (task -> task .getTitle ().toLowerCase ().contains (keyword .toLowerCase ()))
223+ .collect (Collectors .toList ());
224+
225+ if (matchingTasks .isEmpty ()) {
226+ System .out .println ("\n 🔍 No tasks found matching: " + keyword );
227+ return ;
228+ }
229+
230+ System .out .println ("\n 🔍 SEARCH RESULTS FOR: " + keyword );
231+ System .out .println ("=" .repeat (50 ));
232+
233+ for (int i = 0 ; i < matchingTasks .size (); i ++) {
234+ System .out .printf ("%d. %s\n " , i + 1 , matchingTasks .get (i ).toString ());
235+ }
236+ }
237+
238+ /**
239+ * Prints task summary statistics
240+ */
241+ public void printTaskSummary () {
242+ int total = tasks .size ();
243+ int completed = (int ) tasks .stream ().filter (Task ::isCompleted ).count ();
244+ int pending = total - completed ;
245+
246+ Map <String , Long > categoryCount = tasks .stream ()
247+ .collect (Collectors .groupingBy (Task ::getCategory , Collectors .counting ()));
248+
249+ System .out .println ("\n 📊 TASK SUMMARY" );
250+ System .out .println ("-" .repeat (30 ));
251+ System .out .println ("📝 Total Tasks: " + total );
252+ System .out .println ("✅ Completed: " + completed );
253+ System .out .println ("⏳ Pending: " + pending );
254+
255+ if (!categoryCount .isEmpty ()) {
256+ System .out .println ("\n 📂 By Category:" );
257+ categoryCount .forEach ((category , count ) -> {
258+ String emoji = getEmojiForCategory (category );
259+ System .out .println (" " + emoji + " " + category + ": " + count );
260+ });
261+ }
262+
263+ if (total > 0 ) {
264+ double completionRate = (double ) completed / total * 100 ;
265+ System .out .printf ("\n 🎯 Completion Rate: %.1f%%\n " , completionRate );
266+ }
267+ }
268+
269+ /**
270+ * Loads tasks from JSON file
271+ */
272+ private void loadTasks () {
273+ File file = new File (TASKS_FILE );
274+ if (!file .exists ()) {
275+ System .out .println ("📁 Creating new tasks file: " + TASKS_FILE );
276+ return ;
277+ }
278+
279+ try (FileReader reader = new FileReader (file )) {
280+ Type taskListType = new TypeToken <List <Task >>(){}.getType ();
281+ List <Task > loadedTasks = gson .fromJson (reader , taskListType );
282+
283+ if (loadedTasks != null ) {
284+ this .tasks = loadedTasks ;
285+ System .out .println ("📂 Loaded " + tasks .size () + " tasks from " + TASKS_FILE );
286+ }
287+ } catch (IOException e ) {
288+ System .err .println ("❌ Error loading tasks: " + e .getMessage ());
289+ } catch (Exception e ) {
290+ System .err .println ("❌ Error parsing tasks file: " + e .getMessage ());
291+ System .out .println ("🔄 Starting with empty task list" );
292+ }
293+ }
294+
295+ /**
296+ * Saves tasks to JSON file
297+ */
298+ private void saveTasks () {
299+ try (FileWriter writer = new FileWriter (TASKS_FILE )) {
300+ gson .toJson (tasks , writer );
301+ } catch (IOException e ) {
302+ System .err .println ("❌ Error saving tasks: " + e .getMessage ());
303+ }
304+ }
305+
306+ /**
307+ * Validates if a category is valid
308+ *
309+ * @param category The category to validate
310+ * @return true if valid, false otherwise
311+ */
312+ private boolean isValidCategory (String category ) {
313+ String [] validCategories = AICategorizer .getAvailableCategories ();
314+ return Arrays .stream (validCategories )
315+ .anyMatch (cat -> cat .equalsIgnoreCase (category ));
316+ }
317+
318+ /**
319+ * Gets emoji for a category
320+ *
321+ * @param category The category name
322+ * @return The corresponding emoji
323+ */
324+ private String getEmojiForCategory (String category ) {
325+ switch (category .toLowerCase ()) {
326+ case "work" : return "💼" ;
327+ case "personal" : return "👤" ;
328+ case "urgent" : return "🚨" ;
329+ default : return "📝" ;
330+ }
331+ }
332+
333+ /**
334+ * Gets the total number of tasks
335+ *
336+ * @return The total task count
337+ */
338+ public int getTaskCount () {
339+ return tasks .size ();
340+ }
341+
342+ /**
343+ * Gets the list of all tasks (read-only)
344+ *
345+ * @return Unmodifiable list of tasks
346+ */
347+ public List <Task > getTasks () {
348+ return Collections .unmodifiableList (tasks );
349+ }
350+
351+ /**
352+ * Clears all completed tasks
353+ *
354+ * @return Number of tasks cleared
355+ */
356+ public int clearCompletedTasks () {
357+ int initialSize = tasks .size ();
358+ tasks .removeIf (Task ::isCompleted );
359+ int removedCount = initialSize - tasks .size ();
360+
361+ if (removedCount > 0 ) {
362+ saveTasks ();
363+ System .out .println ("\n 🧹 Cleared " + removedCount + " completed task(s)" );
364+ } else {
365+ System .out .println ("\n 📭 No completed tasks to clear" );
366+ }
367+
368+ return removedCount ;
369+ }
370+ }
0 commit comments