// SPDX-License-Identifier: LGPL-3.0-or-later

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* nymea-zigbee
* Zigbee integration module for nymea
*
* Copyright (C) 2013 - 2024, nymea GmbH
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
*
* This file is part of nymea-zigbee.
*
* nymea-zigbee is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* nymea-zigbee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with nymea-zigbee. If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "zigbeedatatype.h"
#include "zigbeeutils.h"

#include <QtGlobal>
#include <QDataStream>
#include <qmath.h>

ZigbeeDataType::ZigbeeDataType()
{
    setDataType(Zigbee::NoData);
}

ZigbeeDataType::ZigbeeDataType(const ZigbeeDataType &other)
{
    setDataType(other.dataType());
    m_data = other.data();
}

ZigbeeDataType::ZigbeeDataType(Zigbee::DataType dataType, const QByteArray &data):
    m_data(data)
{
    setDataType(dataType);

    // TODO: verify data length and consistency


    /*
    switch (dataType) {
    case Zigbee::NoData:
        break;
    case Zigbee::Data8:
        break;
    case Zigbee::Data16:
        break;
    case Zigbee::Data24:
        break;
    case Zigbee::Data32:
        break;
    case Zigbee::Data40:
        break;
    case Zigbee::Data48:
        break;
    case Zigbee::Data56:
        break;
    case Zigbee::Data64:
        break;
    case Zigbee::Bool:
        break;
    case Zigbee::BitMap8:
        break;
    case Zigbee::BitMap16:
        break;
    case Zigbee::BitMap24:
        break;
    case Zigbee::BitMap32:
        break;
    case Zigbee::BitMap40:
        break;
    case Zigbee::BitMap48:
        break;
    case Zigbee::BitMap56:
        break;
    case Zigbee::BitMap64:
        break;
    case Zigbee::Uint8:
        break;
    case Zigbee::Uint16:
        break;
    case Zigbee::Uint24:
        break;
    case Zigbee::Uint32:
        break;
    case Zigbee::Uint40:
        break;
    case Zigbee::Uint48:
        break;
    case Zigbee::Uint56:
        break;
    case Zigbee::Uint64:
        break;
    case Zigbee::Int8:
        break;
    case Zigbee::Int16:
        break;
    case Zigbee::Int24:
        break;
    case Zigbee::Int32:
        break;
    case Zigbee::Int40:
        break;
    case Zigbee::Int48:
        break;
    case Zigbee::Int56:
        break;
    case Zigbee::Int64:
        break;
    case Zigbee::Enum8:
        break;
    case Zigbee::Enum16:
        break;
    case Zigbee::FloatSemi:
        break;
    case Zigbee::FloatSingle:
        break;
    case Zigbee::FloatDouble:
        break;
    case Zigbee::OctetString:
        break;
    case Zigbee::CharString:
        break;
    case Zigbee::LongOctetString:
        break;
    case Zigbee::LongCharString:
        break;
    case Zigbee::Array:
        break;
    case Zigbee::Structure:
        break;
    case Zigbee::Set:
        break;
    case Zigbee::Bag:
        break;
    case Zigbee::TimeOfDay:
        break;
    case Zigbee::Date:
        break;
    case Zigbee::UtcTime:
        break;
    case Zigbee::Cluster:
        break;
    case Zigbee::Attribute:
        break;
    case Zigbee::BacnetId:
        break;
    case Zigbee::IeeeAddress:
        break;
    case Zigbee::BitKey128:
        break;
    case Zigbee::Unknown:
        break;
    }
    */

}

ZigbeeDataType::ZigbeeDataType(quint8 value)
{
    setDataType(Zigbee::Uint8);
    m_data.clear();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QDataStream stream(&m_data, QDataStream::WriteOnly);
#else
    QDataStream stream(&m_data, QIODevice::WriteOnly);
#endif
    stream.setByteOrder(QDataStream::LittleEndian);
    stream << value;
}

ZigbeeDataType::ZigbeeDataType(quint16 value)
{
    setDataType(Zigbee::Uint16);
    m_data.clear();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QDataStream stream(&m_data, QDataStream::WriteOnly);
#else
    QDataStream stream(&m_data, QIODevice::WriteOnly);
#endif
    stream.setByteOrder(QDataStream::LittleEndian);
    stream << value;
}

