#include <chrono>
#include <functional>
#include <list>
#include <mutex>
#include <thread>
#include <vector>
#include <iostream>

#include "ThreadPool.h"
#include "spdlog/spdlog.h"
#include "RingQueue.hpp"

/**
 * @brief 这是一个时间轮定时器的实现,将任务添加到时间轮中,时间轮会在指定的时间点执行任务
 *        任务是一个函数,可以是lambda表达式,也可以是std::bind绑定的函数
 *        任务是挂在一个个时间点上的,同一个时间点可以有多个任务,时间点的最大数目就是任务容器最大容量
 *        间隔时间是1ms,最大定时时间是1000 * 60 * 60 ms,也就是1小时
 * 
 * 缺点:
 *        1、时间轮的时间点是固定的,如果任务的时间点不在时间轮的时间点上,就会被延迟执行
 *        2、任务执行是在这个线程中执行的,如果任务执行时间过长,会影响时间轮的执行
 */


/** ==================================================================
 *
 * ================================================================== */

/* 定义函数指针 */
using TaskFunc = std::function<void()>;
/* 定义任务结构体 */
struct Task {
    bool is_loop;           /* 是否循环定时 */
    TaskFunc func;          /* 任务函数 */
    long interval;          /* 定时间隔 */
};

class TimerWheel {
public:
    
    /* 构造函数,第一个参数是任务时间点容器最大数量,第二个参数是最低检测时间单位 */
    explicit TimerWheel()
        : m_current_index(0) {
        m_wheel_size = 1000 * 60 * 60;      /* 1小时 */
        m_interval_ms = 1000;               /* 1ms */
        m_firstLevelWheel.resize(1000);
        m_secondLevelWheel.resize(60);
        m_thirdLevelWheel.resize(60);
    }

    ~TimerWheel() {
        Stop();
    }
    /* 开启时间轮 */
    void Start() {
        if (m_running) {
            return;
        }
        m_running = true;
        /* 开启新线程,定时检测队列 */
        m_thread = std::thread([this]() {
            while (m_running) {
                std::this_thread::sleep_for(std::chrono::milliseconds(m_interval_ms));
                Tick();
            }
        std::cout << "timer oooops!" << std::endl;
            });
        m_thread.detach();
    }

    void Stop() {
        if (!m_running) {
            return;
        }
        m_running = false;
        if (m_thread.joinable()) {
            m_thread.join();
        }
    }
    /* 添加任务函数 */
    void AddTask(int timeout_ms, Task task, bool is_loop = false) {
        std::lock_guard<std::mutex> lock(mutex_);
        /* 计算出需要轮训的次数 */
        size_t ticks = timeout_ms / m_interval_ms;
        /* 在当前时间点上加上ticks */
        size_t index = (m_current_index + ticks) % m_wheel_size;
        /* 这里是设置在整个时间轮中,该任务在这个时间轮中的分布,以实现循环定时 */
        size_t allindex = index;
        for (size_t i = 1 ; allindex < m_wheel_size; i++)
        {
            allindex = index * i;
            if (allindex >= m_wheel_size)
                break;
            wheel_[allindex].push_back(task);
        }
        
    }

private:
    /* 时间片 */
    void Tick() {
        std::lock_guard<std::mutex> lock(mutex_);
        /* 取出这个时间点的函数,循环执行 */
        auto& tasks = wheel_[m_current_index];
        for (const auto& task : tasks) {
            task();
        }
        //tasks.clear();
        /* 可以循环定时的关键,超过了设置的最大时间点,就会重置 */
        m_current_index = (m_current_index + 1) % m_wheel_size;
    }

private:
    long m_max_interval = 0;                /* 最大的定时时间长度,等于下面两个之和,超过就报错 */
    size_t m_wheel_size;                    /* 时间轮最大的轮训次数 */
    int m_interval_ms;                      /* 每个时间点的间隔秒数,这里是1ms */
    long m_firstLevelCount;                 /* 第一层级的时间轮的个数 */
    long m_secondLevelCount;                /* 第二层级的时间轮的个数 */
    long m_thirdLevelCount;                 /* 第三层级的时间轮的个数 */
    std::vector<std::list<Task>> m_firstLevelWheel;     /* 第一层级的时间轮,这里是ms等级,总共1000个 */
    std::vector<std::list<Task>> m_secondLevelWheel;    /* 第二层级的时间轮,这里是秒的等级,总共60个 */
    std::vector<std::list<Task>> m_thirdLevelWheel;     /* 第三层级的时间轮,这里是分钟的等级,总共60个 */
    size_t m_current_index;                 /* 现在的时间点 */
    bool m_running = false;                 /* 运行标识位 */
    std::thread m_thread;
    std::mutex mutex_;
    
};