#include "module_call.h"

#include <boost/bind.hpp>
#include <vector>
#include <set>
#include <string>
#include <chrono>
#include<algorithm>

#include <area.h>
#include <site_area.h>
#include"mine.h"


void module_call::run()
{
    std::vector<call_card_ptr> arr;

    get_all_call_cards(arr);

    if(arr.empty())
    {
        return ;
    }
    //转发给web
    swsClientMgr.send(JSON_CMD_VALUE_PUSH, to_call_card_list_json(arr));

    //将呼叫命令发送给标识卡终端
    call_site_map site_map;
    get_site_map(arr, site_map);

    if(!site_map.empty())
    {
        send_to_sites(site_map);
    }
}

void module_call::rev_from_card_resp(std::shared_ptr<card_location_base> card_ptr)
{
    card_ptr->get_mine_tool()->m_status_call = CALL_SUCCESSED;
}

/*
web发给采集：发起呼叫
{
    cmd: 'call_card_req',
    data: {
      call_type_id: this.cal_type, // 全员呼叫:0 定员呼叫:1
      call_time_out: this.choosedTime, // 呼叫时长
      call_level_id: this.call_type_id, // 呼叫类型 一般呼叫:1 紧急呼叫:2
      user_name: xdata.userName, // 呼叫人
      call_time: time, // 呼叫时间戳
      stations: [{ stationid: 0 }], // 分站 0为全员
      cards: cards // 人员 0为全员   //旧代码为map格式 [{ stationid: 0 }]
    }
}
*/
void module_call::accept_call(sio::message::ptr const& data)
{
    int call_type = -1, call_level = -1, call_time_interval=-1;
    uint32_t call_time_out = 0;
    std::string user_name;

    if(!tool_map::try_get_value(call_type, JSON_KEY_CALL_CARD_CALL_TYPE, data)
            || !tool_map::try_get_value(call_level, JSON_KEY_CALL_CARD_CALL_LEVEL, data)
            || !tool_map::try_get_value(call_time_out, JSON_KEY_CALL_CARD_CALL_TIME_OUT, data)
            || !tool_map::try_get_value(user_name, JSON_KEY_CALL_CARD_USER_NAME, data)
            || !tool_map::try_get_value(call_time_interval, JSON_KEY_CALL_CARD_CALL_TIME, data)
            )
    {
        log_error("发起呼叫: 收到的json不对，解析不出call_type = -1, call_level = -1, \
                  call_time_out=-1, call_time_interval=-1，user_name");
                return;
    }

    if(user_name.empty())
    {
        log_error("发起呼叫: user_name用户名为空");
        return;
    }

    std::vector<call_card_ptr> result_arr;//结果集

    call_user_ptr user_ptr;
    if(CCT_CALL_ALL==call_type)// 全员
    {
        user_ptr=call_user_ptr(new call_user());

        user_ptr->call_time=std::chrono::system_clock::now();
        user_ptr->call_time_out=call_time_out;
        user_ptr->call_time_interval = call_time_interval;
        user_ptr->call_type_id=call_type;
        user_ptr->call_level_id=call_level;
        user_ptr->user_name=user_name;

        call_card_ptr temp(new call_card());
        copy(user_ptr, temp);
        temp->cardid = CCT_CALL_ALL; //全员卡
        temp->call_state = CALL_ING;

        result_arr.push_back(temp);//增加到结果集中

        //添加全员卡
        user_ptr->cards.insert(std::make_pair(temp->cardid, temp));

        //更新用户信息
        std::lock_guard<std::mutex> lock(_mutex);
        _map[user_name]=user_ptr;
    }
    else// 定员: 如果有全员，定员不生效
    {
        std::lock_guard<std::mutex> lock(_mutex);

        auto user_map_ptr = _map.find(user_name);
        if(_map.end() == user_map_ptr)//没有这个用户就新建并加入
        {
            user_ptr=call_user_ptr(new call_user());
            _map[user_name]=user_ptr;
        }
        else//有这个用户
        {
            user_ptr=user_map_ptr->second;
            // 如果有全员，定员不生效
            auto card_temp = user_ptr->cards.find(CCT_CALL_ALL);
            if(user_ptr->cards.end() != card_temp)
            {
                return;
            }
        }

        //更新用户信息
        user_ptr->call_time=std::chrono::system_clock::now();
        user_ptr->call_time_out=call_time_out;
        user_ptr->call_time_interval = call_time_interval;
        user_ptr->call_type_id=call_type;
        user_ptr->call_level_id=call_level;
        user_ptr->user_name=user_name;

        std::vector<sio::message::ptr> card_vec;
        if(!tool_map::try_get_value(card_vec, JSON_KEY_CALL_CARD_CARDS, data) || card_vec.size() == 0)
        {
            log_error("发起呼叫，web发来的数据JSON_KEY_CALL_CARD_CARDS字段为空 或者不是数组");
            return;
        }

        add_cards_to_user(card_vec, user_ptr);

        for(auto& it:user_ptr->cards)//添加到结果集中
        {
            result_arr.push_back(it.second);
        }
    }

    //组装json发送给web
    if(!result_arr.empty())
    {
        response_accept_call(result_arr);
    }
}

