#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <chrono>

// Class representing a shared bathroom used by one party at a time
class Bathroom {
private:
    int inside = 0;                    // Current number of people inside
    const int capacity;               // Max capacity of bathroom
    std::string current_party = "";   // Party currently occupying the bathroom ("Democrat" or "Republican")
    std::mutex mtx;                   // Mutex to protect shared state
    std::condition_variable cv;       // Condition variable for waiting and notifying threads

public:
    // Constructor with max capacity
    Bathroom(int maxCapacity) : capacity(maxCapacity) {}

    // Thread calls this to enter the bathroom
    void enter(int id, const std::string& party) {
        std::unique_lock<std::mutex> lock(mtx);  // Acquire the mutex

        // Wait while the bathroom is either:
        // 1. Occupied by the opposite party
        // 2. Full (reached max capacity)
        cv.wait(lock, [&]() {
            return (current_party == "" || current_party == party) && inside < capacity;
        });

        // If the bathroom was empty, set the current party
        if (current_party == "") {
            current_party = party;
        }

        // Allow the thread to enter
        inside++;
        std::cout << party << " " << id << " entered. Inside: " << inside << "\n";
    }

    // Thread calls this to leave the bathroom
    void leave(int id, const std::string& party) {
        std::unique_lock<std::mutex> lock(mtx);  // Acquire the mutex

        inside--;  // Decrement count as this person is leaving
        std::cout << party << " " << id << " left. Inside: " << inside << "\n";

        // If the last person left, reset the current party
        if (inside == 0) {
            current_party = "";
        }

        // Notify all waiting threads so they can check if they can enter now
        cv.notify_all();
    }
};

// Function representing a person trying to use the bathroom
void person(Bathroom& bathroom, int id, const std::string& party) {
    bathroom.enter(id, party);  // Try to enter
    std::this_thread::sleep_for(std::chrono::milliseconds(200 + rand() % 300));  // Simulate usage time
    bathroom.leave(id, party);  // Exit
}

int main() {
    Bathroom bathroom(3);  // Bathroom can hold up to 3 people
    std::vector<std::thread> threads;

    // Simulate 10 people trying to enter, alternating between Democrat and Republican
    for (int i = 0; i < 10; ++i) {
        std::string party = (i % 2 == 0) ? "Democrat" : "Republican";
        threads.emplace_back(person, std::ref(bathroom), i, party);
    }

    // Wait for all threads to finish
    for (auto& t : threads) t.join();

    return 0;
}