#include "OutilModbus.h"
#include "ProtocolDefs.h"
#include <QDateTime>

COutilModbus::COutilModbus(QObject *parent) :
    QObject(parent)
{
    mModbusSEIEmulator = new CModbusSlave(&mSEIModbusRepo);
    mModbusCCEmulator = new CModbusMaster(&mCCModbusRepo);
    w = new MainWindow(0,this);

    mAutoRequestTimer = new QTimer();
    mAutoRequestTimer->setSingleShot(false);
    connect(mAutoRequestTimer,SIGNAL(timeout()),this,SLOT(AutoRequestTimeExpired()));

    mWatchdogEnabled = true;
    mWatchdog = 0;
    mWatchdogTimer = new QTimer();
    mWatchdogTimer->setSingleShot(false);
    connect(mWatchdogTimer,SIGNAL(timeout()),this,SLOT(WatchdogTimerExpired()));
    mWatchdogTimer->setInterval(MODBUS_CC_WATCHDOG_TIMEOUT);

    mSEIWatchdogTimer = new QTimer();
    mSEIWatchdogTimer->setSingleShot(false);
    mSEIWatchdogTimer->setInterval(MODBUS_SEI_WATCHDOG_TIMEOUT);
    connect(mSEIWatchdogTimer,SIGNAL(timeout()),this,SLOT(SEIWatchdogExpired()));

    mZTAlarmGenerator = new CZTAlarmGenerator;
    mZTAlarmGenerator->mAlarmGeneratorPageHandle = w->mZTAlarmGeneratorPage;
    w->mZTAlarmGeneratorPage->mProgramHandle = mZTAlarmGenerator;

    mHistorienMaster = new CModbusHistorienMaster(&mHistorienRepo);
    w->mHistorianPage->mProgramHandle = mHistorienMaster;

    mSEIWatchdogValue = 0;
}

COutilModbus::~COutilModbus()
{
    delete mModbusSEIEmulator;
    delete mAutoRequestTimer;
    delete mWatchdogTimer;
    delete w;
    delete mSEIWatchdogTimer;
    delete mZTAlarmGenerator;
}

int COutilModbus::Start()
{
    w->resize(1024,768);
    w->show();

    mModbusPageHandle = w->mModbusPage;
    mSEIModbusPageHandle = w->mSEIModbusPage;
    mHistorienMaster->mPageHandle = w->mHistorianPage;

    mCCModbusRepo.AddHRDataMap(2000,128);


    //mModbusSEIEmulator->StartSlaveServer(502);
    mModbusSEIEmulator->SetServerPort(502);

    mSEIModbusRepo.AddHRDataMap(2000,128);
    mSEIModbusRepo.AddHRDataMap(3000,128);

    connect(mModbusCCEmulator,SIGNAL(ModbusMasterConnected(CModbusMaster*)),this,SLOT(ModbusMasterConnected(CModbusMaster*)));
    connect(mModbusCCEmulator,SIGNAL(ModbusMasterDisconnected(CModbusMaster*)),this,SLOT(ModbusMasterDisconnected(CModbusMaster*)));
    connect(mModbusCCEmulator,SIGNAL(ModbusMasterRepositoryUpdated()),this,SLOT(ModbusMasterRepositoryUpdated()));
    connect(mModbusCCEmulator,SIGNAL(ModbusRX()),mModbusPageHandle,SLOT(ModbusActivity()));

    connect(mModbusSEIEmulator,SIGNAL(ModbusSlaveConnected(CModbusSlave*)),this,SLOT(SEIModbusConnected(CModbusSlave*)));
    connect(mModbusSEIEmulator,SIGNAL(ModbusSlaveDisconnected(CModbusSlave*)),this,SLOT(SEIModbusDisconnected(CModbusSlave*)));
    connect(mModbusSEIEmulator,SIGNAL(ModbusSlaveRepoUpdated()),this,SLOT(SEIRepositoryUpdated()));

    mHistorienRepo.AddHRDataMap(2000,128);

    return 1;
}

