#include "ChaletDevice.h"
#include "GlobalDefine.h"


CChaletDevice::CChaletDevice(int Address, CAbstractNetworkCommIF *NetworkInterface):
    CNetworkDevice(ID_CHALET_DEVICE,Address,NetworkInterface)
{
    NetworkInterface->mDevicePtr = this;
    mDeviceAddress = Address;
    mDeviceID = ID_CHALET_DEVICE;

    mChaletStatusTimer = new QTimer();
    mChaletStatusTimer->setInterval(1000);
    mChaletStatusTimer->setSingleShot(true);
    connect(mChaletStatusTimer,SIGNAL(timeout()),this,SLOT(CommTimerExpired()));

 //   mJFUbidotsInterface = new CChaletJFUbidotsInterface("BBFF-tEq4lGAegEyP1H4EaYckNQ7ZHdZYzI");

    mThingsBoardInterface.mChaletLoraDevice = this;

    mChaletModbusRepo.AddHRDataMap(0x2000,50);
    mChaletModbusServer = new CChaletModbusServer(&mChaletModbusRepo,512,1);

    mDeviceWiFiParameters.fill(0xFF);
    mDeviceFirmwareVersion.fill(0xFF);
    mHomeServerInterface = 0;

    ResetCommStats();
}

CChaletDevice::~CChaletDevice()
{
    delete mChaletStatusTimer;
    if(mHomeServerInterface != 0)
    {
        delete mHomeServerInterface;
    }
}

int CChaletDevice::Start()
{
    ScheduleChaletStatusRequest();
    mChaletStatusTimer->start(1000);
   // mThingsBoardInterface.RegisterThingsboardRPC();
    if(mHomeServerInterface != 0)
    {
        mHomeServerInterface->Start();
    }
    return RET_OK;
}

int CChaletDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data)
{
    Q_UNUSED(DeviceID)
    Q_UNUSED(DeviceAddress)
    Q_UNUSED(DataSize)

    switch(MessageID)
    {
        case CHALET_ACK:
        {
            CmdResponseReceived(CHALET_ACK);
            break;
        }
        case CHALET_GENERAL_STATUS_RESPONSE:
        {
            float temp;
            qint16 temp2;
            quint32 temp3;
//            qDebug("Chalet Status RX");
            char VoltageArray[4];
            char TemperatureArray[4];
            char TotalRequestsArray[4];
            char SolarPanelCurrentArray[2];
            char BatterySOCArray[2];

            mChaletMainStatus.mInverterRelayStatus = ((Data[0] & LORA_CHALET_STATUS_POWER_RELAY_MASK) != 0);
            mChaletMainStatus.mCurrentSensorStatus = ((Data[0] & LORA_CHALET_STATUS_CUR_SENSOR_MASK) != 0);

            mChaletMainStatus.mWiFiModuleStatus = Data[1];
            VoltageArray[0] = Data[2];
            VoltageArray[1] = Data[3];
            VoltageArray[2] = Data[4];
            VoltageArray[3] = Data[5];
            memcpy(&temp,VoltageArray,4);
            mChaletMainStatus.mBatteryVoltage = temp;

            SolarPanelCurrentArray[0] = Data[6];
            SolarPanelCurrentArray[1] = Data[7];
            memcpy(&temp2,SolarPanelCurrentArray,2);
            mChaletMainStatus.mBatteryCurrent = temp2;

            BatterySOCArray[0] = Data[8];
            BatterySOCArray[1] = Data[9];
            memcpy(&temp2,BatterySOCArray,2);
            mChaletMainStatus.mBatterySOC = temp2;

            TemperatureArray[0] = Data[10];
            TemperatureArray[1] = Data[11];
            TemperatureArray[2] = Data[12];
            TemperatureArray[3] = Data[13];
            memcpy(&temp,TemperatureArray,4);
            mChaletMainStatus.mChaletTemperature = temp;

            TotalRequestsArray[0] = Data[14];
            TotalRequestsArray[1] = Data[15];
            TotalRequestsArray[2] = Data[16];
            TotalRequestsArray[3] = Data[17];
            memcpy(&temp3,TotalRequestsArray,4);
            mChaletMainStatus.mTotalNbChaletRxCmds = temp3;

            mChaletMainStatus.mStatusToggleBit = !mChaletMainStatus.mStatusToggleBit;
            mChaletMainStatus.mLastLoraStatus = QDateTime::currentDateTime();

            CmdResponseReceived(CHALET_GENERAL_STATUS_REQUEST);
//            qDebug("voltage: %f",mChaletMainStatus.mBatteryVoltage);
//            qDebug("Current: %d",mChaletMainStatus.mBatteryCurrent);
//            qDebug("SOC: %d",mChaletMainStatus.mBatterySOC);
//            qDebug("Inverter: %d",mChaletMainStatus.mInverterRelayStatus);
//             qDebug("Chalet Rx Nb Cmd: %d",mChaletMainStatus.mTotalNbChaletRxCmds);

            mChaletDataLogger.LogChaletLORAData(&mChaletMainStatus);

        //    mJFUbidotsInterface->LogUbidotsChaletData(&mChaletMainStatus);

     //       mBlynkInterface.UpdateChaletCurrent(mChaletMainStatus.mBatteryCurrent);
     //       mBlynkInterface.UpdateChaletVoltage(mChaletMainStatus.mBatteryVoltage);
//            mBlynkInterface.UpdateChaletWifiStatus((int)mChaletMainStatus.mInverterRelayStatus);
     //       mBlynkInterface.UpdateChaletInverterStatus((int)mChaletMainStatus.mInverterRelayStatus);
     //       mBlynkInterface.UpdateChaletStatusLed(((int)mChaletMainStatus.mStatusToggleBit)*255);

           // mThingsBoardInterface.UpdateChaletVoltage(mChaletMainStatus.mBatteryVoltage);
            //mThingsBoardInterface.UpdateChaletData(mChaletMainStatus.mInverterRelayStatus,mChaletMainStatus.mWiFiModuleStatus,mChaletMainStatus.mCurrentSensorStatus,mChaletMainStatus.mBatteryVoltage,mChaletMainStatus.mBatteryCurrent,mChaletMainStatus.mBatterySOC,mChaletMainStatus.mIsOnline);
            //mThingsBoardInterface.UpdateChaletData(&mChaletMainStatus);

            if(mHomeServerInterface != 0)
            {
                mHomeServerInterface->UpdateChaletData(&mChaletMainStatus);
            }


//            mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_ONLINE_STATE_REG,(quint16)mChaletMainStatus.mIsOnline);
//            mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_WIFI_STATE_REG,(quint16)mChaletMainStatus.mWiFiModuleStatus);
//            mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_CURRENT_SENSOR_STATE_REG,(quint16)mChaletMainStatus.mCurrentSensorStatus);
//            mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_HARAKIRI_DONE_REG,(quint16)mChaletMainStatus.mHarakiriDone);
//            mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_STATUS_TOGGLE_REG,(quint16)mChaletMainStatus.mStatusToggleBit);
//            //mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_WATCHDOG_STATUS_REG,(quint16),mChaletMainStatus.m)
//            mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_INVERTER_REG,(quint16)mChaletMainStatus.mInverterRelayStatus);

//            quint16 TempInt16;
//            memcpy(&TempInt16,&VoltageArray[0],2);
//            mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_BATTERY_VOLTAGE_REG_1,TempInt16);
//            memcpy(&TempInt16,&VoltageArray[2],2);
//            mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_BATTERY_VOLTAGE_REG_2,TempInt16);

//            mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_BATTERY_CURRENT_REG_1,mChaletMainStatus.mBatteryCurrent);
//            mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_BATTERY_SOC_REG,mChaletMainStatus.mBatterySOC);

            break;
        }
        case CHALET_AC_POWER_STATE_STATUS_RESPONSE:
        {
            mChaletMainStatus.mInverterRelayStatus = Data[0];
            CmdResponseReceived(CHALET_AC_POWER_STATE_STATUS_REQUEST);
            break;
        }
        case CHALET_AC_POWER_SET_STATE_RESPONSE:
        {
  //          qDebug("Lora set Inverter Power response : 0x%d",(int)Data[0]);
            mChaletMainStatus.mInverterRelayStatus = Data[0];
            CmdResponseReceived(CHALET_AC_POWER_SET_STATE_REQUEST);
            break;
        }
        case CHALET_BATTERY_VOLTAGE_RESPONSE:
        {
            CmdResponseReceived(CHALET_BATTERY_VOLTAGE_REQUEST);
            break;
        }
        case CHALET_BATTERY_CURRENT_RESPONSE:
        {
            CmdResponseReceived(CHALET_BATTERY_CURRENT_REQUEST);
            break;
        }
        case CHALET_WIFI_STATUS_RESPONSE:
        {
            CmdResponseReceived(CHALET_WIFI_STATUS_REQUEST);
            emit DeviceWifiStatusReceived(Data);
            break;
        }
        case CHALET_WIFI_SET_STATE_RESPONSE:
        {
  //          qDebug("Lora set WiFi response : 0x%d",(int)Data[0]);
            mChaletMainStatus.mWiFiModuleStatus = Data[0];
            CmdResponseReceived(CHALET_WIFI_SET_STATE_REQUEST);
            break;
        }
        case CHALET_DO_HARAKIRI_CONFIRMATION:
        {
            if(Data[0] == (char)1)
            {
                qDebug("ChaletDuino has commited suicide (HARAKIRI)");
                mChaletMainStatus.mHarakiriDone = true;
            }
            else
            {
                qDebug("HARAKIRI magic word invalid.");
            }
            CmdResponseReceived(CHALET_DO_HARAKIRI_REQUEST);
            break;
        }
        case CHALET_REBOOT_CPU_RESPONSE:
        {
            if(Data[0] == (char)1)
            {
                qDebug("ChaletDuino is rebooting");
            }
            else
            {
                qDebug("Reboot magic word invalid");
            }
            CmdResponseReceived(CHALET_DO_HARAKIRI_REQUEST);
            break;
        }
        case CHALET_GET_STORED_WIFI_SETTINGS_RESPONSE:
        {

            CmdResponseReceived(CHALET_GET_STORED_WIFI_SETTINGS_REQUEST);
            mDeviceWiFiParameters = Data.left(DataSize);
            emit DeviceWifiStoredParamsReceived();
            break;
        }
        case CHALET_SET_STORED_WIFI_SETTINGS_RESPONSE:
        {
            CmdResponseReceived(CHALET_SET_STORED_WIFI_SETTINGS_REQUEST);
            emit DeviceWifiSetParamsResult(Data[0]);
            break;
        }
        case CHALET_GET_FIRMWARE_VERSION_RESPONSE:
        {
            CmdResponseReceived(CHALET_GET_FIRMWARE_VERSION_REQUEST);
            mDeviceFirmwareVersion = Data.left(15);
            emit DeviceFirmwareVersionReceived();
            break;
        }
        case CHALET_CLEAR_COMMS_STATISTICS_RESPONSE:
        {
            ResetCommStats();
            CmdResponseReceived(CHALET_CLEAR_COMMS_STATISTICS_REQUEST);
            break;
        }

        default:
        {
            return RET_ERROR;
            break;
        }

    }
    return RET_OK;
}

