/***** (C) Copyright, Shenzhen SUNWIN Intelligent Co.,Ltd. ******source file****
* File name          : expel.cpp
* Author             : JinLiang Yang
* Brief              : 
********************************************************************************
* modify
* Version   Date                 Author              Described
* V2.00     2021/04/8            ShengYin Li         Created
*******************************************************************************/
#include "expel.h"
#include "config.hpp"

//device
#include "deviceGasV1.h"
#include "deviceGasV2.h"
#include "deviceGasV3.h"
#include "deviceUlt.h"
#include "deviceBase.h"
#include "devicePlayer.h"

#include "Network/TcpClient.h"
using namespace toolkit;


class ExpelClient: public TcpClient {
public:
    typedef std::shared_ptr<ExpelClient> Ptr;
    ExpelClient():TcpClient() {
    }
    ~ExpelClient(){
    }

    /**
     * @description: setDeviceControl
     * @param {shared_ptr<deviceBase>} DeviceControl
     * @return {*}
     */    
    void setDeviceControl(std::shared_ptr<deviceBase> DeviceControl){
        this->deviceControl = DeviceControl;
    }

    /**
     * @description: setSendBuf
     * @param {Ptr} Buffer
     * @return {*}
     */    
    void setSendBuf(const BufferRaw::Ptr Buffer){
        buffer = Buffer;
    }

protected:
    virtual void onConnect(const SockException &ex) override{
        //连接结果事件
        if(buffer != nullptr) this->send(buffer);
    }
    virtual void onRecv(const Buffer::Ptr &pBuf) override{
        int msg = 0;
        if(this->deviceControl == nullptr) return;
        if(this->deviceControl->Consumer((uint8_t *)pBuf->data(), (int)pBuf->size(), msg)){
            if(config::getPtr()->debug) DebugL << "deviceId[" << deviceControl->getDeviceId() << "] Device response is succeed" << endl;
            if(config::getPtr()->debug) DebugL << "REV:" << Expel::bytesToHexString((uint8_t *)pBuf->data(), (int)pBuf->size()) << endl;
            std::string json;
            SendDevice sendDevice;
            time_t timestamp;
            NettyClientResultMsg<SendDevice> nettyClientResultMsg;
            deviceControl->BuildDeviceInfo(sendDevice);
            time(&timestamp);
            sendDevice.msgTime = ctime(&timestamp);
            std::string requestId;
            requestId = uuid::generate();
            nettyClientResultMsg.setRequestId(requestId);
            nettyClientResultMsg.setDataType(NettyClientCommandEnum().device_info);
            nettyClientResultMsg.setData(sendDevice);
            nettyClientResultMsg.objectToJson(json);
            NoticeCenter::Instance().emitEvent(NOTICE_DEVICEINFO, requestId ,json);
        }
    }
    virtual void onFlush() override{
        //发送阻塞后，缓存清空事件
        DebugL;
    }
    virtual void onErr(const SockException &ex) override{
        //断开连接事件，一般是EOF
        WarnL << ex.what();
    }
    virtual void onManager() override{
    }
private:
    int _nTick = 0;
    std::shared_ptr<deviceBase> deviceControl  = nullptr;
    BufferRaw::Ptr buffer = nullptr;
};


Expel::Expel(){  

}

void Expel::Init()
{
}


/**
 * @description: 获取Ptr
 * @return {*}
 */
std::shared_ptr<Expel> Expel::getPtr(){
    static std::shared_ptr<Expel> m_Expel = nullptr;
    if(m_Expel == nullptr) m_Expel = std::shared_ptr<Expel>(new Expel);
    return m_Expel;
}

/**
 * @description: 
 * @param {string} &commandCode
 * @return {*}
 */
int32_t Expel::Controler(std::string commandCode){
    int code = atoi(commandCode.c_str());

    switch (code)
    {
    // 开启电源
    case 2:
        this->AllOpen();
        break;
    // 关闭电源
    case 3:
        this->AllClose();
        break;
    // 开炮
    case 4:
        this->AllFire();
        break;
    default:
        break;
    }
  
    return OK;
}


/**
 * @description: 
 * @param {NettyServerResultMsg<RecDeviceCommand>} &nettyServerResultMsg
 * @param {ExpelDevice} expelDevice
 * @return {*}
 */