void COutilModbus::ModbusMasterConnected(CModbusMaster *Master)
{
    mModbusPageHandle->MasterConnected();
    mAutoRequestTimer->stop();
    if(mWatchdogEnabled)
    {
        mWatchdogTimer->start();
    }
}

void COutilModbus::ModbusMasterDisconnected(CModbusMaster *Master)
{
    mModbusPageHandle->MasterDisconnected();
    mWatchdogTimer->stop();
    mAutoRequestTimer->stop();
}

void COutilModbus::ModbusMasterRepositoryUpdated()
{
    if(UpdateZTInfo(&mCurZTInfo,&mCCModbusRepo) == RET_OK)
    {
        mModbusPageHandle->UpdateZTInfo(&mCurZTInfo);
    }
}

void COutilModbus::SEIModbusConnected(CModbusSlave *Slave)
{
    mSEIModbusPageHandle->SetConnected(true);
}

void COutilModbus::SEIModbusDisconnected(CModbusSlave *Slave)
{
    mSEIModbusPageHandle->SetConnected(false);
}

void COutilModbus::SEIRepositoryUpdated()
{
    if(UpdateZTInfo(&mCurZTInfo, &mSEIModbusRepo) == RET_OK)
    {
        mSEIModbusPageHandle->UpdateZTInfo(&mCurZTInfo);
    }
}

int COutilModbus::ConnectCCToZTButtonPressed(QString IP, int Port)
{
    mModbusCCEmulator->ConnectToSlave(IP,Port);
    return 1;
}

int COutilModbus::DisconnectCCFromZTButtonPressed()
{
    return mModbusCCEmulator->DisconnectFromSlave();
}

int COutilModbus::SetCCReqDelay(int delay)
{
    return 1;
}

int COutilModbus::SendMasterReadCommand()
{
    return mModbusCCEmulator->ReadModbusRegisters();
}

