#ifndef MODULE_CONST_H
#define MODULE_CONST_H

/**
 * @brief 包含常用的常量，接口基类，工具类
 * @author 戴月腾
 * @date 2018-08-24
 */

#include<map>
#include<vector>
#include<string>
#include<chrono>
#include"db_api/CDBConnPool.h"
#include"log.h"
#include<rapidjson/document.h>
#include<rapidjson/prettywriter.h>
#include<rapidjson/stringbuffer.h>
#include<websocket/wsClientMgr.h>
#include<thread>

#include"card.h"
#include"area.h"
#include"constdef.h"

#define LENGTH_SQL 2000

///全局常量
class global_constant
{
public:
//    ///区域超速最小次数门限
//    static int AREA_SPEED_COUNT_MIN;
    ///区域超速最大次数门限
    static int AREA_SPEED_COUNT_LIMIT;
//    ///区域超速最小时间门限
//    static int AREA_SPEED_TIME_MIN;
//    ///区域超速最大时间门限
//    static int AREA_SPEED_TIME_MAX;

//    ///车辆考勤最小次数门限
//    static int ATTENDANCE_VEHICLE_COUNT_MIN;
    ///车辆考勤最大次数门限
    static int ATTENDANCE_VEHICLE_COUNT_LIMIT;
//    ///车辆考勤最小时间门限
//    static int ATTENDANCE_VEHICLE_TIME_MIN;
//    ///车辆考勤最大时间门限
//    static int ATTENDANCE_VEHICLE_TIME_MAX;
};

/**
 * @brief 事件状态
 */
enum EVENT_STATUS
{
    ///事件开始
    ES_START = 0,
    ///呼救已处理状态
    ES_DEAL_HELP = 1,
    ///事件结束
    ES_END = 100
};

//enum STATUS_CARD
//{
//    STATUS_NORMAL = 0,				//正常
//    STATUS_ERROR = 1,
//    STATUS_ERROR_SERIOUS = 2,

//    STATUS_POWER_LOWER = 1,			//电量低
//    STATUS_POWER_LOWER_SERIOUS = 2, //电量极低
//    STATUS_OVER_TIME = 4,			//超时
//    //STATUS_OVER_COUNT = 1,
//    STATUS_OVER_SPEED = 8,			//超速
//    STATUS_AREA_OVER_TIME = 16,		//区域超时
//    //STATUS_AREA_OVER_COUNT = 1,
//    STATUS_AREA_OVER_SPEED = 32,	//区域超速
//    STATUS_AREA_FORBIDDEN = 64,		//进入限制区域
//    ////STATUS_HELP = 128,				//呼救
//    STATUS_HELP_DEALED = 256,		//呼救已处理
//    STATUS_CALL = 512,				//呼叫
//    STATUS_LOST = 1024				//进入盲区
//    //STATUS_ERROR_DEALED,
//};


enum EVENT_TYPE{ // 事件类型
    ET_UNKNOWN = 0,
    ET_OVER_COUNT_PERSON = 1, // 井下人员超员mp_card_list_over_count_person
    ET_OVER_COUNT_VEHICLE = 2,    // 井下车辆超员mp_card_list_over_count_person

    ET_AREA_OVER_COUNT_PERSON = 3,	// 区域人员超员
    ET_AREA_OVER_COUNT_VEHICLE = 4,	// 区域车辆超员

    ET_SECTION_OVER_COUNT = 5, // 路段拥堵

    ET_READER_ERROR = 6, // 分站通信异常
    ET_ctrl_reader_ERROR = 7, // 控制分站异常
    ET_LIGHT_ERROR = 8, // 交通灯异常
    ET_READER_CALL = 9, // 分站呼叫标识卡
    ET_READER_CALL_CANCEL = 10, // 取消呼叫分站

    ET_CARD_LOW_POWER = 11, // 电量低
    ET_CARD_LOW_POWER_SERIOUS = 12, // 电量极低

