#include #include // streamsize::max #include #include "booking.cpp" /* declaring X(option) generates: - Ordered menu message entry - Ordered Menu::State::option symbol */ #define MENU_ENTRIES \ X(RESERVE), \ X(CANCEL), \ X(CHECK), \ X(ACCUMULATE), \ X(SORT), \ X(MAX_ELEMENT), \ X(REVERSE), \ X(COUNT), \ X(REPLACE), \ X(COPY), \ X(EXIT), \ X(SHUTDOWN) #include "menugen.cpp" #include "module.cpp" namespace Menu { // generate enum #define X(entry) entry enum class State : StateType{ MENU = 0, // default state MENU_ENTRIES, COUNT_TAG // number of menu entries }; #undef X // generate menu message constexpr auto generateMenuMessage() { constexpr char menuMsg[] = "=== Ticket Booking System ==="; constexpr char dot[] = ". "; constexpr char nl[] = "\n"; constexpr auto str = concat( nl, nl, menuMsg, nl, #define X(entry) NumToString(State::entry)>::value, dot, #entry, nl MENU_ENTRIES #undef X ); return str; } } constexpr auto MENU_MSG = Menu::generateMenuMessage(); std::istream& operator>>(std::istream& is, Menu::State& state) { if (!(is >> reinterpret_cast(state))) throw Booking::InvalidArgument(); return is; } std::ostream& operator<<(std::ostream& os, Booking::seat_range r) { if (r.second == r.first) os << " seat " << r.first << " is " << std::flush; else os << " seats " << r.first << "-" << r.second << " are " << std::flush; return os; } Booking::Seat::Type getType(std::istream& is, std::ostream& os) { uint64_t n; os << "Enter seat type: 0 - VIP, 1 - REGULAR, 2 - CHEAP\n" << std::flush; if (!(is >> n) || (n != 0 && n != 1 && n != 2)) { is.clear(); is.ignore(std::numeric_limits::max(), '\n'); throw Booking::InvalidArgument(); } return static_cast(n); } std::string getName(std::istream& is, std::ostream& os) { std::string name; os << "Enter name: " << std::flush; is >> std::ws; if (!std::getline(is, name) || name.empty()) { is.clear(); throw Booking::InvalidArgument(); } return name; } std::pair getRange(std::istream& is) { auto isEnd = [](char c) { return c == '\n' || c == EOF; }; try { std::pair output; // if (!(is >> output.first) || output.first < 1 || output.first > Booking::SEAT_CAPACITY) // throw Booking::InvalidSeat(); output.second = output.first; while (!std::isdigit(is.peek())) { char c = is.get(); if (c == '-') break; if (isEnd(c)) return output; if (!std::iswspace(c)) throw Booking::InvalidSeat(); } // if (!(is >> output.second) || output.second < 1 || output.second > Booking::SEAT_CAPACITY) // throw Booking::InvalidSeat(); char c = is.peek(); if (isEnd(c)) { is.get(); // clear newline or eof if (output.first > output.second) std::swap(output.first, output.second); return output; } while (std::iswspace(c - is.get())) if (isEnd(c)) return output; throw Booking::InvalidSeat(); // unexpected character } catch(...) { throw Booking::InvalidSeat(); } } void* menuInit() { return new Booking::Booker; // return nullptr; } void menuCleanup(void* ctx) { Booking::Booker* h = reinterpret_cast(ctx); delete h; } bool menu(std::istream& is, std::ostream& os, void* ctx) { Menu::State state = Menu::State::MENU; Booking::Booker& h = *reinterpret_cast(ctx); Booking::Seat::Type t; std::string name, name_rep; //---------------- std::pair stats; std::vector sorted; std::pair cli; size_t cnt; std::vector vips; //---------------- Booking::seat_range range; for (;;) try { state = Menu::State::MENU; os << Menu::BOLDWHITE << MENU_MSG << Menu::RESET << Menu::CURSOR << std::flush; is >> state; switch (state) { case Menu::State::RESERVE: os << "Seat(s) to be reserved:\n" << Menu::CURSOR << std::flush; t = getType(is, os); name = getName(is, os); h.reserveSeat(t, name, os); break; case Menu::State::CANCEL: os << "Seat(s) to be cancelled:\n" << Menu::CURSOR << std::flush; name = getName(is, os); h.cancelSeat(os, name); break; case Menu::State::CHECK: h.print(os); break; case Menu::State::EXIT: os << "Client exiting..." << std::endl; return EXIT_SUCCESS; break; case Menu::State::SHUTDOWN: os << "Sending shutdown signal..." << std::endl; return 1; // ------------- NEW ---------------------------------------------- case Menu::State::ACCUMULATE: stats = h.calculateSeatStatistics(); os << "Avaliable: " << stats.first << std::endl; os << "Reserved: " << stats.second << std::endl; break; case Menu::State::SORT: sorted = h.getSortedClients(); os << "Clients:\n"; for (const auto& i : sorted) os << i << "\n"; os << std::flush; break; case Menu::State::MAX_ELEMENT: cli = h.getHighestSeatReservation(); os << "Seat " << cli.first << " by " << cli.second << std::endl; break; case Menu::State::REVERSE: h.displaySeatsReversed(os); break; case Menu::State::COUNT: name = getName(is, os); cnt = h.countBookingsByClient(name); os << Menu::GREEN << "Count: " << cnt << Menu::RESET << std::endl; break; case Menu::State::REPLACE: name = getName(is, os); os << "Replace with" << std::endl; name_rep = getName(is, os); os << Menu::GREEN << h.replaceClientName(name, name_rep) << " seats replaced" << Menu::RESET << std::endl; break; case Menu::State::COPY: vips = h.getVipClients(); os << "VIP clients:\n"; for (const auto& i : vips) os << Menu::GREEN << i << "\n"; os << Menu::RESET << std::flush; break; // ---------------------------------------------------------------- default: throw Booking::InvalidArgument(); } } catch (Booking::BookingException& e) { os << Menu::RED << e.what() << Menu::RESET << std::endl; is.clear(); is.ignore(std::numeric_limits::max(), '\n'); // return EXIT_FAILURE; } return EXIT_SUCCESS; }