#include "barrier_gate.hpp"
#include "modbus/crc16-modbus.hpp"

#include "3th-part/json.hpp"
#include <sstream>
#include <chrono>
#include <cstdint>

// const std::vector<const uint8_t> all_on {0x01, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x8C, 0x3A};
// const std::vector<const uint8_t> all_off {};
uint64_t get_current_timestamp()
{
    auto now = std::chrono::system_clock::now();
    auto duration_since_epoch = now.time_since_epoch();
    auto millis_since_epoch = std::chrono::duration_cast<std::chrono::milliseconds>(duration_since_epoch).count();
    return static_cast<uint64_t>(millis_since_epoch);
}
void printf_hex(const std::vector<uint8_t>& data)
{
    std::ostringstream ss;
    for (auto& v : data) {
        ss << "0x" << std::hex << (uint32_t)v << " ";
    }
    std::cout << ss.str() << std::endl;
}
BarrierGateControl::BarrierGateControl(nlohmann::json& config,const std::map<int, std::string>& numbarrier_info_config_map)
  : self_id(config["self_id"])
  , uart_name_(config["uart"])
  , report_topic_(config["report_topic"])
  , control_topic_(config["control_topic"])
  ,numbarrier_config_map(numbarrier_info_config_map)
{
    init();
}

BarrierGateControl::BarrierGateControl(nlohmann::json& config,const std::map<int, std::string>& numbarrier_info_config_map, const std::string& command)
  : self_id(config["self_id"])
  , uart_name_(config["uart"])
  , report_topic_(config["report_topic"])
  , control_topic_(config["control_topic"])
  , numbarrier_config_map(numbarrier_info_config_map)
  , command_(command)
{
    init();
}