ZigbeeDataType::ZigbeeDataType(quint32 value, Zigbee::DataType dataType)
{
    Q_ASSERT_X(dataType == Zigbee::Uint24 || dataType == Zigbee::Uint32, "ZigbeeDataType", "invalid data type for quint32 constructor");
    setDataType(dataType);
    m_data.clear();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QDataStream stream(&m_data, QDataStream::WriteOnly);
#else
    QDataStream stream(&m_data, QIODevice::WriteOnly);
#endif
    stream.setByteOrder(QDataStream::LittleEndian);
    stream << value;

    if (m_dataType == Zigbee::Uint24) {
        m_data.chop(1);
    }
}

ZigbeeDataType::ZigbeeDataType(quint64 value, Zigbee::DataType dataType)
{
    Q_ASSERT_X(dataType == Zigbee::Uint40 || dataType == Zigbee::Uint48 || dataType == Zigbee::Uint56 || dataType == Zigbee::Uint64, "ZigbeeDataType", "invalid data type for quint64 constructor");
    setDataType(dataType);
    m_data.clear();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QDataStream stream(&m_data, QDataStream::WriteOnly);
#else
    QDataStream stream(&m_data, QIODevice::WriteOnly);
#endif
    stream.setByteOrder(QDataStream::LittleEndian);
    stream << value;

    if (m_dataType == Zigbee::Uint40) {
        m_data.chop(3);
    } else if (m_dataType == Zigbee::Uint48) {
        m_data.chop(2);
    } else if (m_dataType == Zigbee::Uint56) {
        m_data.chop(1);
    }
}

ZigbeeDataType::ZigbeeDataType(qint8 value)
{
    setDataType(Zigbee::Int8);
    m_data.clear();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QDataStream stream(&m_data, QDataStream::WriteOnly);
#else
    QDataStream stream(&m_data, QIODevice::WriteOnly);
#endif
    stream.setByteOrder(QDataStream::LittleEndian);
    stream << value;
}

ZigbeeDataType::ZigbeeDataType(qint16 value)
{
    setDataType(Zigbee::Int16);
    m_data.clear();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QDataStream stream(&m_data, QDataStream::WriteOnly);
#else
    QDataStream stream(&m_data, QIODevice::WriteOnly);
#endif
    stream.setByteOrder(QDataStream::LittleEndian);
    stream << value;
}

ZigbeeDataType::ZigbeeDataType(qint32 value, Zigbee::DataType dataType)
{
    Q_ASSERT_X(dataType == Zigbee::Int24 || dataType == Zigbee::Int32, "ZigbeeDataType", "invalid data type for qint32 constructor");
    setDataType(dataType);
    m_data.clear();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QDataStream stream(&m_data, QDataStream::WriteOnly);
#else
    QDataStream stream(&m_data, QIODevice::WriteOnly);
#endif
    stream.setByteOrder(QDataStream::LittleEndian);
    stream << value;

    if (m_dataType == Zigbee::Int24) {
        m_data.chop(1);
    }
}

ZigbeeDataType::ZigbeeDataType(qint64 value, Zigbee::DataType dataType)
{
    Q_ASSERT_X(dataType == Zigbee::Int40 || dataType == Zigbee::Int48 || dataType == Zigbee::Int56 || dataType == Zigbee::Int64, "ZigbeeDataType", "invalid data type for qint64 constructor");
    setDataType(dataType);
    m_data.clear();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QDataStream stream(&m_data, QDataStream::WriteOnly);
#else
    QDataStream stream(&m_data, QIODevice::WriteOnly);
#endif
    stream.setByteOrder(QDataStream::LittleEndian);
    stream << value;

    if (m_dataType == Zigbee::Int40) {
        m_data.chop(3);
    } else if (m_dataType == Zigbee::Int48) {
        m_data.chop(2);
    } else if (m_dataType == Zigbee::Int56) {
        m_data.chop(1);
    }
}

ZigbeeDataType::ZigbeeDataType(bool value)
{
    setDataType(Zigbee::Bool);
    m_data.clear();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QDataStream stream(&m_data, QDataStream::WriteOnly);
#else
    QDataStream stream(&m_data, QIODevice::WriteOnly);
#endif
    stream.setByteOrder(QDataStream::LittleEndian);
    stream << (value ? static_cast<quint8>(1) : static_cast<quint8>(0));
}

