#ifndef __MATCHER_HPP__
#define __MATCHER_HPP__
#include "db.hpp"
#include "room.hpp"
#include "util.hpp"
#include "online.hpp"
#include <list>
#include <thread>
#include <mutex>
#include <condition_variable>

/*用户对战匹配管理模块 -- 将用户按分数分为青铜、黄金、王者三档，并分别为它们设计一个匹配队列，队列元素>=2则匹配成功，否则阻塞*/
/*匹配队列类*/
template <class T>
class match_queue {
public:
    match_queue() {}
    ~match_queue() {}
    /*目标元素入队列，并唤醒线程*/
    void push(const T& data) {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        LOG(DEBUG, "%d用户加入匹配队列", data);
        // 匹配队列每新增一个元素，就唤醒对应的匹配线程，判断是否满足匹配要求(队列人数>=2)
        _cond.notify_all();
    }

    /*队头元素出队列并返回队头元素*/
    bool pop(T& data) {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty()) return false;
        data = _list.front();
        _list.pop_front();
        LOG(DEBUG, "%d用户从匹配队列中移除", data);
        return true;
    }

    /*移除队列中的目标元素*/
    void remove(const T& data) {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
        LOG(DEBUG, "%d用户从匹配队列中移除", data);
    }
    
    /*阻塞线程*/
    void wait() {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }

    /*获取队列元素个数*/
    int size() { 
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size(); 
    }

    /*判断队列是否为空*/
    bool empty() {
        std::unique_lock<std::mutex> lock(_mutex); 
        return _list.empty();
    }
private:
    std::list<T> _list;  // 使用双向链表而不是queue充当匹配队列，便于用户取消匹配时将该用户从匹配队列中移除
    std::mutex _mutex;   // 实现线程安全
    std::condition_variable _cond;  // 条件变量，当向队列中push元素时唤醒，用于阻塞消费者
};

/*匹配管理类*/
class matcher {
private:
    void handler_match(match_queue<uint64_t> &mq) {
        while(true) {
            // 检查匹配条件是否满足(人数>=2)，不满足则继续阻塞
            while(mq.size() < 2) mq.wait();
            // 条件满足，从队列中取出两个玩家
            uint64_t uid1, uid2;
            if(mq.pop(uid1) == false) continue;
            if(mq.pop(uid2) == false) {
                // 如果第二个玩家出队列失败，则需要将第一个玩家重新添加到队列中
                this->add(uid1);
                continue;
            }
            // 检查两个玩家是否都处于大厅在线状态，若一方掉线，则需要将另一方重新添加到队列
            wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
            wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
            if(conn1.get() == nullptr) {
                this->add(uid2);
                continue;
            }
            if(conn2.get() == nullptr) {
                this->add(uid1);
                continue;
            }
            // 为两个玩家创建房间，失败则重新添加到队列
            room_ptr rp = _rm->create_room(uid1, uid2);
            if(rp.get() == nullptr) {
                this->add(uid1);
                this->add(uid2);
                continue;
            }
            // 给玩家返回匹配成功的响应
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            json_util::serialize(resp, body);
            conn1->send(body);
            conn2->send(body);
        }
    }

    /*三个匹配队列的线程入口*/
    void th_low_entry() { handler_match(_q_low); }
    void th_mid_entry() { handler_match(_q_mid); }
    void th_high_entry() { handler_match(_q_high); }
public:
    matcher(user_table *ut, online_manager *om, room_manager *rm)
    : _ut(ut), _om(om), _rm(rm), 
    _th_low(std::thread(&matcher::th_low_entry, this)),
    _th_mid(std::thread(&matcher::th_mid_entry, this)),
    _th_high(std::thread(&matcher::th_high_entry, this)) {
        LOG(DEBUG, "游戏对战匹配管理模块初始化完毕");
    }

    ~matcher() {
        LOG(DEBUG, "游戏对战匹配管理模块已被销毁");
    }

    /*添加用户到匹配队列*/
    bool add(uint64_t uid) {
        // 根据用户id获取用户数据库信息
        Json::Value user;
        if(_ut->select_by_id(uid, user) == false) {
            LOG(DEBUG, "查找玩家%d信息失败", uid);
            return false;
        }
        // 根据用户分数将用户添加到对应的匹配队列中去
        int score = user["score"].asInt();
        if(score < 2000) _q_low.push(uid);
        else if(score >= 2000 && score < 3000) _q_mid.push(uid);
        else _q_high.push(uid);
        return true;
    }

    /*将用户从匹配队列中移除*/
    bool remove(uint64_t uid) {
        // 根据用户id获取用户数据库信息
        Json::Value user;
        if(_ut->select_by_id(uid, user) == false) {
            LOG(DEBUG, "查找用户%d信息失败", uid);
            return false;
        }
        // 根据用户分数将用户从对应的匹配队列中移除
        int score = user["score"].asInt();
        if(score < 2000) _q_low.remove(uid);
        else if(score >= 2000 && score < 3000) _q_mid.remove(uid);
        else _q_high.remove(uid);
        return true;        
    }
private:
    // 三个匹配队列(青铜/黄金/王者 -> low/mid/high)
    match_queue<uint64_t> _q_low;
    match_queue<uint64_t> _q_mid;
    match_queue<uint64_t> _q_high;
    // 三个管理匹配队列的线程
    std::thread _th_low;
    std::thread _th_mid;
    std::thread _th_high;
    room_manager *_rm;    // 游戏房间管理句柄
    online_manager *_om;  // 在线用户管理句柄
    user_table *_ut;      // 用户数据管理句柄
};
#endif