    ET_CARD_OVER_TIME_PERSON = 13, // 人员井下超时mp_card_list_over_time_person
    ET_CARD_OVER_TIME_VEHICLE = 14, // 车辆井下超时mp_card_list_over_time_vehicle
    ET_CARD_AREA_OVER_TIME_PERSON = 15, // 人员区域超时
    ET_CARD_AREA_OVER_TIME_VEHICLE = 16, // 车辆区域超时
    ET_CARD_AREA_LIMIT_PERSON = 17, // 人员进入限制区域
    ET_CARD_AREA_LIMIT_VEHICLE = 18, // 车辆进入限制区域
    ET_CARD_AREA_FORBIDDEN_PERSON = 19,  // 人员进入禁止区域
    ET_CARD_AREA_FORBIDDEN_VEHICLE = 20, // 车辆进入禁止区域
    ET_CARD_OVER_SPEED = 21, // 车辆超速
    ET_CARD_AREA_OVER_SPEED = 22, // 车辆区域超速
    ET_CARD_RUN_THE_RED_LIGHT = 23, // 车辆闯红灯
    ET_CARD_HELP = 24, // 人员呼救
    ET_CARD_CALLED = 25, // 人员已被呼叫
    ET_CARD_PATROL_ERROR = 26, // 人员巡检异常
    ET_CARD_LOST = 27, // 标识卡信号丢失
    ET_CARD_DRIVINGFACE_WARNING_AREA = 28,  // 掘进面靠近预警区域告警
    ET_CARD_NEAR_DRIVINGFACE_VEHICLE=29,// 人员靠近掘进机告警
    ET_CARD_NEAR_DRIVINGFACE_OVERCOUNT=30,//掘进机附近人员超员
    ET_UWB_MORE_CARD = 31,
    ET_CARD_MOTIONLESS=32,
    CARD_EVENT_COUNT_MAX,
};

/**
 * @brief 对象类型
 */
enum OBJECT_TYPE
{
    ///矿井
    OT_MINE = 1,
    ///区域
    OT_AREA = 2,
    ///路段
    OT_SECTION = 3,
    ///分站
    OT_DEVICE_READER = 4,
    ///交通灯
    OT_DEVICE_LIGHT = 5,
    ///控制分站
    OT_DEVICE_ctrl_reader = 6,
    ///led屏
    OT_DEVICE_LED = 7,
    ///防爆音箱
    OT_DEVICE_SPEAKER = 8,
    ///标识卡，包括人员、车辆、自组网等
    OT_CARD = 9,
    ///
    OT_DRIVINGFACE_AREA=10,
    ///
    OT_UWB_MORE_CARD=11,
};

/**
 * @brief 告警事件
 */
class ya_event
{
private:
    uint64_t m_ev_id;
public:
    ya_event(uint64_t e_id):m_cur_time(std::chrono::system_clock::now())
    {
        m_ev_id = e_id;

        m_obj_id = 0;
        m_map_id = 0;
        m_area_id = 0;
        x = 0;
        y = 0;
        m_limit_value = 0;
        m_cur_value = 0;
        m_desc = "";
        m_landmarkid = 0;
        m_landmarkdirect = 0;
        m_landmarkdist = 0;

        m_status=ES_START;
    }
    ~ya_event(){}
public:
    ///告警状态，开始、结束
    EVENT_STATUS m_status;

    ///告警类型
    EVENT_TYPE m_ev_type;
    ///告警对象类型
    OBJECT_TYPE m_obj_type;
    /// 告警对象编号，与告警对象类型对应，如告警对象类型为分站，此字段为分站编号
    uint64_t m_obj_id;
    ///当前时间，为告警事件的触发时间，如果状态为开始，则表示开始时间，否则为结束时间
    std::chrono::system_clock::time_point m_cur_time;

    ///告警所在地图
    int m_map_id;
    ///告警所在区域
    int m_area_id;
    ///位置
    double x;
    ///位置
    double y;
    ///告警阈值
    double m_limit_value;
    ///当前值
    double m_cur_value;
    ///描述
    std::string m_desc;

    /// 地标信息
    int m_landmarkid;
    /// 与地标的距离
    double m_landmarkdist;
    /// 所处地标的方向
    int m_landmarkdirect;

    ///作为事件map列表的id，方便查找；(obj_type<<32| obj_id)
    uint64_t m_list_id;

    uint64_t get_id(){return m_ev_id;}
};
typedef std::shared_ptr<ya_event> event_ptr;
typedef std::map<uint64_t, std::shared_ptr<ya_event>> event_map;
typedef std::shared_ptr<event_map> event_map_ptr;

class tool_time
{
public:
    static uint32_t elapse_seconds(std::chrono::system_clock::time_point &start)
    {
        return std::chrono::duration_cast<std::chrono::seconds>
                     (std::chrono::system_clock::now() - start).count();
    }

    static uint64_t elapse_ms(std::chrono::system_clock::time_point &start)
    {
        return std::chrono::duration_cast<std::chrono::milliseconds>
                     (std::chrono::system_clock::now() - start).count();
    }

    static uint32_t now_to_seconds()
    {
        return std::chrono::duration_cast<std::chrono::seconds>
                     (std::chrono::system_clock::now().time_since_epoch()).count();
    }

