236 lines
7.1 KiB
C++
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;
|
|
}
|