#include "zigateplugin.h"

using namespace std;

ZigatePlugin zigatePlugin;
SerialManager serialManager;

QHash <QString, ResponseProperties*> responseListIndex
{
    {"004D", nullptr},
    {"8000", nullptr},
    {"8001", nullptr},
    {"8002", nullptr},
    {"8003", nullptr},
    {"8004", nullptr},
    {"8005", nullptr},
    {"8006", nullptr},
    {"8007", nullptr},
    {"8010", nullptr},
    {"8014", nullptr},
    {"8024", nullptr},
    {"8030", nullptr},
    {"8031", nullptr},
    {"8040", nullptr},
    {"8041", nullptr},
    {"8042", nullptr},
    {"8043", nullptr},
    {"8044", nullptr},
    {"8045", nullptr},
    {"8046", nullptr},
    {"802B", nullptr},
    {"802C", nullptr},
    {"8047", nullptr},
    {"8048", nullptr},
    {"804A", nullptr},
    {"804B", nullptr},
    {"804E", nullptr},
    {"8140", nullptr},
    {"8008", nullptr},
    {"8009", nullptr},
    {"8011", nullptr},
    {"8012", nullptr},
    {"8015", nullptr},
    {"8017", nullptr},
    {"8028", nullptr},
    {"8035", nullptr},
    {"8049", nullptr},
    {"8052", nullptr},
    {"8060", nullptr},
    {"8061", nullptr},
    {"8062", nullptr},
    {"8063", nullptr},
    {"8085", nullptr},
    {"8095", nullptr},
    {"80A0", nullptr},
    {"80A1", nullptr},
    {"80A2", nullptr},
    {"80A3", nullptr},
    {"80A4", nullptr},
    {"80A6", nullptr},
    {"80A7", nullptr},
    {"8100", nullptr},
    {"8101", nullptr},
    {"8102", nullptr},
    {"8110", nullptr},
    {"8120", nullptr},
    {"8122", nullptr},
    {"8531", nullptr},
    {"8701", nullptr},
    {"8702", nullptr},
    {"9999", nullptr}
};

QByteArray ZigatePlugin::checksum(QByteArray msgType, QByteArray length, QByteArray datas)
{
    quint16 temp = 0;
    int i;
    QString str;

    str = msgType.mid(0,2);
    temp ^=  str.toInt(nullptr, 16);
    str = msgType.mid(2,2);
    temp ^= str.toInt(nullptr, 16);
    str = length.mid(0,2);
    temp ^= str.toInt(nullptr, 16);
    str = length.mid(2,2);
    temp ^= str.toInt(nullptr, 16);
    for (i=0;i<=(datas.count());i+=2)
    {
        str = datas.mid(i,2);
        temp ^= str.toInt(nullptr, 16);
    }
    return QByteArray::number(temp, 16);
}

QByteArray ZigatePlugin::transcode(QByteArray datas)
{
    QByteArray msg = "";
    int i;
    QByteArray byte;
    if (datas.count()%2 != 0)
    {
        return "-1";
    }
    for (i=0;i<datas.count();i+=2)
    {
        byte = datas.mid(i,2);
        if (byte.toUInt(nullptr, 16) > 15)
        {
            msg += byte;
        }else
        {
            msg.append("02");
            msg.append(QByteArray::number(byte.toUInt(nullptr, 16) ^ 16, 16));
        }
    }
    return msg;
}

QByteArray ZigatePlugin::unTranscode(QByteArray datas)
{
    QByteArray result;
    int i;
    char tmp;

    if (datas.isEmpty())
    {
        return "-1";
    }

    for (i=0;i<=datas.count()-1;i++)
    {
        tmp = datas.at(i);
        if (tmp == 0x02)
        {
            i++;
            result.append(datas[i] ^ 16);
        }else
        {
            //if (tmp != 0 and tmp != 1)
            //{
                result += tmp;
            //}
        }
    }
    cout << result.toHex(' ').toStdString() << endl;
    return result;
}