int COutilModbus::UpdateZTInfo(CZTInfo *DataStruct, CModbusRepository *SourceRepo)
{
    bool OK = false;
   // quint16 data = mCCModbusRepo.GetSingleReg(MODBUS_ZT1_ALARM_DATA_BASE_REG_ADD,&OK);
    quint16 data = SourceRepo->GetSingleReg(MODBUS_ZT1_ALARM_DATA_BASE_REG_ADD,&OK);

    if(OK == true)
    {

       DataStruct->mZT1PPIAlarmFlag1 = (data & ZT1_PP_INT_FLAG_MASK_1) != 0;
       DataStruct->mZT1PPIAlarmFlag2 = (data & ZT1_PP_INT_FLAG_MASK_2) != 0;
       DataStruct->mZT1PPIAlarmFlag3 = (data & ZT1_PP_INT_FLAG_MASK_3) != 0;
       DataStruct->mZT1PPIAlarmFlag4 = (data & ZT1_PP_INT_FLAG_MASK_4) != 0;
       DataStruct->mZT1PPEAlarmFlag1 = (data & ZT1_PP_EXT_FLAG_MASK_1) != 0;
       DataStruct->mZT1PPEAlarmFlag2 = (data & ZT1_PP_EXT_FLAG_MASK_2) != 0;
       DataStruct->mZT1PPEAlarmFlag3 = (data & ZT1_PP_EXT_FLAG_MASK_3) != 0;
       DataStruct->mZT1PPEAlarmFlag4 = (data & ZT1_PP_EXT_FLAG_MASK_4) != 0;
       DataStruct->mZT1PGAlarmFlag1 = (data & ZT1_PG_FLAG_MASK_1) != 0;
       DataStruct->mZT1PGAlarmFlag2 = (data & ZT1_PG_FLAG_MASK_2) != 0;
       DataStruct->mZT1PGAlarmFlag3 = (data & ZT1_PG_FLAG_MASK_3) != 0;
       DataStruct->mZT1PGAlarmFlag4 = (data & ZT1_PG_FLAG_MASK_4) != 0;
       DataStruct->mZT1FNAlarmFlag1 = (data & ZT1_FN_FLAG_MASK_1) != 0;
       DataStruct->mZT1FNAlarmFlag2 = (data & ZT1_FN_FLAG_MASK_2) != 0;
       DataStruct->mZT1FNAlarmFlag3 = (data & ZT1_FN_FLAG_MASK_3) != 0;
       DataStruct->mZT1FNAlarmFlag4 = (data & ZT1_FN_FLAG_MASK_4) != 0;

    }
    else
    {
        qDebug("GetSingleReg failed in UpdateZTInfo (MODBUS_ZT1_ALARM_DATA_BASE_REG_ADD)");
        return RET_ERROR;
    }

    QList<qint16> RankData;
    RankData = SourceRepo->GetRegs(MODBUS_ZT1_ALARM_RANKS_BASE_ADD,16,&OK);
    if(OK == true)
    {
        DataStruct->mZT1Ranks = RankData;
    }
    else
    {
        qDebug("GetSingleReg failed in UpdateZTInfo (MODBUS_ZT1_ALARM_RANKS_BASE_ADD)");
        return RET_ERROR;
    }

    data = SourceRepo->GetSingleReg(MODBUS_ZT2_ALARM_DATA_BASE_REG_ADD,&OK);
    if(OK == true)
    {
        DataStruct->mZT2PPIAlarmFlag1 = (data & ZT2_PP_INT_FLAG_MASK_1) != 0;
        DataStruct->mZT2PPIAlarmFlag2 = (data & ZT2_PP_INT_FLAG_MASK_2) != 0;
        DataStruct->mZT2PPIAlarmFlag3 = (data & ZT2_PP_INT_FLAG_MASK_3) != 0;
        DataStruct->mZT2PPIAlarmFlag4 = (data & ZT2_PP_INT_FLAG_MASK_4) != 0;
        DataStruct->mZT2PPEAlarmFlag1 = (data & ZT2_PP_EXT_FLAG_MASK_1) != 0;
        DataStruct->mZT2PPEAlarmFlag2 = (data & ZT2_PP_EXT_FLAG_MASK_2) != 0;
        DataStruct->mZT2PPEAlarmFlag3 = (data & ZT2_PP_EXT_FLAG_MASK_3) != 0;
        DataStruct->mZT2PPEAlarmFlag4 = (data & ZT2_PP_EXT_FLAG_MASK_4) != 0;
    }
    else
    {
        qDebug("GetSingleReg failed in UpdateZTInfo (MODBUS_ZT2_ALARM_DATA_BASE_REG_ADD)");
        return RET_ERROR;
    }

    RankData.clear();
    RankData = SourceRepo->GetRegs(MODBUS_ZT2_ALARM_RANKS_BASE_ADD,8,&OK);
    if(OK == true)
    {
        DataStruct->mZT2Ranks = RankData;
    }
    else
    {
        qDebug("GetSingleReg failed in UpdateZTInfo (MODBUS_ZT2_ALARM_RANKS_BASE_ADD)");
        return RET_ERROR;
    }


    data = SourceRepo->GetSingleReg(MODBUS_MISC_DATA_BASE_REG_ADD,&OK);

    if(OK == true)
    {

       DataStruct->mZT1V00AlarmFlag = (data & ZT1_V00_ALARM_FLAG_MASK) != 0;
       DataStruct->mZT1PEQ1AlarmFlag = (data & ZT1_PEQ1_ALARM_FLAG_MASK) != 0;
       DataStruct->mZT2V02AlarmFlag = (data & ZT2_V02_ALARM_FLAG_MASK) != 0;
       DataStruct->mZT2PEQ2AlarmFlag = (data & ZT2_PEQ2_ALARM_FLAG_MASK) != 0;
       DataStruct->mITI10_12Flag = (data & ZT1_ALARM_ITI_FLAG_MASK) != 0;
    }
    else
    {
        qDebug("GetSingleReg failed in UpdateZTInfo (MODBUS_PANNES_DATA_BASE_REG_ADD)");
        return RET_ERROR;
    }

    DataStruct->mWatchdogZT = SourceRepo->GetSingleReg(MODBUS_ZT_WATCHDOG_REG_ADD,&OK);

    return RET_OK;
}

