#pragma once

#include "forks.hpp"

#include <algorithm>
#include <chrono>
#include <mutex>
#include <print>
#include <random>
#include <thread>

namespace workers {
void philosopher(std::size_t id, th_safe::forks &table,
                 std::size_t iterations = 3) {
  std::mt19937 rng(id + std::random_device{}());
  std::uniform_int_distribution<> think_time(100, 300);
  std::uniform_int_distribution<> eat_time(100, 200);

  for (std::size_t round = 0; round < iterations; ++round) {
    std::print("Philosopher {} is thinking (round {})\n", id, round + 1);
    std::this_thread::sleep_for(std::chrono::milliseconds(think_time(rng)));

    std::mutex &left_fork = table.left(id);
    std::mutex &right_fork = table.right(id);

    if (std::addressof(left_fork) < std::addressof(right_fork)) {
      std::scoped_lock lock(left_fork, right_fork);
      std::print("Philosopher {} is eating (round {})\n", id, round + 1);

      std::this_thread::sleep_for(std::chrono::milliseconds(eat_time(rng)));
      std::print("Philosopher {} finished eating (round {})\n", id, round + 1);
    } else {
      std::scoped_lock lock(right_fork, left_fork);
      std::print("Philosopher {} is eating (round {})\n", id, round + 1);

      std::this_thread::sleep_for(std::chrono::milliseconds(eat_time(rng)));
      std::print("Philosopher {} finished eating (round {})\n", id, round + 1);
    }
  }

  std::print("Philosopher {} leaves the table.\n", id);
}
} // namespace workers