    static uint64_t now_to_ms()
    {
        return std::chrono::duration_cast<std::chrono::milliseconds>
                     (std::chrono::system_clock::now().time_since_epoch()).count();
    }

    static uint64_t to_ms(const std::chrono::system_clock::time_point &time)
    {
        return std::chrono::duration_cast<std::chrono::milliseconds>
                     (time.time_since_epoch()).count();
    }

    static std::string to_str(const std::chrono::system_clock::time_point &time)
    {
        char _time[25] = {0};
        time_t tt = std::chrono::system_clock::to_time_t(time);
        struct tm *local_time=localtime(&tt);
        strftime(_time, 22, "%Y-%m-%d %H:%M:%S", local_time);

        return std::string(_time);
    }

    ///"%u-%u-%u %u:%u:%u.%u"
    static std::string to_str_ex(const std::chrono::system_clock::time_point &time)
    {
        char _time[25] = {0};
        time_t tt = std::chrono::system_clock::to_time_t(time);
        struct tm *local_time=localtime(&tt);
        strftime(_time, 22, "%Y-%m-%d %H:%M:%S", local_time);

        return std::string(_time);
    }

};


class tool_other
{
public:

    static uint64_t to_uint64_cardid(uint32_t type, uint32_t id)
    {
        return (static_cast<uint64_t>(type)<<32)|id; ////// (类型<<32)|卡号
    }

    static uint64_t to_event_list_id(int obj_id, EVENT_TYPE ev_type)
    {
        return (static_cast<uint64_t>(ev_type)<<32)|static_cast<uint64_t>(obj_id);
    }

    static event_ptr create_event(OBJECT_TYPE obj_type, int obj_id, EVENT_TYPE ev_type)
    {
        auto ev_ptr = std::make_shared<ya_event>(tool_time::now_to_ms());

        ev_ptr->m_ev_type = ev_type;
        ev_ptr->m_obj_type = obj_type;
        ev_ptr->m_obj_id = static_cast<OBJECT_TYPE>(obj_id);

        ev_ptr->m_list_id = to_event_list_id(obj_id, ev_type);

        return ev_ptr;
    }

    static void insert_event(const event_ptr ev_ptr, event_map& out_ev_map)
    {
        out_ev_map.insert(std::make_pair(ev_ptr->m_list_id, ev_ptr));
    }

    static void copy_event(const std::shared_ptr<card> card_ptr, event_ptr ev_ptr)
    {
        ev_ptr->x = card_ptr->x;
        ev_ptr->y = card_ptr->y;

        if(nullptr!=card_ptr->m_area_hover)
        {
           ev_ptr->m_area_id =  card_ptr->m_area_hover->m_area->id();
        }

        ev_ptr->m_landmarkid = card_ptr->m_landmarkid;
        ev_ptr->m_landmarkdist = card_ptr->m_landmarkdist;
        ev_ptr->m_landmarkdirect = card_ptr->m_landmarkdirect;
    }

    static event_ptr find_event(int obj_id, EVENT_TYPE ev_type, const event_map& ev_map)
    {
        auto it = ev_map.find(to_event_list_id(obj_id, ev_type));
        if(ev_map.end() == it)
        {
            return nullptr;
        }

        return it->second;
    }
};


class tool_db
{
private:
    //void CYAServerDlg::load_his_event_data()
    //load_alarm_reader()

    static void PushAsync(char* sql)
    {
        log_debug("PushAsync记录到队列中:%s\n", sql);

        if(!sDBConnPool.PushAsync(sql))
        {
            log_error( "PushAsync记录到队列中失败\n");
        }
    }

public:
    static void save_event(const event_ptr ev_ptr)
    {
        char sql[LENGTH_SQL] = {'\0'};

        std::string _time = tool_time::to_str(ev_ptr->m_cur_time);

        sprintf(sql,
                "INSERT IGNORE INTO his_event_data(event_id, stat, event_type_id, obj_type_id, obj_id, \
                map_id, area_id, limit_value, cur_value, x, y, cur_time, description, \
                landmark_id, landmark_dist,direction_mapper_id )\
                VALUES(%ld, %d, %d, %d, %ld, %d, %d, %.2f, %.2f, %f, %f, '%s', '%s', %d, %d, %10.3f);",
        ev_ptr->get_id(), ev_ptr->m_status, ev_ptr->m_ev_type, ev_ptr->m_obj_type, ev_ptr->m_obj_id,
                ev_ptr->m_map_id, ev_ptr->m_area_id, ev_ptr->m_limit_value, ev_ptr->m_cur_value, ev_ptr->x, ev_ptr->y,
                _time.c_str(), ev_ptr->m_desc.c_str(),
                ev_ptr->m_landmarkid, ev_ptr->m_landmarkdirect, ev_ptr->m_landmarkdist);

        PushAsync(sql);
    }