void CChaletDevice::CommTimerExpired()
{
    if(mPendingNetworkMsgList.isEmpty())
    {
        qDebug("Empty command list in comm timer ");
        ScheduleChaletStatusRequest();
        mChaletStatusTimer->start(LORA_NORMAL_REQUEST_TIMEOUT);
        return;
    }
    if(mPendingNetworkMsgList.first().PendingResponse == true)
    {
        //The current command is still waiting for a response.  Check how many times we tried to send it
       // qDebug("Cmd 0x%x timetout... retrying",mPendingNetworkMsgList.first().mMessageID);
        if(mPendingNetworkMsgList.first().ResendCounter >= 2)
        {
            //After 2 retries, declare module offline, clear the send buffer and start sending status requests...
            if(mChaletMainStatus.mIsOnline == true)
            {
                mChaletMainStatus.mIsOnline = false;
                qDebug("Chalet LORA interface Offline.  Switching to status requests...");

                mPendingNetworkMsgList.clear();
                ScheduleChaletStatusRequest();
                mPendingNetworkMsgList.first().ResendCounter = 2;
            }
        }
        mPendingNetworkMsgList[0].ResendCounter++;
        mNbLostRequest++;
        mChaletMainStatus.mMasterLostRequestCount = mNbLostRequest;
        ComputeCommStats();
    }

//    qDebug("Sending chalet request 0x%x",mPendingNetworkMsgList.at(0).mMessageID);

    mPendingNetworkMsgList[0].PendingResponse = true;
    SendChaletCommand(mPendingNetworkMsgList.at(0).mMessageID,mPendingNetworkMsgList.at(0).mData.size(),mPendingNetworkMsgList.at(0).mData);


    //Harakiri and Reboot commands should not stay in the queue...
    if(mPendingNetworkMsgList.at(0).mMessageID == CHALET_DO_HARAKIRI_REQUEST ||
       mPendingNetworkMsgList.at(0).mMessageID == CHALET_REBOOT_CPU_REQUEST)
    {
        mPendingNetworkMsgList.clear();
        ScheduleChaletStatusRequest();
    }

    mChaletStatusTimer->start(LORA_RESPONSE_TIME_TIMEOUT);    //we should get an answer within 5 seconds.
//    SendChaletCommand(CHALET_GENERAL_STATUS_REQUEST,0,QByteArray());
}