ZigbeeDataType::ZigbeeDataType(const QString &value, Zigbee::DataType dataType)
{
    Q_ASSERT_X(dataType == Zigbee::OctetString || dataType == Zigbee::CharString || dataType == Zigbee::LongOctetString || dataType == Zigbee::LongCharString, "ZigbeeDataType", "invalid data type for QString constructor");
    setDataType(dataType);
    m_data.clear();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QDataStream stream(&m_data, QDataStream::WriteOnly);
#else
    QDataStream stream(&m_data, QIODevice::WriteOnly);
#endif
    stream.setByteOrder(QDataStream::LittleEndian);

    if (dataType == Zigbee::OctetString || dataType == Zigbee::CharString) {
        Q_ASSERT_X(value.length() <= 255, "ZigbeeDataType", "the given string is too long for this datatype. Maximum size is 255");
        stream << static_cast<quint8>(value.length());
        for (int i = 0; i < value.length(); i++) {
            stream << static_cast<quint8>(value.at(i).toLatin1());
        }
    } else if (dataType == Zigbee::LongOctetString || dataType == Zigbee::LongCharString) {
        Q_ASSERT_X(value.length() <= 0xffff, "ZigbeeDataType", "the given string is too long for this datatype. Maximum size is 0xffff");
        stream << static_cast<quint16>(value.length());
        for (int i = 0; i < value.length(); i++) {
            stream << static_cast<quint16>(value.at(i).toLatin1());
        }
    }
}

quint8 ZigbeeDataType::toUInt8(bool *ok) const
{
    if (ok) *ok = true;
    if (m_data.size() != 1) {
        if (ok) *ok = false;
        return 0;
    }

    return static_cast<quint8>(m_data.at(0));
}

quint16 ZigbeeDataType::toUInt16(bool *ok) const
{
    if (ok) *ok = true;

    quint16 value = 0;
    if (m_data.size() != 2) {
        if (ok) *ok = false;
        return value;
    }

    QDataStream stream(m_data);
    stream.setByteOrder(QDataStream::LittleEndian);
    stream >> value;
    return value;
}

quint32 ZigbeeDataType::toUInt32(bool *ok) const
{
    if (ok) *ok = true;
    quint32 value = 0;

    // Verify the data type
    if (m_dataType != Zigbee::Uint24 && m_dataType != Zigbee::Uint32) {
        if (ok) *ok = false;
        return value;
    }

    // Make sure there is enought data
    if (m_data.size() != 3 && m_data.size() != 4) {
        if (ok) *ok = false;
        return value;
    }

    if (m_data.size() == 3) {
        // Make it 32 bit for converting
        QByteArray convertedData(m_data);
        convertedData.append(static_cast<char>(0));
        QDataStream stream(convertedData);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
    } else {
        QDataStream stream(m_data);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
    }

    return value;
}

quint64 ZigbeeDataType::toUInt64(bool *ok) const
{
    if (ok) *ok = true;
    quint64 value = 0;

    switch (m_dataType) {
    case Zigbee::Uint40: {
        if (m_data.size() != 5) {
            if (ok) *ok = false;
            break;
        }

        // Make it 64 bit for converting
        QByteArray convertedData(m_data);
        for (int i = 0; i < 3; i++)
            convertedData.append(static_cast<char>(0));

        QDataStream stream(convertedData);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
        break;
    }
    case Zigbee::Uint48: {
        if (m_data.size() != 6) {
            if (ok) *ok = false;
            break;
        }

        // Make it 64 bit for converting
        QByteArray convertedData(m_data);
        for (int i = 0; i < 2; i++)
            convertedData.append(static_cast<char>(0));

        QDataStream stream(convertedData);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
        break;
    }
    case Zigbee::Uint56: {
        if (m_data.size() != 7) {
            if (ok) *ok = false;
            break;
        }

        // Make it 64 bit for converting
        QByteArray convertedData(m_data);
        convertedData.append(static_cast<char>(0));
        QDataStream stream(convertedData);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
        break;
    }
    case Zigbee::Uint64: {
        if (m_data.size() != 8) {
            if (ok) *ok = false;
            break;
        }

        QDataStream stream(m_data);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
        break;
    }
    default:
        if (ok) *ok = false;
        break;
    }

    return value;
}