    static void save_attendance(const std::shared_ptr<card_location_base> card_ptr)
    {
        char sql[LENGTH_SQL] = {0};

        std::string call("add_att_staff");
        if(CT_VEHICLE==card_ptr->m_type)//车卡
        {
            call="add_att_vehicle";
        }

        auto tt = card_ptr->m_attendance_start_time;
        if(AS_ATTENDANCE != card_ptr->m_stat_attendance)//考勤结束时间
        {
            tt = std::chrono::system_clock::now();
        }

        std::string _time = tool_time::to_str(tt);

        sprintf(sql, "CALL %s(%ld, %d, '%s', '%s', %d, %d, %.3f);", call.c_str(),
                card_ptr->m_id, card_ptr->m_type, _time.c_str(), _time.c_str(),
                card_ptr->m_landmarkid, card_ptr->m_landmarkdirect, card_ptr->m_landmarkdist);

        PushAsync(sql);
    }

    static void save_his_raw_data(const std::shared_ptr<card> card_ptr)
    {
        char sql[LENGTH_SQL] = {0};
        std::string _time = tool_time::to_str(card_ptr->m_deal_time);

        sprintf(sql,
                "INSERT IGNORE INTO his_raw_data(card_id, reader_id, antenna_id, reader_stamp, card_stamp, \
                fly_time, distance, ranging_type, power_state, accelerate, his_time, rec_time ) \
                VALUES(%d, %d, %d, %d, %d, %ld, %4.f, %d, %d, %d, '%s', '%s');",
                card_ptr->m_id, card_ptr->m_site_id, card_ptr->m_antenna_id,
                card_ptr->m_reader_tickcount, card_ptr->m_time_stamp,
                card_ptr->m_flying_time, card_ptr->m_distance, card_ptr->m_ranging_type, card_ptr->m_power_state,
                card_ptr->m_accelerate_state,
                card_ptr->m_str_his_time.c_str(), card_ptr->m_str_rec_time.c_str());
    }


    static void save_his_location(const std::shared_ptr<card_location_base> card_ptr)
    {
//        sprintf_s(_time,
//            STR_LEN_TIME,
//            "%u-%u-%u %u:%u:%u.%u",
//            card->deal_time.wYear,card->deal_time.wMonth,card->deal_time.wDay,
//            card->deal_time.wHour,card->deal_time.wMinute,card->deal_time.wSecond,card->deal_time.wMilliseconds);

//        if(card->map_id != 0 && card->area_id != 0){
//            b_exec = true;
//            switch (card->card_type)
//            {
//            case CT_PERSON:
//                sprintf_s(sql, LENGTH_SQL,
//                    "INSERT IGNORE INTO his_location_staff(card_id, staff_id, cur_time, x, y, z, map_id, area_id, state,speed,mileage, landmark_id, direction_mapper_id, landmark_dist) VALUES(%s, %d, '%s', %.3f, %.3f, %.3f, %d, %d, %d,%.3f,%.4f, %d, %d, %.4f);",
//                    card->card_id.c_str(), card->id, _time, card->x_offset_after(), card->y_offset_after(), card->z_offset_after(), card->map_id, card->area_id, card->state,card->get_speed(),card->mileage, card->landmark_id, card->landmark_direction, card->landmark_dis);
//                break;
//            case CT_VEHICLE:
//                sprintf_s(sql, LENGTH_SQL,
//                    "INSERT IGNORE INTO his_location_vehicle(card_id, vehicle_id, cur_time, x, y, z, map_id, area_id, state,speed,mileage, landmark_id, direction_mapper_id, landmark_dist) VALUES(%s, %d, '%s', %.3f, %.3f, %.3f, %d, %d, %d,%.3f,%.4f, %d, %d, %.4f);",
//                    card->card_id.c_str(), card->id, _time, card->x_offset_after(), card->y_offset_after(), card->z_offset_after(), card->map_id, card->area_id, card->state,card->get_speed(),card->mileage, card->landmark_id, card->landmark_direction, card->landmark_dis);
//                break;
//            }
//        }
    }

