/******************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtKnx module.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
******************************************************************************/

#include "qknxtpdufactory_p.h"

QT_BEGIN_NAMESPACE

/*!
    \class QKnxTpduFactory::Broadcast
    \internal

    \inmodule QtKnx
    \brief The QKnxTpduFactory::Broadcast class is used to create a valid
    Transport protocol data unit (\l QKnxTpdu) for application services sent by
    broadcast.
*/

// -- A_NetworkParameter

static QKnxTpdu createNetworkParameterTpdu(QKnxTpdu::ApplicationControlField apci,
                                           QKnxInterfaceObjectType object,
                                           QKnxInterfaceObjectProperty property,
                                           const QKnxByteArray &data, // aka. testInfo
                                           const QKnxByteArray &testResult = {})
{
    if (!QKnxInterfaceObjectType::isMatch(object, property))
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast, apci,
        QKnxUtils::QUint16::bytes(quint16(object)) + QKnxUtils::QUint8::bytes(quint8(property))
        + data + testResult };
}

QKnxTpdu QKnxTpduFactory::Broadcast::createNetworkParameterReadTpdu(QKnxInterfaceObjectType object,
    QKnxInterfaceObjectProperty property, const QKnxByteArray &testInfo)
{
    if (testInfo.size() > 250) // L_Data_Extended -> max 254 bytes payload
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid}; // 4 bytes already used for APCI, object type, PID

    return createNetworkParameterTpdu(QKnxTpdu::ApplicationControlField::NetworkParameterRead,
        object, property, testInfo);
}

QKnxTpdu
QKnxTpduFactory::Broadcast::createNetworkParameterResponseTpdu(QKnxInterfaceObjectType object,
    QKnxInterfaceObjectProperty property, const QKnxByteArray &testInfo, const QKnxByteArray &testResult)
{
    if ((testInfo.size() + testResult.size()) > 250) // L_Data_Extended -> max 254 bytes payload
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid}; // 4 bytes already used for APCI, object type, PID

    return createNetworkParameterTpdu(QKnxTpdu::ApplicationControlField::NetworkParameterResponse,
        object, property, testInfo, testResult);
}

QKnxTpdu QKnxTpduFactory::Broadcast::createNetworkParameterWriteTpdu(QKnxInterfaceObjectType object,
    QKnxInterfaceObjectProperty property, const QKnxByteArray &value)
{
    if (value.size() > 250) // L_Data_Extended -> max 254 bytes payload
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid}; // 4 bytes already used for APCI, object type, property id

    return createNetworkParameterTpdu(QKnxTpdu::ApplicationControlField::NetworkParameterWrite,
        object, property, value);
}

QKnxTpdu
QKnxTpduFactory::Broadcast::createNetworkParameterInfoReportTpdu(QKnxInterfaceObjectType object,
    QKnxInterfaceObjectProperty property, const QKnxByteArray &testInfo, const QKnxByteArray &testResult)
{
    if ((testInfo.size() + testResult.size()) > 250) // L_Data_Extended -> max 254 bytes payload
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid}; // 4 bytes already used for APCI, object type, property id

    return createNetworkParameterTpdu(QKnxTpdu::ApplicationControlField::NetworkParameterInfoReport,
        object, property, testInfo, testResult);
}


// -- A_SystemNetworkParameter

static QKnxTpdu createSystemNetworkParameterTpdu(QKnxTpdu::ApplicationControlField apci,
                                                 QKnxInterfaceObjectType object,
                                                 quint16 property,
                                                 const QKnxByteArray &data, // aka. operand, testInfo
                                                 const QKnxByteArray &testResult = {})
{
    if (property > 0x0fff)
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    if ((data.size() + testResult.size()) > 249) // L_Data_Extended -> max 254 bytes payload
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid}; // 5 bytes already used for APCI, object type, PID

    return { QKnxTpdu::TransportControlField::DataSystemBroadcast, apci,
        QKnxUtils::QUint16::bytes(quint16(object)) + QKnxUtils::QUint8::bytes(quint8(property >> 4))
         + QKnxUtils::QUint8::bytes(quint8(property << 4)) + data + testResult };
}