qint8 ZigbeeDataType::toInt8(bool *ok) const
{
    if (ok) *ok = true;
    if (m_data.size() != 1) {
        if (ok) *ok = false;
        return 0;
    }

    return static_cast<qint8>(m_data.at(0));
}

qint16 ZigbeeDataType::toInt16(bool *ok) const
{
    if (ok) *ok = true;

    qint16 value = 0;
    if (m_data.size() != 2 || m_dataType != Zigbee::Int16) {
        if (ok) *ok = false;
        return value;
    }

    QDataStream stream(m_data);
    stream.setByteOrder(QDataStream::LittleEndian);
    stream >> value;
    return value;
}

qint32 ZigbeeDataType::toInt32(bool *ok) const
{
    if (ok) *ok = true;
    qint32 value = 0;

    // Verify the data type
    if (m_dataType != Zigbee::Int24 && m_dataType != Zigbee::Int32) {
        if (ok) *ok = false;
        return value;
    }

    // Make sure there is enought data
    if (m_data.size() != 3 && m_data.size() != 4) {
        if (ok) *ok = false;
        return value;
    }

    if (m_data.size() == 3) {
        // Make it 32 bit for converting
        QByteArray convertedData(m_data);
        convertedData.append(static_cast<char>(0));
        QDataStream stream(convertedData);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
    } else {
        QDataStream stream(m_data);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
    }

    return value;
}

qint64 ZigbeeDataType::toInt64(bool *ok) const
{
    if (ok) *ok = true;
    qint64 value = 0;

    switch (m_dataType) {
    case Zigbee::Int40: {
        if (m_data.size() != 5) {
            if (ok) *ok = false;
            break;
        }

        // Make it 64 bit for converting
        QByteArray convertedData(m_data);
        for (int i = 0; i < 3; i++)
            convertedData.append(static_cast<char>(0));

        QDataStream stream(convertedData);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
        break;
    }
    case Zigbee::Int48: {
        if (m_data.size() != 6) {
            if (ok) *ok = false;
            break;
        }

        // Make it 64 bit for converting
        QByteArray convertedData(m_data);
        for (int i = 0; i < 2; i++)
            convertedData.append(static_cast<char>(0));

        QDataStream stream(convertedData);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
        break;
    }
    case Zigbee::Int56: {
        if (m_data.size() != 7) {
            if (ok) *ok = false;
            break;
        }

        // Make it 64 bit for converting
        QByteArray convertedData(m_data);
        convertedData.append(static_cast<char>(0));
        QDataStream stream(convertedData);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
        break;
    }
    case Zigbee::Int64: {
        if (m_data.size() != 8) {
            if (ok) *ok = false;
            break;
        }

        QDataStream stream(m_data);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream >> value;
        break;
    }
    default:
        if (ok) *ok = false;
        break;
    }

    return value;
}

bool ZigbeeDataType::toBool(bool *ok) const
{
    if (ok) *ok = true;
    bool value = false;

    if (m_data.size() != 1) {
        if (ok) *ok = false;
        return value;
    }

    if (m_data.at(0) != 0) {
        value = true;
    }

    return value;
}

QString ZigbeeDataType::toString(bool *ok) const
{
    if (ok) *ok = true;
    QString value;

    if (m_dataType == Zigbee::OctetString || m_dataType == Zigbee::CharString) {
        quint8 length = m_data.at(0);
        value = QString::fromUtf8(m_data.right(length));
    } else if (m_dataType == Zigbee::LongOctetString || m_dataType == Zigbee::LongCharString) {
        quint16 length = 0;
        QDataStream lengthStream(m_data.left(2));
        lengthStream.setByteOrder(QDataStream::LittleEndian);
        lengthStream >> length;
        value = QString::fromUtf8(m_data.right(length));
    } else {
        if (ok) *ok = false;
    }
    return value;
}