    static void save_his_area_location_enter(const std::shared_ptr<card_location_base> card_ptr)
    {
//        sprintf_s(_time,
//            STR_LEN_TIME,
//            "%u-%u-%u %u:%u:%u.%u",
//            card->deal_time.wYear,card->deal_time.wMonth,card->deal_time.wDay,
//            card->deal_time.wHour,card->deal_time.wMinute,card->deal_time.wSecond,card->deal_time.wMilliseconds);

//        if(card->map_id != 0 && card->area_id != 0){
//            b_exec = true;
//            switch (card->card_type)
//            {
//            case CT_PERSON:
//                sprintf_s(sql, LENGTH_SQL,
//                    "call add_area_staff(%s, %d, %d, %d, '%s','%s');",
//                    card->card_id.c_str(), card->id, card->area_id, card->map_id, _time, _time);
//                break;
//            case CT_VEHICLE:
//                sprintf_s(sql, LENGTH_SQL,
//                    "call add_area_vehicle(%s, %d, %d, %d, '%s','%s');",
//                    card->card_id.c_str(), card->id, card->area_id, card->map_id, _time, _time);
//                break;
//            }
//        }
    }

    static void save_his_area_location_leave(const std::shared_ptr<card_location_base> card_ptr)
    {
//        sprintf_s(_time,
//            STR_LEN_TIME,
//            "%u-%u-%u %u:%u:%u.%u",
//            card->deal_time.wYear,card->deal_time.wMonth,card->deal_time.wDay,
//            card->deal_time.wHour,card->deal_time.wMinute,card->deal_time.wSecond,card->deal_time.wMilliseconds);

//        if(card->map_id != 0 && card->area_id != 0){
//            b_exec = true;
//            sprintf_s(_time_ex,
//                STR_LEN_TIME,
//                "%u-%u-%u %u:%u:%u.%u",
//                card->enter_area_time.wYear,card->enter_area_time.wMonth,card->enter_area_time.wDay,
//                card->enter_area_time.wHour,card->enter_area_time.wMinute,card->enter_area_time.wSecond,card->enter_area_time.wMilliseconds);

//            switch (card->card_type)
//            {
//            case CT_PERSON:
//                sprintf_s(sql, LENGTH_SQL,
//                    "call add_area_staff(%s, %d, %d, %d, '%s','%s');",
//                    card->card_id.c_str(), card->id, card->area_id, card->map_id, _time_ex, _time);
//                break;
//            case CT_VEHICLE:
//                sprintf_s(sql, LENGTH_SQL,
//                    "call add_area_vehicle(%s, %d, %d, %d, '%s','%s');",
//                    card->card_id.c_str(), card->id, card->area_id, card->map_id, _time_ex, _time);
//                break;
//            }
//        }
    }
};



/**
 * @brief 线程接口类，重写run函数，改变睡眠时间 sleep_ms
 */
class i_thread
{
public:
    i_thread()
    {
        sleep_ms=10*1000;
    }

    virtual ~i_thread(){}

    /**
     * @brief 启动线程
     */
    void start()
    {
        _thread_flag=true;

        _thread_handler=std::thread(&i_thread::thread_proc, this);
    }

    /**
     * @brief 终止线程
     */
    void stop()
    {
        _thread_flag=false;
        _thread_handler.join();
    }

    ///线程睡眠时间 毫秒
    std::atomic<int> sleep_ms;

protected:
    /// 互斥量
    std::mutex _mutex;

    /**
     * @brief 线程函数
     */
    virtual void run(){}

private:
    /// 线程句柄
    std::thread _thread_handler;
    /// 线程标志
    std::atomic<bool> _thread_flag;

    void thread_proc()
    {
        while(_thread_flag)
        {
            run();

            std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
        }
    }
};


template<typename T>
class singleton_base
{
public:
    static T *instance()
    {
        if(nullptr != _instance)
        {
            return _instance;
        }

        std::lock_guard<std::mutex> ll(_mutex_singleton_base);
        if(nullptr == _instance)
        {
            _instance = new(std::nothrow) T();
        }

        return _instance;
    }

protected:
    //使继承者无法public构造函数和析构函数
    singleton_base(){}
    virtual ~singleton_base(){}
private:
    //禁止拷贝构造和赋值运算符
    singleton_base(const singleton_base& src){}
    singleton_base &operator=(const singleton_base& src){}

    //它的唯一工作就是在析构函数中析构Singleton的实例，所以private
    class Garbo
    {
    public:
        ~Garbo()
        {
            if (singleton_base::_instance)
            {
                delete singleton_base::_instance;
                singleton_base::_instance = nullptr;
            }
        }
    };
    //定义一个静态成员变量，程序结束时，系统会自动调用它的析构函数，我们不需要访问这个变量，所以不需要初始化
    static Garbo garbo;

    static T *_instance;
    static std::mutex _mutex_singleton_base;
};



#endif // MODULE_CONST_H