void BarrierGateControl::exec()
{
    __m_running = true;
    std::string barrier_gate_status = cvt_gate_status(current_gate_);
    uint16_t gate_code ;
    auto start = std::chrono::steady_clock::now();
    while (__m_running) {
        
        auto now = std::chrono::steady_clock::now();
        
        // publish traffic light states once per second
        if(std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count() > 1000)
        {
            std::cout << "--------------------------------------" << std::endl;
            start = now;
            
            // send read input status request to gate
            {
                // std::cout << "read gate input status..." << std::endl;
                // std::vector<uint8_t> request_cmd = {0x01, 0x03, 0x06, 0x00, 0x00, 0x01};
                // auto crc = tools::crc16_modbus::crc16_code(request_cmd);
                // request_cmd.insert(request_cmd.end(), crc.begin(), crc.end());
                // std::cout << "send request status to gate: ";
                // printf_hex(request_cmd);
                // uart_->write(request_cmd.data(), request_cmd.size());
                // gate_control(0,current_gate_);
            }

            // publish barrier gate states
            {
                nlohmann::json j = nlohmann::json::object();
                j.emplace("self_id", self_id);
                j.emplace("relay_code", gate_code);
                j.emplace("status", cvt_gate_status(current_gate_));
                j.emplace("lightsource",2);
                auto containts = j.dump();
                mqtt_dataio::subscriber_basic::DataBuffer buffer;
                buffer.resize(containts.size());
                std::copy(containts.begin(), containts.end(), buffer.begin());
                std::cout << "publish traffic light states: " << containts << std::endl;
                __m_status_puber->publish(buffer);
            }
        }
        // read data from uart port
        uint8_t buffer[1024];
        int len = uart_->read(buffer, sizeof(buffer));
        if(len > 0)
        {
            for(int i = 0; i < len; i++)
            {
                std::cout << std::hex << (uint32_t)buffer[i] << " ";
            }
            std::cout << std::endl;

            // check crc code
            auto crc = tools::crc16_modbus::crc16_code(std::vector<uint8_t>(buffer, buffer + len - 2));
            if(crc[0] == buffer[len - 2] && crc[1] == buffer[len - 1])
            {
                std::cout << "crc check ok!" << std::endl;

                uint8_t func_code = buffer[1];
                gate_code = (buffer[len - 4] << 8 | buffer[len - 3]);
                if(func_code == 0x03)
                {
                    //此处属于执行指令成功
                    Exc_Barrier(3,gate_code);

                }
                else if(func_code == 0x0f)
                {
                    // TODO: this is gate control command ack package.
                    Exc_Barrier(0,gate_code); // 失败
                }
                else
                {
                    std::cout << "unknown func code: " << (int)func_code << std::endl;
                    Exc_Barrier(0,gate_code);// 失败
                }
            }
            else
            {
                std::cerr << "check gate status failed, crc error: " << buffer[len - 2] << ", " << buffer[len - 1] << ", should be: " << crc[0] << ", " << crc[1] << std::endl;
            }
        }

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

BarrierGateControl::~BarrierGateControl()
{
    __m_running = false;
}
void BarrierGateControl::init()
{
    uart_ = std::make_shared<awe::Uart>(uart_name_.c_str());
    uart_->setOpt(9600, 8, 'N', 1);

    // regist self to traffic light controller
    nlohmann::json j = nlohmann::json::object();
    j.emplace("self_id", self_id);
    j.emplace("type", "traffic_light");

    // publish traffic light status
    __m_status_puber = std::make_shared<mqtt_dataio::publisher<mqtt_dataio::subscriber_basic::DataBuffer>>(report_topic_);
    // subscribe control request
    __m_control_suber = std::make_shared<mqtt_dataio::subscriber<mqtt_dataio::subscriber_basic::DataBuffer>>(control_topic_, 
        std::bind(&BarrierGateControl::on_control_request, this, std::placeholders::_1));

    // on start up, do nothing
    // current_gate_ = GateState::OFF;
    current_gate_= cvt_gate_status(command_);
    gate_control(0, current_gate_);
    std::cerr << "init gate_control ..........."<<command_<<"current_gate_=" <<current_gate_ <<std::endl;
}

void BarrierGateControl::on_control_request(const mqtt_dataio::subscriber_basic::DataBuffer& msg)
{
    std::string str;
    str.resize(msg.size());
    memcpy((void*)str.data(), msg.data(), msg.size());

    nlohmann::json j = nlohmann::json::parse(str);
    std::cout << "recv control request: " << j.dump() << std::endl;

    if(j["id"] != self_id)
    {
        std::cerr << "invalid id: " << j["id"] << ", self id: " << self_id << ", ignore" << std::endl;
        return;
    }

    std::string status = j["status"];
    GateState gate = cvt_gate_status(status);
    gate_control(0, gate);

    std::cout << "change traffic light status from: " << cvt_gate_status(current_gate_) << " to " << cvt_gate_status(gate) << std::endl;
    current_gate_ = gate;
}

void BarrierGateControl::gate_control(const int channel, const GateState gate)
{
    curExcBarrierQueue=std::queue<roadside_info_barrier>();
    for (auto it = numbarrier_config_map.begin(); it != numbarrier_config_map.end(); ++it) 
    {
        roadside_info_barrier _info_barrier;
        _info_barrier.Id=it->first;
        _info_barrier.gatestate=gate;
        curExcBarrierQueue.push(_info_barrier); // 将键存入
    }
    //第一次执行按失败计算
    Exc_Barrier(0,0);
}

void BarrierGateControl::Exc_Barrier(int stateCode,uint16_t changeCode )
{
    roadside_info_barrier _info_barrier;
    if(stateCode==3)//成功取下一个值
    {
        if(curExcBarrierQueue.empty())
        {
            // 所有得切换成功 ，提示状态
            current_gate_=(GateState)changeCode;
            std::string barrier_gate_status = cvt_gate_status(changeCode);
            std::cout << "gate code: " << (int)changeCode << ", barrier gate status: " << barrier_gate_status << std::endl;
            return;
        }
        //成功删除第一个值，取下一个值
        curExcBarrierQueue.pop();
        _info_barrier=curExcBarrierQueue.front();
    }
    else //不成功执行当前值
    {
        _info_barrier=curExcBarrierQueue.front();
    }
    std::vector<uint8_t> relay_cmd = {_info_barrier.Id, 0x06, 0x05, 0x05, 0x00, static_cast<uint8_t>(_info_barrier.gatestate)};
    auto pkg = tools::crc16_modbus::crc16_code(relay_cmd);
    relay_cmd.insert(relay_cmd.end(), pkg.begin(), pkg.end());
    std::cout << "exc send relay control cmd: ";
    printf_hex(relay_cmd);
    uart_->write(relay_cmd.data(), relay_cmd.size());
   
}

const BarrierGateControl::GateState BarrierGateControl::cvt_gate_status(const std::string& status)
{
    // lower status string
    std::string s = status;
    std::transform(s.begin(), s.end(), s.begin(), ::tolower);
    if(s == "red")
    {
        return GateState::OFF;
    }
    else if(s == "green")
    {
        return GateState::ON;
    }
    else if(s == "yellow")
    {
        return GateState::PAUSE;
    }
    return GateState::UNKNOWN;
}
const std::string BarrierGateControl::cvt_gate_status(const GateState& status)
{
    switch(status)
    {
        case GateState::ON:
            return "green";
        case GateState::OFF:
            return "red";
        case GateState::PAUSE:
            return "yellow";
        default:
            return "unknown";
    }
    return "unknown";
}
const std::string BarrierGateControl::cvt_gate_status(const uint16_t& status)
{
    switch(status)
    {
        case 1: return "initilized"; break;
        case 2: return "opening"; break;
        case 3: return "opened"; break;
        case 4: return "closing"; break;
        case 5: return "closed"; break;
        case 6: return "close_blocked"; break;
        case 7: return "open_blocked"; break;
        case 8: return "stopping"; break;
        case 9: return "studdy"; break;
        case 10: return "system_reset"; break;
        case 11: return "reset_needed"; break;
        case 12: return "remote_always_on"; break;
        default: return "unknown"; break;
    }
    return "unknown";
}