float ZigbeeDataType::toFloat(bool *ok) const
{
    if (ok) *ok = true;
    QDataStream stream(m_data);
    stream.setByteOrder(QDataStream::LittleEndian);

    float value = 0;
    if (m_dataType == Zigbee::FloatSemi && m_data.length() == 2) {
        quint16 number;
        stream >> number;
        qint8 sign = ((number >> 15) & 1) == 1 ? -1 : 1;
        qint8 exponent = ((number >> 10) & 0x1f) - 31;

        int power = -1;
        double total = 0.0;
        for (int i = 0; i < 10; i++) {
            int calc = (number >> (10 - i - 1)) & 0x01;
            total += calc * pow(2.0, power);
            power--;
        }
        value = sign * qPow(2.0, exponent) * (total + 1.0);
    } else if (m_dataType == Zigbee::FloatSingle && m_data.length() == 4) {
        quint32 number;
        stream >> number;
        qint8 sign = ((number >> 31) & 1) == 1 ? -1 : 1;
        qint8 exponent = ((number >> 23) & 0xff) - 127;

        int power = -1;
        double total = 0.0;
        for (int i = 0; i < 23; i++) {
            int calc = (number >> (23 - i - 1)) & 0x01;
            total += calc * pow(2.0, power);
            power--;
        }
        value = sign * qPow(2.0, exponent) * (total + 1.0);
    } else {
        if (ok) *ok = false;
    }
    return value;
}

double ZigbeeDataType::toDouble(bool *ok) const
{
    if (ok) *ok = true;
    double value = 0;
    if (m_dataType == Zigbee::FloatSemi && m_data.length() == 2) {
        return toFloat(ok);
    } else if (m_dataType == Zigbee::FloatSingle && m_data.length() == 4) {
        return toFloat(ok);
    } else if (m_dataType == Zigbee::FloatDouble && m_data.length() == 8) {
        QDataStream stream(m_data);
        stream.setByteOrder(QDataStream::LittleEndian);
        quint64 number;
        stream >> number;
        qint8 sign = ((number >> 63) & 1) == 1 ? -1 : 1;
        qint16 exponent = ((number >> 52) & 0x7FF) - 1023;

        int power = -1;
        double total = 0.0;
        for (int i = 0; i < 52; i++) {
            int calc = (number >> (52 - i - 1)) & 0x01;
            total += calc * pow(2.0, power);
            power--;
        }
        value = sign * qPow(2.0, exponent) * (total + 1.0);
    } else {
        if (ok) *ok = false;
    }
    return value;
}

Zigbee::DataType ZigbeeDataType::dataType() const
{
    return m_dataType;
}

QString ZigbeeDataType::name() const
{
    return m_name;
}

QString ZigbeeDataType::className() const
{
    return m_className;
}

QByteArray ZigbeeDataType::data() const
{
    return m_data;
}

int ZigbeeDataType::dataLength() const
{
    return typeLength(m_dataType);
}

bool ZigbeeDataType::isValid() const
{
    // FIXME: implement validate data depending on the type
    return m_dataType != Zigbee::NoData && !m_data.isNull();
}