QKnxTpdu QKnxTpduFactory::Broadcast
::createSystemNetworkParameterReadTpdu(QKnxInterfaceObjectType object, quint16 property,
    const QKnxByteArray &testInfo)
{
    return createSystemNetworkParameterTpdu(
        QKnxTpdu::ApplicationControlField::SystemNetworkParameterRead, object, property, testInfo);
}

QKnxTpdu
QKnxTpduFactory::Broadcast::createSystemNetworkParameterResponseTpdu(QKnxInterfaceObjectType object,
    quint16 property, const QKnxByteArray &testInfo, const QKnxByteArray &testResult)
{
    return createSystemNetworkParameterTpdu(
        QKnxTpdu::ApplicationControlField::SystemNetworkParameterResponse, object, property,
        testInfo, testResult);
}

QKnxTpdu
QKnxTpduFactory::Broadcast::createSystemNetworkParameterWriteTpdu(QKnxInterfaceObjectType object,
    quint16 property, const QKnxByteArray &value)
{
    return createSystemNetworkParameterTpdu(
        QKnxTpdu::ApplicationControlField::SystemNetworkParameterWrite, object, property, value);
}


// -- A_IndividualAddress

/*!
    \internal

    Returns a TPDU for Individual Address Read Application Service.
*/
QKnxTpdu QKnxTpduFactory::Broadcast::createIndividualAddressReadTpdu()
{
    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::IndividualAddressRead };
}

/*!
    \internal

    Returns a TPDU for Individual Address Write Application Service with the
    given \l QKnxAddress \a address; or an empty TPDU if the \a address is not
    of type \l QKnxAddress::Individual.
*/
QKnxTpdu QKnxTpduFactory::Broadcast::createIndividualAddressWriteTpdu(const QKnxAddress &address)
{
    if (address.type() != QKnxAddress::Type::Individual)
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::IndividualAddressWrite, address.bytes() };
}

QKnxTpdu
QKnxTpduFactory::Broadcast::createIndividualAddressResponseTpdu()
{
    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::IndividualAddressResponse };
}


// -- A_IndividualAddressSerialNumber

/*!
    \internal

    Returns a TPDU for Individual Address Serial Number Read Application Service
    with the given \a sn set; or an empty TPDU if the \a sn
    has a different size then six octets.
*/
QKnxTpdu
QKnxTpduFactory::Broadcast::createIndividualAddressSerialNumberReadTpdu(const QKnxByteArray &sn)
{
    if (sn.size() != 6)
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::IndividualAddressSerialNumberRead, sn };
}

QKnxTpdu
QKnxTpduFactory::Broadcast::createIndividualAddressSerialNumberResponseTpdu(const QKnxByteArray &sn,
    const QKnxAddress &domainAddress)
{
    if ((sn.size() != 6) || (domainAddress.type() != QKnxAddress::Type::Individual))
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::IndividualAddressSerialNumberResponse, sn
        + domainAddress.bytes() + QKnxUtils::QUint16::bytes(0u) };
}

/*!
    \internal

    Returns a TPDU for Individual Address Serial Number Write Application Service
    with the given \a sn and \l QKnxAddress \a newAddress set; or otherwise an
    empty TPDU if the \a sn has a different size then six octets or the type of
    \a newAddress is not of type \l QKnxAddress::Individual.
*/
QKnxTpdu
QKnxTpduFactory::Broadcast::createIndividualAddressSerialNumberWriteTpdu(const QKnxByteArray &sn,
    const QKnxAddress &newAddress)
{
    if ((sn.size() != 6) || (newAddress.type() != QKnxAddress::Type::Individual))
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::IndividualAddressSerialNumberWrite, sn + newAddress
        .bytes() + QKnxUtils::QUint32::bytes(0u) };
}


// -- A_DomainAddress

/*!
    \internal

    Returns a TPDU for Domain Address Read Application Service.
*/
QKnxTpdu QKnxTpduFactory::Broadcast::createDomainAddressReadTpdu()
{
    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::DomainAddressRead };
}