int COutilModbus::SendAN1Request()  //FCYCLE
{
    bool OK = false;
    quint16 AN1;
    quint16 CurValue = mCCModbusRepo.GetSingleReg(MODBUS_CC_AN_BASE_REG_ADD,&OK);
    AN1 = CurValue & MODBUS_CC_FCYCLE_ZT1_FLAG_MASK;
    if(OK == true)
    {
        if(AN1 == 0)
        {
            CurValue |= MODBUS_CC_FCYCLE_ZT1_FLAG_MASK;
            mCCModbusRepo.WriteSingleReg(MODBUS_CC_AN_BASE_REG_ADD,CurValue);
            qDebug("Setting FCYCLE in repo");
        }
        else
        {
           CurValue &= ~MODBUS_CC_FCYCLE_ZT1_FLAG_MASK;
           mCCModbusRepo.WriteSingleReg(MODBUS_CC_AN_BASE_REG_ADD,CurValue);
           qDebug("Clearing FCYCLE in repo");
        }
        mModbusCCEmulator->SendAN1ToZT();
    }
    return RET_OK;
}

int COutilModbus::SendAN2Request()
{
    bool OK = false;
    quint16 AN2;
    quint16 CurValue = mCCModbusRepo.GetSingleReg(MODBUS_CC_AN_BASE_REG_ADD,&OK);
    AN2 = CurValue & MODBUS_CC_FCYCLE_ZT2_FLAG_MASK;
    if(OK == true)
    {
        if(AN2 == 0)
        {
            CurValue |= MODBUS_CC_FCYCLE_ZT2_FLAG_MASK;
            mCCModbusRepo.WriteSingleReg(MODBUS_CC_AN_BASE_REG_ADD,CurValue);
            qDebug("Setting FCYCLE in repo");
        }
        else
        {
           CurValue &= ~MODBUS_CC_FCYCLE_ZT2_FLAG_MASK;
           mCCModbusRepo.WriteSingleReg(MODBUS_CC_AN_BASE_REG_ADD,CurValue);
           qDebug("Clearing FCYCLE in repo");
        }
        mModbusCCEmulator->SendAN2ToZT();
    }
    return RET_OK;
}

int COutilModbus::SendZTCZT1Request()
{
    bool OK = false;
    quint16 ZTC1;
    quint16 CurValue = mCCModbusRepo.GetSingleReg(MODBUS_CC_AN_BASE_REG_ADD,&OK);
    ZTC1 = CurValue & MODBUS_CC_FCYCLE_ZT1_FLAG_MASK;
    if(OK == true)
    {
        if(ZTC1 == 0)
        {
            CurValue |= MODBUS_CC_FCYCLE_ZT1_FLAG_MASK;
            mCCModbusRepo.WriteSingleReg(MODBUS_CC_AN_BASE_REG_ADD,CurValue);
            qDebug("Setting ZTC1 in repo");
        }
        else
        {
           CurValue ^= MODBUS_CC_FCYCLE_ZT1_FLAG_MASK;
           mCCModbusRepo.WriteSingleReg(MODBUS_CC_AN_BASE_REG_ADD,CurValue);
           qDebug("Clearing ZTC1 in repo");
        }
        mModbusCCEmulator->SendAN2ToZT();
    }
    return RET_OK;
}

