// 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/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef ZIGBEECLUSTERLIBRARY_H
#define ZIGBEECLUSTERLIBRARY_H

#include <QObject>
#include <QDebug>

#include "zigbee.h"
#include "zigbeedatatype.h"

class ZigbeeClusterLibrary
{
    Q_GADGET
public:
    /* General ZCL commans */
    enum Command {
        CommandReadAttributes = 0x00,
        CommandReadAttributesResponse = 0x01,
        CommandWriteAttributes = 0x02,
        CommandWriteAttributesUndivided = 0x03,
        CommandWriteAttributesResponse = 0x04,
        CommandWriteAttributesNoResponse = 0x05,
        CommandConfigureReporting = 0x06,
        CommandConfigureReportingResponse = 0x07,
        CommandReadReportingConfiguration = 0x08,
        CommandReadReportingConfigurationResponse = 0x09,
        CommandReportAttributes = 0x0a,
        CommandDefaultResponse = 0x0b,
        CommandDiscoverAttributes = 0x0c,
        CommandDiscoverAttributesResponse = 0x0d,
        CommandReadAttributesStructured = 0x0e,
        CommandWriteAttributesStructured = 0x0f,
        CommandWriteAttributesStructuredResponse = 0x10,
        CommandDiscoverCommandsReceived = 0x11,
        CommandDiscoverCommandsReceivedResponse = 0x12,
        CommandDiscoverCommandsGenerated = 0x13,
        CommandDiscoverCommandsGeneratedResponse = 0x14,
        CommandDiscoverAttributesExtended = 0x15,
        CommandDiscoverAttributesExtendedResponse = 0x16
    };
    Q_ENUM(Command)

    enum Status {
        StatusSuccess = 0x00,
        StatusFailure = 0x01,
        StatusNotAuthorized = 0x7e,
        StatusReservedFieldNotZero = 0x7f,
        StatusMalformedCommand = 0x80,
        StatusUnsupportedClusterCommand = 0x81,
        StatusUnsupportedGeneralCommand = 0x82,
        StatusUnsupportedManufacturerClusterCommand = 0x83,
        StatusUnsupportedManufacturerGeneralCommand = 0x84,
        StatusInvalidField = 0x85,
        StatusUnsupportedAttribute = 0x86,
        StatusInvalidValue = 0x87,
        StatusReadOnly = 0x88,
        StatusInsufficientSpace = 0x89,
        StatusDuplicateExists = 0x8a,
        StatusNotFound = 0x8b,
        StatusUnreportableAttribute = 0x8c,
        StatusInvalidDataType = 0x8d,
        StatusInvalidSector = 0x8e,
        StatusWriteOnly = 0x8f,
        StatusInconsistentStartupState = 0x90,
        StatusDefinedOutOfBand = 0x91,
        StatusInconsistent = 0x92,
        StatusActionDenied = 0x93,
        StatusTimeout = 0x94,
        StatusAbort = 0x95,
        StatusInvalidImage = 0x96,
        StatusWaitForData = 0x97,
        StatusNoImageAvailable = 0x98,
        StatusRequireMoreImage = 0x99,
        StatusNotificationPending = 0x9a,
        StatusHardwareFailure = 0xc0,
        StatusSoftwareFailure = 0xc1,
        StatusCalibrationError = 0xc2,
        StatusUnsupportedCluster = 0xc3
    };
    Q_ENUM(Status)

    enum ClusterId {
        // Basics
        ClusterIdUnknown                = 0xffff,
        ClusterIdBasic                  = 0x0000,
        ClusterIdPowerConfiguration     = 0x0001,
        ClusterIdDeviceTemperature      = 0x0002,
        ClusterIdIdentify               = 0x0003,
        ClusterIdGroups                 = 0x0004,
        ClusterIdScenes                 = 0x0005,
        ClusterIdOnOff                  = 0x0006,
        ClusterIdOnOffConfiguration     = 0x0007,
        ClusterIdLevelControl           = 0x0008,
        ClusterIdAlarms                 = 0x0009,
        ClusterIdTime                   = 0x000A,
        ClusterIdRssiLocation           = 0x000B,
        ClusterIdAnalogInput            = 0x000C,
        ClusterIdAnalogOutput           = 0x000D,
        ClusterIdAnalogValue            = 0x000E,
        ClusterIdBinaryInput            = 0x000F,
        ClusterIdBinaryOutput           = 0x0010,
        ClusterIdBinaryValue            = 0x0011,
        ClusterIdMultistateInput        = 0x0012,
        ClusterIdMultistateOutput       = 0x0013,
        ClusterIdMultistateValue        = 0x0014,
        ClusterIdCommissoning           = 0x0015,

        // Over the air uppgrade (OTA)
        ClusterIdOtaUpgrade             = 0x0019,

        // Poll controll
        ClusterIdPollControl            = 0x0020,


        // Closures
        ClusterIdShadeConfiguration     = 0x0100,
        ClusterIdDoorLock               = 0x0101,
        ClusterIdWindowCovering         = 0x0102,

        // Heating, Ventilation and Air-Conditioning (HVAC)
        ClusterIdPumpConfigurationControl = 0x0200,
        ClusterIdThermostat               = 0x0201,
        ClusterIdFanControl               = 0x0202,
        ClusterIdDehumiditationControl    = 0x0203,
        ClusterIdThermostatUserControl    = 0x0204,

        // Lighting
        ClusterIdColorControl           = 0x0300,
        ClusterIdBallastConfiguration   = 0x0301,

