#pragma once

#include <cstddef>
#include <functional>

namespace fn {

// Pool de threads opaco. Crea N workers en el constructor (default = hw
// concurrency); el destructor los joinea. Re-utilizable across multiples
// parallel_for; cada llamada bloquea hasta que todos los hilos terminan
// el rango.
class ThreadPool {
public:
    explicit ThreadPool(int n_threads = 0);   // 0 = std::thread::hardware_concurrency()
    ~ThreadPool();

    ThreadPool(const ThreadPool&) = delete;
    ThreadPool& operator=(const ThreadPool&) = delete;

    int n_threads() const;

    // Ejecuta fn(i) para i en [begin, end), repartido entre los workers
    // en chunks contiguos. Bloquea hasta finalizar.
    //
    // Si una excepcion se lanza en algun worker, parallel_for la captura
    // y la relanza en el thread caller tras el join (la primera).
    void parallel_for(std::size_t begin, std::size_t end,
                      const std::function<void(std::size_t)>& fn);

    // Ejecuta fn(thread_id, begin, end) por chunk — util cuando cada thread
    // necesita acumular state local. fn recibe los limites del chunk.
    void parallel_for_chunks(std::size_t begin, std::size_t end,
                             const std::function<void(int /*tid*/,
                                                      std::size_t /*lo*/,
                                                      std::size_t /*hi*/)>& fn);

private:
    struct Impl;
    Impl* impl_;
};

} // namespace fn