int CChaletDevice::SendWiFiModuleSetState(bool State)
{
    QByteArray Data;
    Data.resize(1);
    if(State)
    {
        Data[0] = 1;
    }
    else
    {
        Data[0] = 0;
    }

    return ScheduleChaletCommand(CHALET_WIFI_SET_STATE_REQUEST,1,Data);
}

int CChaletDevice::SendInverterPowerRelayState(bool State)
{
    QByteArray Data;
    Data.resize(1);
    if(State)
    {
        Data[0] = 1;
    }
    else
    {
        Data[0] = 0;
    }

   return ScheduleChaletCommand(CHALET_AC_POWER_SET_STATE_REQUEST,Data);
}

int CChaletDevice::SendDOHarakiri()
{
    QByteArray Data;    //Magic word...
    Data.clear();
    Data.append(0xBA);
    Data.append(0xAD);
    Data.append(0xBE);
    Data.append(0xEF);

    return ScheduleChaletCommand(CHALET_DO_HARAKIRI_REQUEST,Data);
}

int CChaletDevice::SendRebootCmd()
{
    QByteArray Data;    //Magic word...
    Data.clear();
    Data.append(0xBA);
    Data.append(0xAD);
    Data.append(0xCA);
    Data.append(0xFE);

    return ScheduleChaletCommand(CHALET_REBOOT_CPU_REQUEST,Data);
}
int CChaletDevice::SendGetWifiParams()
{
    return ScheduleChaletCommand(CHALET_GET_STORED_WIFI_SETTINGS_REQUEST,QByteArray());
}

int CChaletDevice::SendSetWifiParams(QByteArray Data)
{
    return ScheduleChaletCommand(CHALET_SET_STORED_WIFI_SETTINGS_REQUEST,Data);
}

int CChaletDevice::SendGetFirmwareVersion()
{
    return ScheduleChaletCommand(CHALET_GET_FIRMWARE_VERSION_REQUEST,QByteArray());
}

int CChaletDevice::SendClearCommStatsRequest()
{
    return ScheduleChaletCommand(CHALET_CLEAR_COMMS_STATISTICS_REQUEST,QByteArray());
}

int CChaletDevice::SendGetWifiStatusRequest()
{
    return ScheduleChaletCommand(CHALET_WIFI_STATUS_REQUEST,QByteArray());
}