void Expel::sendDeviceMsgCallback(NettyServerResultMsg<RecDeviceCommand> &nettyServerResultMsg, ExpelDevice expelDevice) {
    RecDeviceCommand recDeviceCommand;
    recDeviceCommand = nettyServerResultMsg.getData();
    std::string deviceId = to_string(recDeviceCommand.getDeviceId());
    int commandCode = atoi(recDeviceCommand.getCommandCode().c_str());

    //InfoL  << "deviceId:" << recDeviceCommand.getDeviceId();
    switch (commandCode)
    {
    // 查询
    case 1:
        this->Status(expelDevice);
        break;
    // 开启电源
    case 2:
        this->Open(expelDevice);
        break;
    // 关闭电源
    case 3:
        this->Close(expelDevice);
        break;
    // 开炮
    case 4:
        this->Fire(expelDevice);
        break;
    default:
        break;
    }
}
 
Expel::~Expel() {
    InfoL << "Release Expel resources";
}

/**
 * @description: 获取控制器
 * @param {*}
 * @return {*}
 */
std::shared_ptr<deviceBase> Expel::getControl(ExpelDevice& expelDevice){
    static std::shared_ptr<deviceBase> deviceControl = nullptr;
    switch (expelDevice.deviceType)
    {
    case DeviceGasV1:
        deviceControl = std::make_shared<deviceGasV1>();
        break;
    
    case DeviceGasV2:
        deviceControl = std::make_shared<deviceGasV2>();
        break;

    case DeviceUlt:
        deviceControl = std::make_shared<deviceUlt>();
        break;
    
    case DevicePlayer:
        deviceControl = std::make_shared<devicePlayer>();
        break;

    case DeviceGasV3:
        deviceControl = std::make_shared<deviceGasV3>();
        break;
    }
    // 设置token
    deviceControl->setDeviceToken((enum DeviceToken)expelDevice.deviceToken);
    deviceControl->setServerIp(expelDevice.ServerIp);
    deviceControl->setServerCname(expelDevice.ServerCname);
    deviceControl->setServerCpwd(expelDevice.ServerCpwd);

    return deviceControl;
}

/**
 * @description: 打开设备
 * @param {int} deivceId
 * @return {*}
 */
int32_t Expel::Open(ExpelDevice& expelDevice){
    std::shared_ptr<deviceBase> deviceControl = nullptr;
    deviceControl = getControl(expelDevice);
    uint8_t data[1024];
    memset(data, 0, sizeof(data));
    int length = 0;
    std::string deviceId = expelDevice.DeviceId;
    deviceControl->setDeviceId(deviceId);
    if(config::getPtr()->debug) InfoL << "DeviceId:" << deviceId  << ", deviceType:" << expelDevice.deviceType << endl;
    // 获取指令
    if(deviceControl != nullptr && deviceControl->Open(deviceId, data, length) == 0){
        return Control(deviceControl, expelDevice, data, length, 2);
    }
    return -1;
}

/**
 * @description: 关闭设备
 * @param {int} deivceId
 * @return {*}
 */
int32_t Expel::Close(ExpelDevice& expelDevice){
    std::shared_ptr<deviceBase> deviceControl = nullptr;
    deviceControl = getControl(expelDevice);
    uint8_t data[1024];
    memset(data, 0, sizeof(data));
    int length = 0;
    std::string deviceId = expelDevice.DeviceId;
    if(config::getPtr()->debug) InfoL << "DeviceId:" << deviceId  << ", deviceType:" << expelDevice.deviceType << endl;
    // 获取指令
    if(deviceControl != nullptr && deviceControl->Close(deviceId,  data, length) == 0){
        return Control(deviceControl, expelDevice, data, length, 3);
    }
    return -1;
}

/**
 * @description: 开火
 * @param {int} deivceId
 * @return {*}
 */
int32_t Expel::Fire(ExpelDevice& expelDevice){
    std::shared_ptr<deviceBase> deviceControl = nullptr;
    deviceControl = getControl(expelDevice);
    uint8_t data[1024];
    memset(data, 0, sizeof(data));
    int length = 0;
    std::string deviceId = expelDevice.DeviceId;
    if(config::getPtr()->debug) InfoL << "DeviceId:" << deviceId  << ", deviceType:" << expelDevice.deviceType << endl;
    // 获取指令
    if(deviceControl != nullptr && deviceControl->Fire(deviceId,  data, length) == 0){
        return Control(deviceControl, expelDevice, data, length, 4);
    }
    return -1;
}
    
/**
 * @description: 状态
 * @param {int} deviceId
 * @param {string&} json
 * @return {*}
 */
int32_t Expel::Status(ExpelDevice& expelDevice){
    std::shared_ptr<deviceBase> deviceControl = nullptr;
    deviceControl = getControl(expelDevice);
    uint8_t data[1024];
    memset(data, 0, sizeof(data));
    int length = 0;
    std::string deviceId = expelDevice.DeviceId;
    if(config::getPtr()->debug) InfoL << "DeviceId:" << deviceId  << ", deviceType:" << expelDevice.deviceType << endl;
    // 获取指令
    if(deviceControl != nullptr && deviceControl->Status(deviceId,  data, length) == 0){
        return Control(deviceControl, expelDevice, data, length, 1);
    }
    return -1;
}

