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

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright (C) 2013 - 2024, nymea GmbH
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
*
* This file is part of libnymea-sunspec.
*
* libnymea-sunspec 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.
*
* libnymea-sunspec 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 libnymea-sunspec. If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "sunspeclocationmodel.h"
#include "sunspecconnection.h"

SunSpecLocationModel::SunSpecLocationModel(SunSpecConnection *connection, quint16 modbusStartRegister, quint16 modelLength, SunSpecDataPoint::ByteOrder byteOrder, QObject *parent) :
    SunSpecModel(connection, modbusStartRegister, 305, modelLength, byteOrder, parent)
{
    m_modelBlockType = SunSpecModel::ModelBlockTypeFixed;

    initDataPoints();
}

SunSpecLocationModel::~SunSpecLocationModel()
{

}

QString SunSpecLocationModel::name() const
{
    return "location";
}

QString SunSpecLocationModel::description() const
{
    return "Include to support location measurements";
}

QString SunSpecLocationModel::label() const
{
    return "GPS";
}

QString SunSpecLocationModel::tm() const
{
    return m_tm;
}
QString SunSpecLocationModel::date() const
{
    return m_date;
}
QString SunSpecLocationModel::location() const
{
    return m_location;
}
float SunSpecLocationModel::lat() const
{
    return m_lat;
}
float SunSpecLocationModel::longitude() const
{
    return m_longitude;
}
qint32 SunSpecLocationModel::altitude() const
{
    return m_altitude;
}
void SunSpecLocationModel::initDataPoints()
{
    SunSpecDataPoint modelIdDataPoint;
    modelIdDataPoint.setName("ID");
    modelIdDataPoint.setLabel("Model ID");
    modelIdDataPoint.setDescription("Model identifier");
    modelIdDataPoint.setMandatory(true);
    modelIdDataPoint.setSize(1);
    modelIdDataPoint.setAddressOffset(0);
    modelIdDataPoint.setSunSpecDataType("uint16");
    modelIdDataPoint.setByteOrder(m_byteOrder);
    m_dataPoints.insert(modelIdDataPoint.name(), modelIdDataPoint);

    SunSpecDataPoint modelLengthDataPoint;
    modelLengthDataPoint.setName("L");
    modelLengthDataPoint.setLabel("Model Length");
    modelLengthDataPoint.setDescription("Model length");
    modelLengthDataPoint.setMandatory(true);
    modelLengthDataPoint.setSize(1);
    modelLengthDataPoint.setAddressOffset(1);
    modelLengthDataPoint.setSunSpecDataType("uint16");
    modelLengthDataPoint.setByteOrder(m_byteOrder);
    m_dataPoints.insert(modelLengthDataPoint.name(), modelLengthDataPoint);

    SunSpecDataPoint tmDataPoint;
    tmDataPoint.setName("Tm");
    tmDataPoint.setLabel("Tm");
    tmDataPoint.setDescription("UTC 24 hour time stamp to millisecond hhmmss.sssZ format");
    tmDataPoint.setUnits("hhmmss.sssZ");
    tmDataPoint.setSize(6);
    tmDataPoint.setAddressOffset(2);
    tmDataPoint.setBlockOffset(0);
    tmDataPoint.setSunSpecDataType("string");
    tmDataPoint.setByteOrder(m_byteOrder);
    m_dataPoints.insert(tmDataPoint.name(), tmDataPoint);

    SunSpecDataPoint dateDataPoint;
    dateDataPoint.setName("Date");
    dateDataPoint.setLabel("Date");
    dateDataPoint.setDescription("UTC Date string YYYYMMDD format");
    dateDataPoint.setUnits("YYYYMMDD");
    dateDataPoint.setSize(4);
    dateDataPoint.setAddressOffset(8);
    dateDataPoint.setBlockOffset(6);
    dateDataPoint.setSunSpecDataType("string");
    dateDataPoint.setByteOrder(m_byteOrder);
    m_dataPoints.insert(dateDataPoint.name(), dateDataPoint);

    SunSpecDataPoint locationDataPoint;
    locationDataPoint.setName("Loc");
    locationDataPoint.setLabel("Location");
    locationDataPoint.setDescription("Location string (40 chars max)");
    locationDataPoint.setUnits("text");
    locationDataPoint.setSize(20);
    locationDataPoint.setAddressOffset(12);
    locationDataPoint.setBlockOffset(10);
    locationDataPoint.setSunSpecDataType("string");
    locationDataPoint.setByteOrder(m_byteOrder);
    m_dataPoints.insert(locationDataPoint.name(), locationDataPoint);

    SunSpecDataPoint latDataPoint;
    latDataPoint.setName("Lat");
    latDataPoint.setLabel("Lat");
    latDataPoint.setDescription("Latitude with seven degrees of precision");
    latDataPoint.setUnits("Degrees");
    latDataPoint.setSize(2);
    latDataPoint.setAddressOffset(32);
    latDataPoint.setBlockOffset(30);
    latDataPoint.setScaleFactorName("-7");
    latDataPoint.setSunSpecDataType("int32");
    latDataPoint.setByteOrder(m_byteOrder);
    m_dataPoints.insert(latDataPoint.name(), latDataPoint);

    SunSpecDataPoint longitudeDataPoint;
    longitudeDataPoint.setName("Long");
    longitudeDataPoint.setLabel("Long");
    longitudeDataPoint.setDescription("Longitude with seven degrees of precision");
    longitudeDataPoint.setUnits("Degrees");
    longitudeDataPoint.setSize(2);
    longitudeDataPoint.setAddressOffset(34);
    longitudeDataPoint.setBlockOffset(32);
    longitudeDataPoint.setScaleFactorName("-7");
    longitudeDataPoint.setSunSpecDataType("int32");
    longitudeDataPoint.setByteOrder(m_byteOrder);
    m_dataPoints.insert(longitudeDataPoint.name(), longitudeDataPoint);

    SunSpecDataPoint altitudeDataPoint;
    altitudeDataPoint.setName("Alt");
    altitudeDataPoint.setLabel("Altitude");
    altitudeDataPoint.setDescription("Altitude measurement in meters");
    altitudeDataPoint.setUnits("meters");
    altitudeDataPoint.setSize(2);
    altitudeDataPoint.setAddressOffset(36);
    altitudeDataPoint.setBlockOffset(34);
    altitudeDataPoint.setSunSpecDataType("int32");
    altitudeDataPoint.setByteOrder(m_byteOrder);
    m_dataPoints.insert(altitudeDataPoint.name(), altitudeDataPoint);

}