//采集回复web发起呼叫
//{
//	'cmd': 'call_card_resp',
//	'data': [
//		["fjb", // 发起呼叫人
//		"0010000000001", // 呼叫的卡号
//		103, // 分站号
//		23431, // 发起呼叫时间
//		0, //
//		0 // 正在呼叫：2/结束呼叫/
//		]
//	]
//}
void module_call::response_accept_call(std::vector<call_card_ptr> cards)
{
    rapidjson::Document doc(rapidjson::kObjectType);
    auto& allocator = doc.GetAllocator();
    rapidjson::Value node_cards(rapidjson::kArrayType);

    //组装json发送给web
    to_node_array(node_cards, cards, allocator);

    doc.AddMember(JSON_ROOT_KEY_CMD,JSON_CMD_VALUE_CALL_CARD_RESPONSE, allocator);
    doc.AddMember(JSON_ROOT_KEY_VERSION, INTERFACE_VERSION, allocator);
    doc.AddMember(JSON_ROOT_KEY_DATA, node_cards, allocator);

    swsClientMgr.send(JSON_CMD_VALUE_PUSH, tool_json::doc_to_json(doc));
}


//web发给采集：取消呼叫
//{
//	cmd: 'call_card_cancel_req',
//	data: {
//	  call_type_id: type, // 全员/定员
//	  user_name: xdata.userName, // 取消人
//	  call_time: callTime, // 时间戳
//	  stations: this.stationid, // 分站
//	  cards: this.cards // 取消呼叫卡  //旧代码为map格式 [{ stationid: 0 }]
//	}
//}
void module_call::accept_cancel(sio::message::ptr const& node_data)
{
    int call_type = -1;//, call_level = -1;
    std::string user_name;
    int64_t call_time;

    if(!tool_map::try_get_value(call_type, JSON_KEY_CALL_CARD_CALL_TYPE, node_data)
            || !tool_map::try_get_value(call_time, JSON_KEY_CALL_CARD_CALL_TIME, node_data)
            || !tool_map::try_get_value(user_name, JSON_KEY_CALL_CARD_USER_NAME, node_data)
            )
    {
        log_error("取消呼叫: 收到的json不对，解析不出int call_type = -1, call_level = -1，user_name");
        return;
    }

    if(user_name.empty())
    {
        log_error("取消呼叫: user_name用户名为空");
        return;
    }

    call_user_ptr result_user_ptr(new call_user());
    result_user_ptr->call_type_id=call_type;
    //    result_user_ptr->call_level_id=call_level;
    result_user_ptr->call_time = std::chrono::system_clock::time_point(std::chrono::milliseconds(call_time));
    result_user_ptr->user_name=user_name;

    //取消呼叫有两种：
    //取消全员呼叫
    //对指定卡取消呼叫
    if(CCT_CALL_ALL==call_type)// 取消全员呼叫
    {
        //删除用户
        std::lock_guard<std::mutex> lock(_mutex);
        _map.erase(user_name);
    }
    else// 取消定员呼叫
    {
        std::vector<sio::message::ptr> card_vec;
        if(!tool_map::try_get_value(card_vec, JSON_KEY_CALL_CARD_CARDS, node_data) || card_vec.size() == 0)
        {
            log_error("取消呼叫: web发来的数据cards字段为空 或者不是数组");
            return;
        }

        std::lock_guard<std::mutex> lock(_mutex);

        call_user_ptr user_ptr(new call_user());
        auto it_map = _map.find(user_name);
        if(_map.end()!=it_map)//呼叫用户
        {
            user_ptr=it_map->second;
        }

        std::vector<sio::message::ptr>::const_iterator it_card = card_vec.begin();
        std::string s_card_id;
        for(; it_card != card_vec.end(); ++it_card)
        {
            if(!tool_map::try_get_value(s_card_id, JSON_KEY_CALL_CARD_CARD_ID, (*it_card)))
            {
                log_error("取消呼叫: web发来的数据 card_id 格式不对");
                continue;
            }

            uint32_t id = tool_other::id64_to_id(s_card_id);
            int type= tool_other::id64_to_type(s_card_id);

            log_info("取消呼叫: cardid=%d, cardtype=%d", id, type);

            call_card_ptr card_ptr(new call_card());
            card_ptr->cardid = id;
            card_ptr->cardtype = type;

            result_user_ptr->cards[card_ptr->cardid]=card_ptr;//增加到结果集中

            user_ptr->cards.erase(card_ptr->cardid);//删除这个卡
        }

        // 没有呼叫信息，删除该用户记录
        if(user_ptr->cards.empty())
        {
            _map.erase(user_name);
        }
    }

    //如果要取消呼叫的用户下没有卡，建一张CCT_CALL_ALL卡返回
    if(result_user_ptr->cards.empty())
    {
        //        result_user_ptr->call_level_id=call_level;
        //result_user_ptr->call_time=std::chrono::system_clock::now();
        result_user_ptr->call_time_out=0;
        result_user_ptr->call_type_id=call_type;
        result_user_ptr->user_name=user_name;

        call_card_ptr card_ptr(new call_card());
        card_ptr->cardid=CCT_CALL_ALL;

        result_user_ptr->cards[card_ptr->cardid]=card_ptr;
    }

    //转发给web
    response_accept_cancel(result_user_ptr);
}