/**
 * @description: 数组转换为16进制字符串
 * @param {uint8_t*} bytes
 * @param {int} length
 * @return {*}
 */
std::string Expel::bytesToHexString(const uint8_t* bytes,const int length) 
{
	if (bytes == NULL) {
		return "";
	}
	std::string buff;
	const int len = length;
	for (int j = 0; j < len; j++) {
		int high = bytes[j]/16, low = bytes[j]%16;
		buff += (high<10) ? ('0' + high) : ('a' + high - 10);
		buff += (low<10) ? ('0' + low) : ('a' + low - 10);
        buff += " ";
	}
	return buff;
}

/**
 * @description: control
 * @param {*}
 * @return {*}
 */
int32_t Expel::Control(std::shared_ptr<deviceBase>& deviceControl, ExpelDevice& expelDevice, uint8_t* data, int& length, int type){
    static ExpelClient::Ptr client(new ExpelClient());
    std::lock_guard<mutex> gurad(m_mutex);
    deviceControl->setDeviceId(expelDevice.DeviceId);
    deviceControl->setControlType(type);
    if(deviceControl->getDevcieToken() == serial){
        CSerialPort ser;
        config::Ptr m_config = config::getPtr();
        if(m_config->debug) DebugL << "Serial control data:" << bytesToHexString(data, length) << endl;
        ser.init(m_config->comPort.c_str());
        ser.setBaudRate(deviceControl->getBaudRate());
        if(!ser.open()){ 
            ErrorL << "Open port falied!";
            return -1;
        }
        if(ser.writeData((const char*)data, length) != length){
            WarnL << "Control command failed" << endl;
            return -1;
        }
        this->SerialReceive(ser, deviceControl);
        ser.close();
        return 0;
    }else if(deviceControl->getDevcieToken() == http){
        if(expelDevice.ServerIp == ""){
            ErrorL << "The device IP address is empty" << endl;
            return -1;
        }
        httplib::Client cli(expelDevice.ServerIp, config::getPtr()->ExpelPort);
        std::string json;
        json = (char*)data;
        DebugL << "Http control data:" << json << endl;
        PlayerInquire playerInquire;
        if (auto res = cli.Post(playerInquire.getUrl().c_str(), json, "application/json")) {
            if (res->status == 200) {
                DebugL << res->body << endl;
                return 0;
            }else {
                WarnL << res->body << endl;
                return -1;
            }
        } else {
            auto err = res.error();
            ErrorL << "HTTP Service unavailable:" << err << endl;
            return -1;
        }
    }else if(deviceControl->getDevcieToken() == tcp){
        if(expelDevice.ServerIp == ""){
            ErrorL << "The device IP address is empty" << endl;
            return -1;
        }
        client->setDeviceControl(deviceControl);
        if(!client->alive()) client->startConnect(expelDevice.ServerIp, config::getPtr()->ExpelPort);
        if(config::getPtr()->debug) DebugL << "Serial control data:" << bytesToHexString(data, length) << endl;
        auto buf = BufferRaw::create();
        buf->assign((char*)data, length);
        client->setSendBuf(buf);
        if(client->alive()) client->send(buf);
    }
    else return -1;
}

/**
 * @description: 串口数据接收
 * @param {*}
 * @return {*}
 */
bool Expel::SerialReceive(CSerialPort& ser, std::shared_ptr<deviceBase>& deviceControl){
    if(ser.isOpened()){
        Ticker ticker0;
        uint8_t buffer[1024];
        memset(buffer, 0, sizeof(buffer));
        config::Ptr m_config = config::getPtr();
        while (true)
        {
            if(ticker0.elapsedTime() >= 2000){  
                WarnL << "deviceId[" << deviceControl->getDeviceId() << "] Control instruction timeout" << endl;
                break;
            } 
            int len = ser.readAllData((char *)(buffer));
            if(len == 0) continue;
            int msg_id;
            if(deviceControl->Consumer(buffer, len, msg_id)){
                if(m_config->debug) DebugL << "deviceId[" << deviceControl->getDeviceId() << "] Device response is succeed" << endl;
                if(m_config->debug) DebugL << "REV:" << bytesToHexString(buffer, len) << endl;
                if(deviceControl->getStatusMsg(msg_id)){
                    std::string json;
                    SendDevice sendDevice;
                    time_t timestamp;
                    NettyClientResultMsg<SendDevice> nettyClientResultMsg;
                    deviceControl->BuildDeviceInfo(sendDevice);
                    time(&timestamp);
                    sendDevice.msgTime = ctime(&timestamp);
                    std::string requestId;
                    requestId = uuid::generate();
                    nettyClientResultMsg.setRequestId(requestId);
                    nettyClientResultMsg.setDataType(NettyClientCommandEnum().device_info);
                    nettyClientResultMsg.setData(sendDevice);
                    nettyClientResultMsg.objectToJson(json);
                    NoticeCenter::Instance().emitEvent(NOTICE_DEVICEINFO, requestId ,json);
                }
                break;
            }
        }
    }
    return true;
}