void ZigatePlugin::sendCmd(QByteArray cmd, QByteArray datas)
{
    QByteArray msg;
    QByteArray len;

    len = QByteArray::number(datas.count()/2, 16).insert(0,"000").right(4);
    msg  = QByteArray::fromHex("01");
    msg += QByteArray::fromHex(transcode(cmd));
    msg += QByteArray::fromHex(transcode(len));
    if (!datas.isEmpty())
    {
        msg += QByteArray::fromHex(checksum(cmd, len, datas));
        msg += QByteArray::fromHex(transcode(datas));
    }else{
        msg += QByteArray::fromHex(checksum(cmd, len, ""));
    }
    msg += QByteArray::fromHex("03");
    serialManager.write2serial(msg);
}

void ZigatePlugin::interpretResult(QByteArray payload)
{
    //uint tab = 0;
    uint length = 0;
    quint8 crctmp = 0;
    uint i = 0;
    //int datasResultLength = 0;
    ResponseProperties * responseObject;
    QByteArray type;
    uint ln;
    quint8 crc;
    QByteArray datas;
    QByteArray quality;
    QByteArray payloadD;
    payloadD = unTranscode(payload);
    if (this->datas.count() >= 1)
    {
        cout << "inserting: " << this->datas.toHex(' ').toStdString() << endl;
        payloadD.insert(0, this->datas);
    }
    length = payloadD.count();

    while(length > 5)
    {
        cout << "Interpreting datas =>" << payload.left(1).toHex(' ').toStdString() << endl;
        cout << "PayloadD : " << payloadD.toHex(' ').toStdString() << endl;
        while(payloadD.left(1).toHex() != "01")
        {
            payloadD.remove(0,1);
        }
        payloadD.remove(0,1);crctmp = 0;
        //type de message
        type = payloadD.mid(0, 2);
        ln = payloadD.mid(2, 2).toHex().toUInt(nullptr, 16);
        if (ln + 6 > length)
        {
            cout << "storing payloadD: " << payloadD.toHex(' ').toStdString() << endl;
            this->datas = payloadD;
            break;
        }else
        {
            if ((unsigned char)type[0] == 128 or (unsigned char)type[0] == 0 or (unsigned char)type[0] == 153)
            {
                crctmp = crctmp ^ payloadD.at(0) ^ payloadD.at(1);
                crctmp = crctmp ^ payloadD.at(2) ^ payloadD.at(3);
                //acquisition du CRC
                crc = payloadD.at(4);
                //datasResult
                datas = "";

                for(i=0;i<ln;i+=1)
                {
                    datas += payloadD.at(5+i);
                    crctmp ^= payloadD.at(5+i);
                }
                quality = payloadD.right(1);

                //datasResultLength = datasResult.count();

                //verification du CRC
                if (crc == crctmp)
                {
                    responseObject = responseListIndex[type.toHex()];
                    if (responseObject->manager != nullptr)
                    {
                        responseObject->code = type;
                        responseObject->datas = datas;
                        //responseObject.properties = responseListIndex[type]->properties
                        responseObject->manager(responseObject);

                    }else
                    {
                        cout << "unknown command or command not implemented :" << type.toHex().toStdString() << endl;
                    }
                }else
                {
                    //tab = -2;
                }
            }else
            {
                cout << "unknown type of datagram type: " << type.toHex().toStdString() << endl;
            }
        }
        payloadD.remove(0,ln+6);
        length = payloadD.count();
        cout << "---------------" << endl;
    }
    //return tab;
}


