1#include "database.h"
2#include <iostream>
3#include <limits>
4#include <iomanip>
5
6/**
7 * @brief Clear input buffer after invalid input
8 */
9void clearInput() {
10 std::cin.clear();
11 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
12}
13
14/**
15 * @brief Display main menu
16 */
17void displayMenu() {
18 std::cout << "\n";
19 std::cout << "╔════════════════════════════════════════╗\n";
20 std::cout << "║ STUDENT MANAGEMENT SYSTEM ║\n";
21 std::cout << "╠════════════════════════════════════════╣\n";
22 std::cout << "║ 1. Add Student ║\n";
23 std::cout << "║ 2. Remove Student ║\n";
24 std::cout << "║ 3. Find Student by ID ║\n";
25 std::cout << "║ 4. Find Students by Name ║\n";
26 std::cout << "║ 5. List All Students ║\n";
27 std::cout << "║ 6. Sort Students ║\n";
28 std::cout << "║ 7. Statistics ║\n";
29 std::cout << "║ 8. Save to File ║\n";
30 std::cout << "║ 9. Load from File ║\n";
31 std::cout << "║ 0. Exit ║\n";
32 std::cout << "╚════════════════════════════════════════╝\n";
33 std::cout << "Choice: ";
34}
35
36/**
37 * @brief Add a new student interactively
38 */
39void addStudentInteractive(StudentDatabase& db) {
40 int id;
41 std::string name, major;
42 double gpa;
43
44 std::cout << "\nAdd New Student\n";
45 std::cout << "---------------\n";
46 std::cout << "Student ID: ";
47 std::cin >> id;
48 clearInput();
49
50 std::cout << "Name: ";
51 std::getline(std::cin, name);
52
53 std::cout << "Major: ";
54 std::getline(std::cin, major);
55
56 std::cout << "GPA (0.0-4.0): ";
57 std::cin >> gpa;
58
59 try {
60 auto student = std::make_shared<Student>(id, name, major, gpa);
61 db.addStudent(student);
62 std::cout << "Student added successfully!\n";
63 } catch (const std::exception& e) {
64 std::cerr << "Error: " << e.what() << "\n";
65 }
66}
67
68/**
69 * @brief Display sort submenu
70 */
71void sortSubmenu(StudentDatabase& db) {
72 std::cout << "\nSort By:\n";
73 std::cout << "1. ID\n";
74 std::cout << "2. Name\n";
75 std::cout << "3. GPA (descending)\n";
76 std::cout << "Choice: ";
77
78 int choice;
79 std::cin >> choice;
80
81 switch (choice) {
82 case 1:
83 db.sortById();
84 std::cout << "Sorted by ID.\n";
85 break;
86 case 2:
87 db.sortByName();
88 std::cout << "Sorted by Name.\n";
89 break;
90 case 3:
91 db.sortByGpa();
92 std::cout << "Sorted by GPA (descending).\n";
93 break;
94 default:
95 std::cout << "Invalid choice.\n";
96 }
97}
98
99/**
100 * @brief Display statistics
101 */
102void showStatistics(const StudentDatabase& db) {
103 std::cout << "\n" << std::string(50, '=') << "\n";
104 std::cout << "STATISTICS\n";
105 std::cout << std::string(50, '=') << "\n";
106
107 std::cout << "Total Students: " << db.size() << "\n";
108
109 if (db.size() > 0) {
110 std::cout << "Average GPA: " << std::fixed << std::setprecision(2)
111 << db.calculateAverageGpa() << "\n\n";
112
113 std::cout << "Students by Major:\n";
114 auto majorCounts = db.countByMajor();
115 for (const auto& [major, count] : majorCounts) {
116 std::cout << " " << std::setw(20) << std::left << major
117 << ": " << count << " student(s)\n";
118 }
119 }
120
121 std::cout << std::string(50, '=') << "\n";
122}
123
124/**
125 * @brief Main application entry point
126 */
127int main() {
128 StudentDatabase db;
129 int choice;
130
131 std::cout << "Welcome to Student Management System (C++17)\n";
132 std::cout << "Demonstrates: STL, Smart Pointers, File I/O, Exception Handling\n";
133
134 while (true) {
135 displayMenu();
136 std::cin >> choice;
137
138 if (std::cin.fail()) {
139 clearInput();
140 std::cout << "Invalid input. Please enter a number.\n";
141 continue;
142 }
143
144 try {
145 switch (choice) {
146 case 0:
147 std::cout << "Goodbye!\n";
148 return 0;
149
150 case 1:
151 addStudentInteractive(db);
152 break;
153
154 case 2: {
155 int id;
156 std::cout << "Enter Student ID to remove: ";
157 std::cin >> id;
158 db.removeStudent(id);
159 std::cout << "Student removed successfully.\n";
160 break;
161 }
162
163 case 3: {
164 int id;
165 std::cout << "Enter Student ID: ";
166 std::cin >> id;
167 auto student = db.findById(id);
168 std::cout << "\nFound: " << *student << "\n";
169 break;
170 }
171
172 case 4: {
173 std::string name;
174 std::cout << "Enter name (or partial name): ";
175 clearInput();
176 std::getline(std::cin, name);
177 auto results = db.findByName(name);
178
179 if (results.empty()) {
180 std::cout << "No students found matching '" << name << "'\n";
181 } else {
182 std::cout << "\nFound " << results.size() << " student(s):\n";
183 for (const auto& s : results) {
184 std::cout << *s << "\n";
185 }
186 }
187 break;
188 }
189
190 case 5:
191 db.listAll();
192 break;
193
194 case 6:
195 sortSubmenu(db);
196 break;
197
198 case 7:
199 showStatistics(db);
200 break;
201
202 case 8: {
203 std::string filename;
204 std::cout << "Enter filename (e.g., students.csv): ";
205 std::cin >> filename;
206 db.saveToFile(filename);
207 break;
208 }
209
210 case 9: {
211 std::string filename;
212 std::cout << "Enter filename: ";
213 std::cin >> filename;
214 db.loadFromFile(filename);
215 break;
216 }
217
218 default:
219 std::cout << "Invalid choice. Please try again.\n";
220 }
221
222 } catch (const StudentNotFoundException& e) {
223 std::cerr << "Error: " << e.what() << "\n";
224 } catch (const std::exception& e) {
225 std::cerr << "Error: " << e.what() << "\n";
226 }
227 }
228
229 return 0;
230}