/*
采集回复web：取消呼叫
{
    "user_name":"fjb", // 取消人
    "call_time": 23432, // 发起呼叫时间
    "stations":[{"stationid":102}], // 分站号
    "cards":[{"cardid":"0","cardtype":"1"}] // 取消的卡
}
*/
void module_call::response_accept_cancel(const call_user_ptr user_ptr)
{
    rapidjson::Document doc(rapidjson::kObjectType);
    auto& allocator = doc.GetAllocator();
    rapidjson::Value node_data(rapidjson::kObjectType);

    tool_json::add_member(node_data, JSON_KEY_CALL_CARD_USER_NAME, user_ptr->user_name, allocator);

    node_data.AddMember(JSON_KEY_CALL_CARD_CALL_TIME, tool_time::to_ms(user_ptr->call_time), allocator);
    node_data.AddMember(JSON_KEY_CALL_CARD_CALL_TYPE, user_ptr->call_type_id, allocator);

    rapidjson::Value node_cards(rapidjson::kArrayType);

    //加入取消呼叫的卡
    auto iter = user_ptr->cards.begin();
    for(;iter != user_ptr->cards.end();++iter)
    {
        rapidjson::Value node_card(rapidjson::kObjectType);

        tool_json::add_member(node_card, JSON_KEY_CALL_CARD_CARD_ID, iter->second->to_id64_str(), allocator);
        node_card.AddMember(JSON_KEY_CALL_CARD_CARD_TYPE_ID,iter->second->cardtype, allocator);

        node_cards.PushBack(node_card,allocator);
    }

    node_data.AddMember(JSON_KEY_CALL_CARD_CARDS, node_cards, allocator);

    doc.AddMember(JSON_ROOT_KEY_CMD, JSON_CMD_VALUE_CALL_CARD_CANCEL_RESPONSE, allocator);
    doc.AddMember(JSON_ROOT_KEY_VERSION, INTERFACE_VERSION, allocator);
    doc.AddMember(JSON_ROOT_KEY_DATA, node_data, allocator);

    //转发给web
    swsClientMgr.send(JSON_CMD_VALUE_PUSH, tool_json::doc_to_json(doc));
}

/*
登陆时，采集发送web：井下所有呼叫
{"cmd":"callcardlist","data":[
    ["fjb", // 发起呼叫人
    "0010000000001", // 呼叫的卡号
    103, // 分站号
    23431, // 发起呼叫时间
    0, //
    0 // 正在呼叫：2/结束呼叫/
    ]
]}
*/
std::string module_call::accept_login()
{
    std::vector<call_card_ptr> arr;

    get_all_call_cards(arr);

    if(arr.empty())
    {
        return "";
    }

    return to_call_card_list_json(arr);
}