void defaultManager(ResponseProperties *responseObject)
{
    int nbyte;
    int isValue;
    int offset = 0;
    int i;

    QByteArray datas = responseObject->datas;
    QByteArray result;
    //QByteArray code = responseObject->code.toHex();

    //cout << responseObject->code.toHex().toStdString() << endl;
    int n = responseObject->properties.count();
    QMap<uint, QList<QVariant>> propertyList;
    QMap<uint, QString> propertyDetail;
    QMap<uint,QString> var;
    QList<QVariant> property;
    QMap<uint,QString>::iterator j;
    //int propertyListKey;

    propertyList = responseObject->properties;
    infoOutput(responseObject);
    for(i=0;i<n;i+=1)
    //while (i != zigatePlugin.responseListIndex[code].properties.end())
    {
        property = propertyList.value(i);
        cout << property.at(0).toString().toStdString() << ": ";
        nbyte = property.at(1).toInt();
        isValue = property.at(2).toInt();
        result = datas.mid(offset, nbyte);
        offset += nbyte;
        propertyDetail = responseObject->propertyDetail.value(i);

        switch (isValue)
        {
            case 0:
                cout << result.toHex().toStdString() << endl;
            break;
            case 1:
                cout << result.toHex().toStdString() << endl;
                j = propertyDetail.begin();
                while(j != propertyDetail.end())
                {
                    if (result.toUInt(nullptr,16) & var[0].toUInt(nullptr,16))
                    {
                        cout << var[1].toStdString() << endl;
                    }
                }
            break;
            case 2:
                if (propertyDetail.contains(result.toUInt()))
                {
                    cout << propertyDetail.value(result.toUInt()).toStdString() << endl;
                }else if (propertyDetail.contains(-1))
                {
                    cout << propertyDetail.value(-1).toStdString() << endl;
                }
            break;
            case 3:
                responseObject->propertyManagerList[i](result);
            break;
            case 4:
                cout << "Liste" << endl;
            break;
            case 5:
                cout << result.toStdString() << endl;
            break;
        }
    }
}

void macCapabilityManager(QByteArray mac)
{
    cout << "macCapabilityManger" << mac.toStdString() << endl;
}


/* void deviceAnnounceManager()
{
    infoOutput(responseListIndex(""));
}
*/

void dataIndicationManager(ResponseProperties * response)
{
    infoOutput(response);
}


void clustersListManager(ResponseProperties * response)
{
    infoOutput(response);
}


/*
void attributesListManager()
{
    infoOutput(attributesList);
}

void commandsListManager()
{
    infoOutput(commandsList);
}

void statusManager()
{
    infoOutput(status);
}

void status2Manager()
{

}

void versionListManager()
{
    infoOutput(versionList);
}
// ****************************************************************************
//   0 - Off
//   1 - On
// *******************************************************************************

void permitJoinManager()
{
    infoOutput(permitJoin);
}

void networkJoinedFormedManager()
{
    infoOutput(networkJoinedFormed);
}
*/


void deviceListManager(ResponseProperties * response)
{
    infoOutput(response);
    // **************************************************************
    // <device list – data each entry is 13 bytes>
    //			<ID: uint8_t>
    //          <Short address: uint16_t>
    //          <IEEE address: uint64_t>
    //          <Power source: bool_t> 0 – battery 1- AC power
    //          <LinkQuality : uint8_t> 1-255
    // **************************************************************
 }


/*
void bindResponseManager()
{
    infoOutput(bindResponse);
}

void unbindResponseManager()
{
    infoOutput(unbindResponse);
}
*/


void networkAddressManager(ResponseProperties * response)
{
    infoOutput(response);
}

void iEEEAddressManager(ResponseProperties * response)
{
    infoOutput(response);
}

void nodeDescriptorManager(ResponseProperties * response)
{
    infoOutput(response);
}

void simpleDescriptorManager(ResponseProperties * response)
{
    infoOutput(response);
}


/*
void powerDescriptorManager()
{
    infoOutput(powerDescriptor);
}

void activeEndpointManager()
{
    infoOutput(activeEndpoint);
}

void matchDescriptorManager()
{
    infoOutput(matchDescriptor);
}

void userDescriptorNotifyManager()
{
    infoOutput(userDescriptorNotify);
}

void userDescriptorManager()
{
    infoOutput(userDescriptor);
}

void complexDescriptorManager()
{
    infoOutput(complexDescriptor);
}

void managementLeaveManager()
{
    infoOutput(managementLeave);
}

void leaveIndicationManager()
{
    infoOutput(leaveIndication);
}

void managementNetworkUpdateManager()
{
    infoOutput(managementNetworkUpdate);
}

void systemServerDiscoveryManager()
{
    infoOutput(systemServerDiscovery);
}
// ********************************* //
// Bitmask according to spec".       //
// ********************************* //

void managementLQIManager()
{
    infoOutput(managementLQI);
}

void attributeDiscoveryManager()
{
    infoOutput(attributeDiscovery);
}
*/
