TalTech_cpp/CT4/menu.cpp

236 lines
7.1 KiB
C++

#include <iostream>
#include <limits> // streamsize::max
#include <memory>
#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<static_cast<StateType>(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<Menu::StateType&>(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<std::streamsize>::max(), '\n');
throw Booking::InvalidArgument();
}
return static_cast<Booking::Seat::Type>(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<int, int> getRange(std::istream& is) {
auto isEnd = [](char c) { return c == '\n' || c == EOF; };
try {
std::pair<Booking::seat_num, Booking::seat_num> 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<Booking::Booker*>(ctx);
delete h;
}
bool menu(std::istream& is, std::ostream& os, void* ctx) {
Menu::State state = Menu::State::MENU;
Booking::Booker& h = *reinterpret_cast<Booking::Booker*>(ctx);
Booking::Seat::Type t;
std::string name, name_rep;
//----------------
std::pair<size_t, size_t> stats;
std::vector<std::string> sorted;
std::pair<Booking::seat_num, std::string> cli;
size_t cnt;
std::vector<std::string> 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<std::streamsize>::max(), '\n');
// return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}