std::string module_call::to_call_card_list_json(std::vector<call_card_ptr> arr)
{
    rapidjson::Document doc(rapidjson::kObjectType);
    auto& allocator = doc.GetAllocator();
    rapidjson::Value node_cards(rapidjson::kArrayType);

    to_node_array(node_cards, arr, allocator);

    doc.AddMember(JSON_ROOT_KEY_CMD, JSON_CMD_VALUE_CALL_CARD_LIST, allocator);
    doc.AddMember(JSON_ROOT_KEY_VERSION, INTERFACE_VERSION, allocator);
    doc.AddMember(JSON_ROOT_KEY_DATA, node_cards, allocator);

    return tool_json::doc_to_json(doc);
}

void module_call::add_cards_to_user(const std::vector<sio::message::ptr>& card_vec, call_user_ptr user_ptr)
{
    std::vector<sio::message::ptr>::const_iterator it_card = card_vec.begin();
    for(; it_card != card_vec.end(); ++it_card)
    {
        std::string s_card_id;
        if(!tool_map::try_get_value(s_card_id, JSON_KEY_CALL_CARD_CARD_ID, (*it_card))
                ||s_card_id.empty())
        {
            log_error("发起呼叫，web发来的数据 card_id 格式不对 或为空");
            continue;
        }

        uint32_t id = tool_other::id64_to_id(s_card_id);
        int type= tool_other::id64_to_type(s_card_id);

        log_info("发起呼叫 cardid=%d, cardtype=%d", id, type);

        call_card_ptr card_ptr(new call_card());
        card_ptr->cardid = id;
        card_ptr->cardtype = type;
        copy(user_ptr, card_ptr);

        card_ptr->call_state = CALL_ING;

        user_ptr->cards[card_ptr->cardid]=card_ptr;
    }
}

void module_call::get_user_all_call_cards(call_user_ptr& user_ptr,
                                          const std::unordered_map<uint64_t,std::shared_ptr<card_location_base>>& cardlist,
                                          std::vector<call_card_ptr>& out_data)
{
    auto& cards=user_ptr->cards;
    int status = CALL_ING;

    //如果是全员呼叫，增加所有卡
    auto card_it = cards.find(CCT_CALL_ALL);
    if(cards.end()!=card_it)
    {
        if(user_ptr->is_timeout())//呼叫超时, 清空全员卡, 并设置呼叫状态为结束
        {
            log_info("全员呼叫发送线程：呼叫用户超时，用户名=%s", user_ptr->user_name.c_str());

            cards.clear();
            status=CALL_END;
        }

        auto g_it=cardlist.begin();
        for(;g_it!=cardlist.end();++g_it)//如果是全员呼叫，增加所有卡
        {
            auto site_ptr = g_it->second->get_site_area();
            if(!site_ptr || site_ptr->is_invalid())
            {
                continue;
            }

            call_card_ptr card_ptr(new call_card());
            copy(user_ptr, card_ptr);
            card_ptr->cardid = g_it->second->m_id;
            card_ptr->cardtype = g_it->second->m_type;

            card_ptr->stationid=site_ptr->site_id();

            card_ptr->call_state=status;
            if(CALL_SUCCESSED == g_it->second->get_mine_tool()->m_status_call)
            {
                card_ptr->call_state = CALL_SUCCESSED;
            }
            else
            {
                g_it->second->get_mine_tool()->m_status_call = CALL_ING;
            }

            if(card_ptr->is_timeout())
            {
                g_it->second->get_mine_tool()->m_status_call = 0;
            }
            card_ptr->is_display = g_it->second->m_display;

            out_data.push_back(card_ptr);
        }

        return;
    }

    //定员呼叫
    auto iter_card = cards.begin();
    for(;iter_card!=cards.end();++iter_card)
    {
        //增加到结果集中
        out_data.push_back(iter_card->second);

        auto g_card = cardlist.find(iter_card->second->to_id64());
        if(cardlist.end()==g_card)//在全局卡列表中没有这张卡，
        {
            log_error("定员呼叫发送线程：全局卡列表中没有这张卡，卡id=%d，卡类型=%d",
                      iter_card->second->cardid, iter_card->second->cardtype);

            iter_card->second->call_state = CALL_FAILED;
            cards.erase(iter_card--); //在用户下删除这张卡

            continue;
        }

        auto site_ptr = g_card->second->get_site_area();
        if(!site_ptr || site_ptr->is_invalid())
        {
            log_error("定员呼叫发送线程：全局卡列表中这张卡已经上井或无效，卡id=%d，卡类型=%d",
                      iter_card->second->cardid, iter_card->second->cardtype);

            iter_card->second->call_state = CALL_FAILED;
            cards.erase(iter_card--); //在用户下删除这张卡

            continue;
        }

        //更新卡的分站id
        iter_card->second->stationid = site_ptr->site_id();

        if(CALL_SUCCESSED == g_card->second->get_mine_tool()->m_status_call)
        {
            iter_card->second->call_state = CALL_SUCCESSED;
        }
        else
        {
            g_card->second->get_mine_tool()->m_status_call = CALL_ING;
        }

        if(iter_card->second->is_timeout())//呼叫超时
        {
            log_info("定员呼叫发送线程：呼叫卡超时，用户名=%s，卡id=%d，卡类型=%d",
                     iter_card->second->user_name.c_str(),
                     iter_card->second->cardid, iter_card->second->cardtype);

            iter_card->second->call_state = CALL_END;
            if(CALL_SUCCESSED == g_card->second->get_mine_tool()->m_status_call)
            {
                iter_card->second->call_state = CALL_SUCCESSED;
                g_card->second->get_mine_tool()->m_status_call = 0;
            }
            cards.erase(iter_card--);//在用户下删除这张卡
        }
    }
}