void SunSpecLocationModel::processBlockData()
{
    // Update properties according to the data point type
    if (m_dataPoints.value("Tm").isValid())
        m_tm = m_dataPoints.value("Tm").toString();

    if (m_dataPoints.value("Date").isValid())
        m_date = m_dataPoints.value("Date").toString();

    if (m_dataPoints.value("Loc").isValid())
        m_location = m_dataPoints.value("Loc").toString();

    if (m_dataPoints.value("Lat").isValid())
        m_lat = m_dataPoints.value("Lat").toFloatWithSSF(-7);

    if (m_dataPoints.value("Long").isValid())
        m_longitude = m_dataPoints.value("Long").toFloatWithSSF(-7);

    if (m_dataPoints.value("Alt").isValid())
        m_altitude = m_dataPoints.value("Alt").toInt32();


    qCDebug(dcSunSpecModelData()) << this;
}

QDebug operator<<(QDebug debug, SunSpecLocationModel *model)
{
    debug.nospace().noquote() << "SunSpecLocationModel(Model: " << model->modelId() << ", Register: " << model->modbusStartRegister() << ", Length: " << model->modelLength() << ")\n";
    debug.nospace().noquote() << "    - " << model->dataPoints().value("Tm") << "-->";
    if (model->dataPoints().value("Tm").isValid()) {
        debug.nospace().noquote() << model->tm() << "\n";
    } else {
        debug.nospace().noquote() << "NaN\n";
    }

    debug.nospace().noquote() << "    - " << model->dataPoints().value("Date") << "-->";
    if (model->dataPoints().value("Date").isValid()) {
        debug.nospace().noquote() << model->date() << "\n";
    } else {
        debug.nospace().noquote() << "NaN\n";
    }

    debug.nospace().noquote() << "    - " << model->dataPoints().value("Loc") << "-->";
    if (model->dataPoints().value("Loc").isValid()) {
        debug.nospace().noquote() << model->location() << "\n";
    } else {
        debug.nospace().noquote() << "NaN\n";
    }

    debug.nospace().noquote() << "    - " << model->dataPoints().value("Lat") << "-->";
    if (model->dataPoints().value("Lat").isValid()) {
        debug.nospace().noquote() << model->lat() << "\n";
    } else {
        debug.nospace().noquote() << "NaN\n";
    }

    debug.nospace().noquote() << "    - " << model->dataPoints().value("Long") << "-->";
    if (model->dataPoints().value("Long").isValid()) {
        debug.nospace().noquote() << model->longitude() << "\n";
    } else {
        debug.nospace().noquote() << "NaN\n";
    }

    debug.nospace().noquote() << "    - " << model->dataPoints().value("Alt") << "-->";
    if (model->dataPoints().value("Alt").isValid()) {
        debug.nospace().noquote() << model->altitude() << "\n";
    } else {
        debug.nospace().noquote() << "NaN\n";
    }


    return debug.space().quote();
}