int COutilModbus::SendZTCZT2Request()
{
    bool OK = false;
    quint16 ZTC2;
    quint16 CurValue = mCCModbusRepo.GetSingleReg(MODBUS_CC_AN_BASE_REG_ADD,&OK);
    ZTC2 = CurValue & MODBUS_CC_FCYCLE_ZT2_FLAG_MASK;
    if(OK == true)
    {
        if(ZTC2 == 0)
        {
            CurValue |= MODBUS_CC_FCYCLE_ZT2_FLAG_MASK;
            mCCModbusRepo.WriteSingleReg(MODBUS_CC_AN_BASE_REG_ADD,CurValue);
            qDebug("Setting ZTC2 in repo");
        }
        else
        {
           CurValue ^= MODBUS_CC_FCYCLE_ZT2_FLAG_MASK;
           mCCModbusRepo.WriteSingleReg(MODBUS_CC_AN_BASE_REG_ADD,CurValue);
           qDebug("Clearing ZTC2 in repo");
        }
        mModbusCCEmulator->SendAN2ToZT();
    }
    return RET_OK;
}

int COutilModbus::SetAutoRequestMode(bool Auto, int Delay)
{
    if(Auto == false)
    {
        mAutoRequestTimer->stop();
    }
    else
    {
        mAutoRequestTimer->setInterval(Delay);
        mAutoRequestTimer->start();
    }
    return RET_OK;
}

void COutilModbus::AutoRequestTimeExpired()
{
    SendMasterReadCommand();
}

int COutilModbus::EnableWatchdog(bool Enabled)
{
    mWatchdogEnabled = Enabled;
    if(Enabled)
    {
        mWatchdogTimer->start();
    }
    else
    {
        mWatchdogTimer->stop();
    }
    return RET_OK;
}

void COutilModbus::WatchdogTimerExpired()
{
    if(mWatchdog == 0)
    {
        mWatchdog = 1;
    }
    else
    {
        mWatchdog = 0;
    }

    mCCModbusRepo.WriteSingleReg(MODBUS_CC_WATCHDOG_BASE_REG_ADD,mWatchdog);
  //  qDebug("Watchdog: %d",mWatchdog);
    mModbusCCEmulator->SendWatchdog();
}

int COutilModbus::SendDateTime()
{
    QList<qint16> DateTimeData;
    QDateTime CurDateTime = QDateTime::currentDateTime().toUTC();
    qint16 tmpWord;
    qint8 tmpByte;

    DateTimeData.append(MODBUS_CC_CLK_UPDATE_FLAG_MASK);
    tmpByte = DecToBCDByte((qint8)CurDateTime.time().second());
    tmpWord = (qint16)tmpByte << 8;

    DateTimeData.append(tmpWord);

    tmpByte = DecToBCDByte((qint8)CurDateTime.time().hour());
    tmpWord = (qint16)tmpByte << 8;
    tmpByte = DecToBCDByte((qint8)CurDateTime.time().minute());
    tmpWord += tmpByte;
    DateTimeData.append(tmpWord);

    tmpByte = DecToBCDByte((qint8)CurDateTime.date().month());
    tmpWord = (qint16)tmpByte << 8;
    tmpByte = DecToBCDByte((qint8)CurDateTime.date().day());
    tmpWord += tmpByte;
    DateTimeData.append(tmpWord);

    tmpWord = DecToBCDWord((qint16)CurDateTime.date().year());
    DateTimeData.append(tmpWord);

    mCCModbusRepo.WriteMultipleRegs(MODBUS_CC_CLK_UPDATE_BASE_REG_ADD,DateTimeData);
    mModbusCCEmulator->SendDateTime();
    return RET_OK;
}

int COutilModbus::SendTrainInfo(QList<qint16> TrainInfo)
{

    mCCModbusRepo.WriteMultipleRegs(MODBUS_CC_ZT1_TRAIN_TYPE_REG_ADD,TrainInfo);
    mModbusCCEmulator->SendTrainData();

    return RET_OK;
}