/**
* @brief 系统自动发送呼叫给指定的卡
*/
void module_call::system_call_apoint(int card_id,int card_type)
{
    int call_type = -1, call_level = -1, call_time_interval=-1;
    uint32_t call_time_out = 0;
    std::string user_name = "system_apoint";

    std::vector<call_card_ptr> result_arr;//结果集

    call_user_ptr user_ptr;
    std::lock_guard<std::mutex> lock(_mutex);
    auto user_map_ptr = _map.find(user_name);
    if(_map.end() == user_map_ptr)//没有这个用户就新建并加入
    {
        user_ptr=call_user_ptr(new call_user());
        _map[user_name]=user_ptr;
    }
    else//有这个用户
    {
        user_ptr=user_map_ptr->second;
    }

    //更新用户信息
    user_ptr->call_time=std::chrono::system_clock::now();
    user_ptr->call_time_out=call_time_out;
    user_ptr->call_time_interval = call_time_interval;
    user_ptr->call_type_id=call_type;
    user_ptr->call_level_id=call_level;
    user_ptr->user_name=user_name;

    call_card_ptr c_card_ptr(new call_card());
    c_card_ptr->cardid = card_id;
    c_card_ptr->cardtype = card_type;
    copy(user_ptr, c_card_ptr);
    c_card_ptr->call_state = CALL_ING;

    user_ptr->cards[c_card_ptr->cardid]=c_card_ptr;

    for(auto& it:user_ptr->cards)//添加到结果集中
    {
        result_arr.push_back(it.second);
    }
    log_info("发起呼叫 cardid=%d, cardtype=%d", card_id,card_type);

    //组装json发送给web
    response_accept_call(result_arr);
}
/**
 * @brief 系统自动发送呼叫给指定的卡
 */
void module_call::system_cancel_call_apoint(int card_id,int card_type)
{
    int call_type = -1;//, call_level = -1;
    std::string user_name = "system_apoint";
    int64_t call_time;

    call_user_ptr result_user_ptr(new call_user());
    result_user_ptr->call_type_id=call_type;
    //    result_user_ptr->call_level_id=call_level;
    result_user_ptr->call_time = std::chrono::system_clock::time_point(std::chrono::milliseconds(call_time));
    result_user_ptr->user_name=user_name;

    std::lock_guard<std::mutex> lock(_mutex);

    call_user_ptr user_ptr(new call_user());
    auto it_map = _map.find(user_name);
    if(_map.end()!=it_map)//呼叫用户
    {
        user_ptr=it_map->second;
    }
    if (user_ptr->cards.find(card_id) == user_ptr->cards.end())
    {
        return ;
    }
    call_card_ptr c_card_ptr(new call_card());
    c_card_ptr->cardid = card_id;
    c_card_ptr->cardtype = card_type;

    result_user_ptr->cards[c_card_ptr->cardid]=c_card_ptr;//增加到结果集中

    user_ptr->cards.erase(c_card_ptr->cardid);//删除这个卡

    log_info("取消呼叫: cardid=%d, cardtype=%d", card_id,card_type);
    // 没有呼叫信息，删除该用户记录
    if(user_ptr->cards.empty())
    {
        _map.erase(user_name);
    }

    response_accept_cancel(result_user_ptr);
}