int CChaletDevice::SendChaletCommand(int CmdID, int DataSize, QByteArray Data)
{
    if(mNetworkInterfacePtr == 0)
    {
        return RET_ERROR;
    }

    mNetworkInterfacePtr->SendNetworkMessage(ID_CHALET_DEVICE,mDeviceAddress,CmdID,DataSize,&Data);
    mTotalNbRequest++;
    mChaletMainStatus.mTotalNbMasterTxCmds++;
    ComputeCommStats();
    return RET_OK;
}

int CChaletDevice::CmdResponseReceived(int CmdID)
{
    Q_UNUSED(CmdID)

    if(mChaletMainStatus.mIsOnline == false)
    {
        mChaletMainStatus.mIsOnline = true;
        qDebug("Chalet is ONLINE!");
    }

    //qDebug("Chalet response received from cmd: 0x%x",CmdID);
    if(mPendingNetworkMsgList.size() == 0)
    {
        qDebug("Cmd ack received but list is empty!!!");
    }
    else
    {
        CChaletNetworkMessage Msg = mPendingNetworkMsgList.takeFirst();
        if(Msg.mMessageID != CmdID)
        {
            qDebug("Inconsistency between active cmd and ack received [received 0x%x] != [cur 0x%x]!!!", Msg.mMessageID,CmdID);
        }
    }

    if(mPendingNetworkMsgList.size() == 0)  //If no message is left pending... schedule status request
    {
        ScheduleChaletStatusRequest();
        mChaletStatusTimer->start(LORA_NORMAL_REQUEST_TIMEOUT);//Next status request will be sent within this timeout
    }
    else
    {
        mChaletStatusTimer->start(500); //wait a little and send the next cmd.
    }

    return RET_OK;
}



int CChaletDevice::ScheduleChaletStatusRequest()
{
   ScheduleChaletCommand(CHALET_GENERAL_STATUS_REQUEST,0,QByteArray());
}

int CChaletDevice::ScheduleChaletCommand(int CmdID, int DataSize, QByteArray Data)
{
    Q_UNUSED(DataSize)

//    if(mChaletMainStatus.mIsOnline == false)
//    {
//        return RET_ERROR;
//    }

//    CChaletNetworkMessage Command(CmdID,Data);
//    mPendingNetworkMsgList.append(Command);

    ScheduleChaletCommand(CmdID,Data);

    return RET_OK;
}

int CChaletDevice::ScheduleChaletCommand(int CmdID, QByteArray Data)
{
//    if(mChaletMainStatus.mIsOnline == false)
//    {
//        return RET_ERROR;
//    }

    if(mChaletMainStatus.mIsOnline == true && CmdID != CHALET_GENERAL_STATUS_REQUEST) //When chalet is online, send command right away if we are not waiting for an answer.
    {
        //if we are in nominal conditions (waiting to send a status request).  Cancel the status request and send the command right away.
        if((mPendingNetworkMsgList.size() == 1) &&
           (mPendingNetworkMsgList.at(0).mMessageID == CHALET_GENERAL_STATUS_REQUEST) &&
            mPendingNetworkMsgList.at(0).PendingResponse == false)
        {
            mPendingNetworkMsgList.clear();
            mChaletStatusTimer->start(10); //Allow some time to breathe
        }

    }

    CChaletNetworkMessage Command(CmdID,Data);
    mPendingNetworkMsgList.append(Command);

    return RET_OK;
}

int CChaletDevice::ResetCommStats()
{
    mLostRequestPercentage = 0.0;
    mTotalNbRequest = 0;
    mNbLostRequest = 0;
    mChaletMainStatus.mTotalNbMasterTxCmds = 0;
    mChaletMainStatus.mMasterLostRequestCount = 0;

    return RET_OK;
}

int CChaletDevice::ComputeCommStats()
{
    mLostRequestPercentage = ((float)mNbLostRequest/(float)mTotalNbRequest)*100;
    mChaletMainStatus.mLostRequestPercentage = mLostRequestPercentage;

  //  qDebug("Percent lost request: %f%%",mLostRequestPercentage);
    return RET_OK;
}