int ZigbeeDataType::typeLength(Zigbee::DataType dataType)
{
    int length = 0;
    switch (dataType) {
    case Zigbee::NoData:
        break;
    case Zigbee::Data8:
        length = 1;
        break;
    case Zigbee::Data16:
        length = 2;
        break;
    case Zigbee::Data24:
        length = 3;
        break;
    case Zigbee::Data32:
        length = 4;
        break;
    case Zigbee::Data40:
        length = 5;
        break;
    case Zigbee::Data48:
        length = 6;
        break;
    case Zigbee::Data56:
        length = 7;
        break;
    case Zigbee::Data64:
        length = 8;
        break;
    case Zigbee::Bool:
        length = 1;
        break;
    case Zigbee::BitMap8:
        length = 1;
        break;
    case Zigbee::BitMap16:
        length = 2;
        break;
    case Zigbee::BitMap24:
        length = 3;
        break;
    case Zigbee::BitMap32:
        length = 4;
        break;
    case Zigbee::BitMap40:
        length = 5;
        break;
    case Zigbee::BitMap48:
        length = 6;
        break;
    case Zigbee::BitMap56:
        length = 7;
        break;
    case Zigbee::BitMap64:
        length = 8;
        break;
    case Zigbee::Uint8:
        length = 1;
        break;
    case Zigbee::Uint16:
        length = 2;
        break;
    case Zigbee::Uint24:
        length = 3;
        break;
    case Zigbee::Uint32:
        length = 4;
        break;
    case Zigbee::Uint40:
        length = 5;
        break;
    case Zigbee::Uint48:
        length = 6;
        break;
    case Zigbee::Uint56:
        length = 7;
        break;
    case Zigbee::Uint64:
        length = 8;
        break;
    case Zigbee::Int8:
        length = 1;
        break;
    case Zigbee::Int16:
        length = 2;
        break;
    case Zigbee::Int24:
        length = 3;
        break;
    case Zigbee::Int32:
        length = 4;
        break;
    case Zigbee::Int40:
        length = 5;
        break;
    case Zigbee::Int48:
        length = 6;
        break;
    case Zigbee::Int56:
        length = 7;
        break;
    case Zigbee::Int64:
        length = 8;
        break;
    case Zigbee::Enum8:
        length = 1;
        break;
    case Zigbee::Enum16:
        length = 2;
        break;
    case Zigbee::FloatSemi:
        length = 2;
        break;
    case Zigbee::FloatSingle:
        length = 4;
        break;
    case Zigbee::FloatDouble:
        length = 8;
        break;
    case Zigbee::OctetString:
        // first byte is length
        length = -1;
        break;
    case Zigbee::CharString:
        // first byte is length
        length = -1;
        break;
    case Zigbee::LongOctetString:
        // first 2 byte is length
        length = -2;
        break;
    case Zigbee::LongCharString:
        // first 2 byte is length
        length = -2;
        break;
    case Zigbee::Array:
        // 2 + sum of lengths of content
        length = -3;
        break;
    case Zigbee::Structure:
        // 2 + sum of lengths of content
        length = -3;
        break;
    case Zigbee::Set:
        // sum of lengths of content
        length = -4;
        break;
    case Zigbee::Bag:
        // sum of lengths of content
        length = -4;
        break;
    case Zigbee::TimeOfDay:
        length = 4;
        break;
    case Zigbee::Date:
        length = 4;
        break;
    case Zigbee::UtcTime:
        length = 4;
        break;
    case Zigbee::Cluster:
        length = 2;
        break;
    case Zigbee::Attribute:
        length = 2;
        break;
    case Zigbee::BacnetId:
        length = 4;
        break;
    case Zigbee::IeeeAddress:
        length = 8;
        break;
    case Zigbee::BitKey128:
        length = 16;
        break;
    case Zigbee::Unknown:
        break;
    }

    return length;
}

ZigbeeDataType &ZigbeeDataType::operator=(const ZigbeeDataType &other)
{
    setDataType(other.dataType());
    m_data = other.data();
    return *this;
}

bool ZigbeeDataType::operator==(const ZigbeeDataType &other) const
{
    return m_dataType == other.dataType() && m_data == other.data();
}

bool ZigbeeDataType::operator!=(const ZigbeeDataType &other) const
{
    return !operator==(other);
}