        // Sensing
        ClusterIdIlluminanceMeasurement         = 0x0400,
        ClusterIdIlluminanceLevelSensing        = 0x0401,
        ClusterIdTemperatureMeasurement         = 0x0402,
        ClusterIdPressureMeasurement            = 0x0403,
        ClusterIdFlowMeasurement                = 0x0404,
        ClusterIdRelativeHumidityMeasurement    = 0x0405,
        ClusterIdOccupancySensing               = 0x0406,

        // Security and Safty
        ClusterIdIasZone = 0x0500,
        ClusterIdIasAce  = 0x0501,
        ClusterIdIasWd   = 0x0502,

        // Smart energy
        ClusterIdPrice                        = 0x0700,
        ClusterIdDemandResponseAndLoadControl = 0x0701,
        ClusterIdMetering                     = 0x0702,
        ClusterIdMessaging                    = 0x0703,
        ClusterIdTunneling                    = 0x0704,
        ClusterIdKeyEstablishment             = 0x0800,

        // ZLL
        ClusterIdTouchlinkCommissioning = 0x1000,

        // NXP Appliances
        ClusterIdApplianceControl           = 0x001B,
        ClusterIdApplianceIdentification    = 0x0B00,
        ClusterIdApplianceEventsAlerts      = 0x0B02,
        ClusterIdApplianceStatistics        = 0x0B03,

        // Electrical Measurement
        ClusterIdElectricalMeasurement      = 0x0B04,
        ClusterIdDiagnostics                = 0x0B05,

        // Zigbee green power
        ClusterIdGreenPower                 = 0x0021,

        // Manufacturer specific
        ClusterIdManufacturerSpecificPhilips = 0xfc00,

    };
    Q_ENUM(ClusterId)

    enum GlobalAttribute {
        GlobalAttributeClusterRevision = 0xfffd,
        GlobalAttributeAttributeReportingStatus = 0xfffe
    };
    Q_ENUM(GlobalAttribute)

    enum AttributeReportingStatus {
        AttributeReportingStatusPending = 0x00,
        AttributeReportingStatusComplete = 0x01
    };
    Q_ENUM(AttributeReportingStatus)

    // Frame control field
    enum FrameType {
        FrameTypeGlobal = 0x00,
        FrameTypeClusterSpecific = 0x01
    };
    Q_ENUM(FrameType)

    enum Direction {
        DirectionClientToServer = 0x00,
        DirectionServerToClient = 0x01
    };
    Q_ENUM(Direction)

    enum ReportingDirection {
        ReportingDirectionReporting = 0x00,
        ReportingDirectionReceiving = 0x01
    };
    Q_ENUM(ReportingDirection)

    typedef struct FrameControl {
        FrameType frameType = FrameTypeGlobal;
        bool manufacturerSpecific = false;
        Direction direction = DirectionClientToServer;
        bool disableDefaultResponse = false;
    } FrameControl;

    typedef struct Header {
        FrameControl frameControl;
        quint16 manufacturerCode = 0;
        quint8 transactionSequenceNumber = 0;
        quint8 command;
    } ZclHeader;

    typedef struct Frame {
        Header header;
        QByteArray payload;
    } Frame;


    // Read attribute
    typedef struct ReadAttributeStatusRecord {
        quint16 attributeId;
        ZigbeeClusterLibrary::Status attributeStatus;
        ZigbeeDataType dataType;
    } ReadAttributeStatusRecord;

    // Write attribute
    typedef struct WriteAttributeRecord {
        quint16 attributeId;
        Zigbee::DataType dataType;
        QByteArray data;
    } WriteAttributeRecord;

    // Reporting attributes
    typedef struct AttributeReportingConfiguration {
        ReportingDirection direction = ReportingDirectionReporting;
        quint16 attributeId = 0x0000;
        Zigbee::DataType dataType = Zigbee::NoData;
        quint16 minReportingInterval = 0x0000; // seconds
        quint16 maxReportingInterval = 0x0000; // seconds
        QByteArray reportableChange; // Data depending on the dataType
        quint16 timeoutPeriod = 0x0000;  // seconds, only used for direction receiving
    } AttributeReportingConfiguration;

    // Response of reporting configuration
    typedef struct AttributeReportingStatusRecord {
        ZigbeeClusterLibrary::Status status;
        ReportingDirection direction = ReportingDirectionReporting;
        quint16 attributeId = 0x0000;
    } AttributeReportingStatusRecord;


    // General parse/build methods
    static quint8 buildFrameControlByte(const FrameControl &frameControl);
    static FrameControl parseFrameControlByte(quint8 frameControlByte);

    static QByteArray buildHeader(const Header &header);

    static QList<ReadAttributeStatusRecord> parseAttributeStatusRecords(const QByteArray &payload);

    //static QByteArray readAttributeData(const QDataStream &stream, Zigbee::DataType dataType);
    static ZigbeeDataType readDataType(QDataStream *stream, Zigbee::DataType dataType);

    static Frame parseFrameData(const QByteArray &frameData);
    static QByteArray buildFrame(const Frame &frame);

    // AttributeReportingConfiguration
    static QByteArray buildAttributeReportingConfiguration(const AttributeReportingConfiguration &reportingConfiguration);
    static QByteArray buildWriteAttributeRecord(const WriteAttributeRecord &writeAttributeRecord);
    // TODO: parseAttributeReportingConfiguration

    static QList<AttributeReportingStatusRecord> parseAttributeReportingStatusRecords(const QByteArray &payload);

};

QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::FrameControl &frameControl);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Header &header);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Frame &frame);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::ReadAttributeStatusRecord &attributeStatusRecord);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::AttributeReportingConfiguration &attributeReportingConfiguration);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::AttributeReportingStatusRecord &attributeReportingStatusRecord);


#endif // ZIGBEECLUSTERLIBRARY_H