QKnxTpdu QKnxTpduFactory::Broadcast::createDomainAddressResponseTpdu(const QKnxByteArray &address)
{
    if ((address.size() != 2) && (address.size() != 6))
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::DomainAddressResponse, address };
}

/*!
    \internal

    Returns a TPDU for Domain Address Write Application Service with the given
    \l QKnxAddress \a address set.
*/
QKnxTpdu QKnxTpduFactory::Broadcast::createDomainAddressWriteTpdu(const QKnxByteArray &address)
{
    if ((address.size() != 2) && (address.size() != 6))
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::DomainAddressWrite, address };
}


// -- A_CreateDomainAddressSerialNumber

/*!
    \internal

    Returns a TPDU for Domain Address Serial Number Read Application Service
    with the given \a sn set; or an empty TPDU if the \a sn has a different size
    then six octets.
*/
QKnxTpdu
QKnxTpduFactory::Broadcast::createDomainAddressSerialNumberReadTpdu(const QKnxByteArray &sn)
{
    if (sn.size() != 6)
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::DomainAddressSerialNumberRead, sn };
}

QKnxTpdu
QKnxTpduFactory::Broadcast::createDomainAddressSerialNumberResponseTpdu(const QKnxByteArray &sn,
    const QKnxByteArray &domainAddress)
{
    if ((sn.size() != 6) || ((domainAddress.size() != 2) && (domainAddress.size() != 6)))
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::DomainAddressSerialNumberResponse, sn + domainAddress };
}

/*!
    \internal

    Returns a TPDU for Domain Address Serial Number Write Application Service
    with the given \a sn and \l QKnxAddress \a domainAddress set; or otherwise
    an empty TPDU if the \a sn has a different size then six octets.
*/
QKnxTpdu QKnxTpduFactory::Broadcast::createDomainAddressSerialNumberWriteTpdu(const QKnxByteArray &sn,
    const QKnxByteArray &domainAddress)
{
    if ((sn.size() != 6) || ((domainAddress.size() != 2) && (domainAddress.size() != 6)))
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::DomainAddressSerialNumberWrite, sn + domainAddress };
}


// -- A_DomainAddressSelective_Read

QKnxTpdu QKnxTpduFactory::Broadcast::createPll110DomainAddressSelectiveReadTpdu(quint8 domainAddress,
    const QKnxAddress &startAddress, quint8 range)
{
    if (startAddress.type() != QKnxAddress::Type::Individual)
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::DomainAddressSelectiveRead, QKnxUtils::QUint8::bytes(0u)
        + QKnxUtils::QUint8::bytes(domainAddress) + startAddress.bytes()
        + QKnxUtils::QUint8::bytes(range) };
}

QKnxTpdu
QKnxTpduFactory::Broadcast::createRfDomainAddressSelectiveReadTpdu(const QKnxByteArray &startAddress,
    const QKnxByteArray &endAddress)
{
    if ((startAddress.size() != 6) && (endAddress.size() != 6))
        return {QKnxTpdu::TransportControlField::Invalid,
            QKnxTpdu::ApplicationControlField::Invalid};

    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::DomainAddressSelectiveRead, startAddress + endAddress
        + QKnxUtils::QUint8::bytes(0u) };
}

QKnxTpdu QKnxTpduFactory::Broadcast::createFeDomainAddressSelectiveReadTpdu(quint16 manufacturerId,
    QKnxInterfaceObjectType obj, QKnxInterfaceObjectProperty property, quint16 parameters)
{
    return { QKnxTpdu::TransportControlField::DataBroadcast,
        QKnxTpdu::ApplicationControlField::DomainAddressSelectiveRead,
        QKnxUtils::QUint16::bytes(manufacturerId) + QKnxUtils::QUint16::bytes(quint16(obj))
        + QKnxUtils::QUint8::bytes(quint8(property)) + QKnxUtils::QUint16::bytes(parameters) };
}

QT_END_NAMESPACE