void ZigbeeDataType::setDataType(Zigbee::DataType dataType)
{
    m_dataType = dataType;

    switch (dataType) {
    case Zigbee::NoData:
        m_name = "No data";
        m_className = "Null";
        m_typeLength = typeLength(m_dataType);
        m_data.clear();
        break;
    case Zigbee::Data8:
        m_name = "8-bit data";
        m_className = "General data discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Data16:
        m_name = "16-bit data";
        m_className = "General data discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Data24:
        m_name = "24-bit data";
        m_className = "General data discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Data32:
        m_name = "32-bit data";
        m_className = "General data discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Data40:
        m_name = "40-bit data";
        m_className = "General data discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Data48:
        m_name = "48-bit data";
        m_className = "General data discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Data56:
        m_name = "56-bit data";
        m_className = "General data discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Data64:
        m_name = "64-bit data";
        m_className = "General data discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Bool:
        m_name = "Bool";
        m_className = "Logical discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::BitMap8:
        m_name = "8-bit bitmap";
        m_className = "Bitmap discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::BitMap16:
        m_name = "16-bit bitmap";
        m_className = "Bitmap discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::BitMap24:
        m_name = "24-bit bitmap";
        m_className = "Bitmap discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::BitMap32:
        m_name = "32-bit bitmap";
        m_className = "Bitmap discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::BitMap40:
        m_name = "40-bit bitmap";
        m_className = "Bitmap discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::BitMap48:
        m_name = "48-bit bitmap";
        m_className = "Bitmap discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::BitMap56:
        m_name = "56-bit bitmap";
        m_className = "Bitmap discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::BitMap64:
        m_name = "64-bit bitmap";
        m_className = "Bitmap discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Uint8:
        m_name = "Unsigned 8-bit integer";
        m_className = "Unsigned integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Uint16:
        m_name = "Unsigned 16-bit integer";
        m_className = "Unsigned integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Uint24:
        m_name = "Unsigned 24-bit integer";
        m_className = "Unsigned integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Uint32:
        m_name = "Unsigned 32-bit integer";
        m_className = "Unsigned integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Uint40:
        m_name = "Unsigned 40-bit integer";
        m_className = "Unsigned integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Uint48:
        m_name = "Unsigned 48-bit integer";
        m_className = "Unsigned integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Uint56:
        m_name = "Unsigned 56-bit integer";
        m_className = "Unsigned integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Uint64:
        m_name = "Unsigned 64-bit integer";
        m_className = "Unsigned integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Int8:
        m_name = "Signed 8-bit integer";
        m_className = "Signed integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Int16:
        m_name = "Signed 16-bit integer";
        m_className = "Signed integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Int24:
        m_name = "Signed 24-bit integer";
        m_className = "Signed integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Int32:
        m_name = "Signed 32-bit integer";
        m_className = "Signed integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Int40:
        m_name = "Signed 40-bit integer";
        m_className = "Signed integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Int48:
        m_name = "Signed 48-bit integer";
        m_className = "Signed integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Int56:
        m_name = "Signed 56-bit integer";
        m_className = "Signed integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Int64:
        m_name = "Signed 64-bit integer";
        m_className = "Signed integer analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Enum8:
        m_name = "8-bit enumeration";
        m_className = "Enumeration discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Enum16:
        m_name = "16-bit enumeration";
        m_className = "Enumeration discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::FloatSemi:
        m_name = "Semi-precision";
        m_className = "Floating point analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::FloatSingle:
        m_name = "Single precision";
        m_className = "Floating point analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::FloatDouble:
        m_name = "Double precision";
        m_className = "Floating point analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::OctetString:
        m_name = "Octet string";
        m_className = "String discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::CharString:
        m_name = "Character string";
        m_className = "String discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::LongOctetString:
        m_name = "Long octet string";
        m_className = "String discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::LongCharString:
        m_name = "Long character string";
        m_className = "String discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Array:
        m_name = "Array";
        m_className = "Ordered sequence discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Structure:
        m_name = "Structure";
        m_className = "Ordered sequence discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Set:
        m_name = "Set";
        m_className = "Collection discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Bag:
        m_name = "Bag";
        m_className = "Collection discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::TimeOfDay:
        m_name = "Time of day";
        m_className = "Time analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Date:
        m_name = "Date";
        m_className = "Time analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::UtcTime:
        m_name = "UTC time";
        m_className = "Time analog";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Cluster:
        m_name = "Cluster ID";
        m_className = "Identifier discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Attribute:
        m_name = "Attribute ID";
        m_className = "Identifier discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::BacnetId:
        m_name = "BACnet OID";
        m_className = "Identifier discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::IeeeAddress:
        m_name = "IEEE address";
        m_className = "Miscellaneous discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::BitKey128:
        m_name = "128-bit security key";
        m_className = "Miscellaneous discrete";
        m_typeLength = typeLength(m_dataType);
        break;
    case Zigbee::Unknown:
        break;
    }
}

QDebug operator<<(QDebug debug, const ZigbeeDataType &dataType)
{
    // FIXME: print data depending on the datatype
    QDebugStateSaver saver(debug);
    debug.nospace() << "ZigbeeDataType(" << dataType.name();
    switch (dataType.dataType()) {
    case Zigbee::OctetString:
    case Zigbee::LongOctetString:
    case Zigbee::LongCharString:
    case Zigbee::CharString:
        debug.nospace() << ", " << dataType.toString();
        break;
    default:
        debug.nospace() << ", " << ZigbeeUtils::convertByteArrayToHexString(dataType.data());
    }

    debug.nospace() << ")";
    return debug;
}