/**
 * @description: 全部打开
 * @param {*}
 * @return {*}
 */    
int32_t Expel::AllOpen(){
    if(!config::getPtr()->LowVersion){
        for (auto iter = m_deviceLists.begin(); iter != m_deviceLists.end(); iter++){
            this->Open(*iter);
        }
    }else{
        if(config::getPtr()->DeviceIds.empty()){
            return 0;
        } 
        for(auto iter = config::getPtr()->DeviceIds.begin(); iter != config::getPtr()->DeviceIds.end(); iter++){
            ExpelDevice expelDevice;
            expelDevice.DeviceId = std::to_string(*iter);
            expelDevice.deviceType = (enum DeviceType)config::getPtr()->deviceType;
            if(expelDevice.deviceType == 4) expelDevice.deviceToken = tcp;
            else if(expelDevice.deviceType == 3) expelDevice.deviceToken = http;
            else expelDevice.deviceToken = serial;  
            this->Open(expelDevice);
        }
    }
    return 0;
}

/**
 * @description: 
 * @param {*}
 * @return {*}
 */    
int32_t Expel::AllClose(){
    if(!config::getPtr()->LowVersion){
        for (auto iter = m_deviceLists.begin(); iter != m_deviceLists.end(); iter++){
            this->Close(*iter);
        }
    }else{
        if(config::getPtr()->DeviceIds.empty()) return 0;
        for(auto iter = config::getPtr()->DeviceIds.begin(); iter != config::getPtr()->DeviceIds.end(); iter++){
            ExpelDevice expelDevice;
            expelDevice.DeviceId = std::to_string(*iter);
            expelDevice.deviceType = (enum DeviceType)config::getPtr()->deviceType;
            if(expelDevice.deviceType == 4) expelDevice.deviceToken = tcp;
            else if(expelDevice.deviceType == 3) expelDevice.deviceToken = http;
            else expelDevice.deviceToken = serial;   
            this->Close(expelDevice);
        }
    }
    return 0;
}

/**
 * @description: 全部开炮
 * @param {*}
 * @return {*}
 */    
int32_t Expel::AllFire(){
    if(!config::getPtr()->LowVersion){
        for (auto iter = m_deviceLists.begin(); iter != m_deviceLists.end(); iter++){
            this->Fire(*iter);
        }
    }else{
        if(config::getPtr()->DeviceIds.empty()) return 0;
        for(auto iter = config::getPtr()->DeviceIds.begin(); iter != config::getPtr()->DeviceIds.end(); iter++){
            ExpelDevice expelDevice;
            expelDevice.DeviceId = std::to_string(*iter);
            expelDevice.deviceType = (enum DeviceType)config::getPtr()->deviceType;
            if(expelDevice.deviceType == 4) expelDevice.deviceToken = tcp;
            else if(expelDevice.deviceType == 3) expelDevice.deviceToken = http;
            else expelDevice.deviceToken = serial;     
            this->Fire(expelDevice);
        }
    }
    return 0;
}

/**
 * @description: 获取设备列表
 * @param {*}
 * @return {*}
 */
int32_t Expel::UpdateDeviceList(vector<vector<std::string>>& datas){
    if(datas.empty()) {
        this->m_deviceLists.clear();
        return 0;
    }
    this->m_deviceLists.clear();
    for(auto &line: datas){
        ExpelDevice expelDevice;
        expelDevice.DeviceId = line[0];
        expelDevice.deviceToken =(enum DeviceToken)atoi(line[1].c_str());
        expelDevice.deviceType =  (enum DeviceType)atoi(line[2].c_str());
        expelDevice.ServerIp =  line[3];
        expelDevice.ServerCname =  line[4];
        expelDevice.ServerCpwd = line[5];
        this->m_deviceLists.push_back(expelDevice);
    }
    return 0;
}

/**
 * @description: 获取驱鸟设备数量
 * @param {*}
 * @return {int32_t}
  */
int32_t Expel::getExpelSize(){
    if(!config::getPtr()->LowVersion){
        return m_deviceLists.size();
    }else{
        return config::getPtr()->DeviceIds.size();
    }
}


/******** (C) Copyright, Shenzhen SUNWIN Intelligent Co.,Ltd. ******** End *****/