int COutilModbus::SendInhibitZT(qint16 InhibZT1, qint16 InhibZT2)
{
    bool OK = false;

    qint16 Register = mCCModbusRepo.GetSingleReg(MODBUS_CC_AN_BASE_REG_ADD,&OK);
    if(OK)
    {
        if(InhibZT1 == 0)
        {
            Register &= ~MODBUS_CC_INHIBIT_ZT1_FLAG_MASK;
        }
        else
        {
            Register |= MODBUS_CC_INHIBIT_ZT1_FLAG_MASK;
        }
        if(InhibZT2 == 0)
        {
            Register &= ~MODBUS_CC_INHIBIT_ZT2_FLAG_MASK;
        }
        else
        {
            Register |= MODBUS_CC_INHIBIT_ZT2_FLAG_MASK;
        }
        mCCModbusRepo.WriteSingleReg(MODBUS_CC_AN_BASE_REG_ADD,Register);
        mModbusCCEmulator->SendZTInhibitionData();
        return RET_OK;
    }

    return RET_ERROR;
}
quint8 COutilModbus::DecToBCDByte(const quint8 byte)
{
    quint8 out = 0;

    out = ((byte/10) << 4) + (byte%10);


    return out;

}

quint16 COutilModbus::DecToBCDWord(const quint16 word)
{
    quint16 out = 0;
    quint16 temp = 0;

    out = word % 10;
    temp = (((word /10) % 10) << 4);
    out += temp;
    temp = (((word / 100) % 10) << 8);
    out += temp;
    temp = (((word / 1000) % 10) << 12);
    out += temp;

    return out;

}

int COutilModbus::ToggleSEIServerState()
{
    return mModbusSEIEmulator->ToggleServerState();
}

bool COutilModbus::IsSEIServerOpened()
{
    return mModbusSEIEmulator->IsSlaveServerOpened();
}

int COutilModbus::SEIWatchdogEnable(bool Enabled)
{
    if(Enabled)
    {
        mSEIWatchdogTimer->start();
    }
    else
    {
        mSEIWatchdogTimer->stop();
    }

    return RET_OK;
}

void COutilModbus::SEIWatchdogExpired()
{
    if(mSEIWatchdogValue == 0)
    {
        mSEIWatchdogValue = SEI_MODBUS_SEI_WATCHDOG_MASK;
    }
    else
    {
        mSEIWatchdogValue = 0;
    }

    mSEIModbusRepo.WriteSingleReg(SEI_MODBUS_SEI_WATCHDOG_REG,mSEIWatchdogValue);
}

int COutilModbus::SEIAN1Toggle(bool Enabled)
{
    qint16 temp;
    bool OK;
    temp = mSEIModbusRepo.GetSingleReg(SEI_MODBUS_SEI_ALARMS_RESET_REG,&OK);

    if(OK == false)
    {
        qDebug("SEIAN1Toggle(): logic error in modbus table assignment.  Can't read address %d",SEI_MODBUS_SEI_ALARMS_RESET_REG);
        return RET_ERROR;
    }

    if(Enabled)
    {
        temp |= SEI_MODBUS_SEI_ZT1_ALARM_RESET_MASK;
    }
    else
    {
        temp &= ~SEI_MODBUS_SEI_ZT1_ALARM_RESET_MASK;
    }

    mSEIModbusRepo.WriteSingleReg(SEI_MODBUS_SEI_ALARMS_RESET_REG,temp);

    return RET_OK;
}

int COutilModbus::SEIAN2Toggle(bool Enabled)
{
    qint16 temp;
    bool OK;
    temp = mSEIModbusRepo.GetSingleReg(SEI_MODBUS_SEI_ALARMS_RESET_REG,&OK);

    if(OK == false)
    {
        qDebug("SEIAN2Toggle(): logic error in modbus table assignment.  Can't read address %d",SEI_MODBUS_SEI_ALARMS_RESET_REG);
        return RET_ERROR;
    }

    if(Enabled)
    {
        temp |= SEI_MODBUS_SEI_ZT2_ALARM_RESET_MASK;
    }
    else
    {
        temp &= ~SEI_MODBUS_SEI_ZT2_ALARM_RESET_MASK;
    }

    mSEIModbusRepo.WriteSingleReg(SEI_MODBUS_SEI_ALARMS_RESET_REG,temp);

    return RET_OK;
}
