/***************************************************************************

  BionxControl
  © 2025 -2026 christoph holzheuer
  christoph.holzheuer@gmail.com

  Using:

  mhs_can_drv.c
  © 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany
  Klaus Demlehner, klaus@mhs-elektronik.de
  @see www.mhs-elektronik.de

  Based on Bionx data type descriptions from:

  BigXionFlasher USB V 0.2.4 rev. 97
  © 2011-2013 by Thomas Koenig <info@bigxionflasher.org>
  @see www.bigxionflasher.org

  Bionx Bike Info
  © 2018 Thorsten Schmidt (tschmidt@ts-soft.de)
  @see www.ts-soft.de

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
  @see https://github.com/bikemike/bionx-bikeinfo

 ***************************************************************************/


#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <QMetaEnum>
#include <QHash>

#include <bcxmlloader.h>


using namespace Qt::StringLiterals;

BCXmlLoader::BCXmlLoader(QObject *parent)
  : QObject(parent)
{
  //qRegisterMetaType<BCValue*>("BCValue*");
  //qRegisterMetaType<BCValue*>();
}


void BCXmlLoader::loadXmlBikeData( const QString& fileName )
{
  auto printAttrs = [](const QXmlStreamReader& xml)
  {
    QStringList parts;
    for (const auto &attr : xml.attributes()) {
      parts << attr.name().toString() + "=\"" + attr.value().toString() + "\"";
    }
    qDebug().noquote() << parts.join(" ");
  };
  /*
  QString fileName = QFileDialog::getOpenFileName(this, "XML öffnen", "", "XML Files (*.xml)");
  if (fileName.isEmpty())
    return;
  */

  QMetaEnum bcDeviceEnum{QMetaEnum::fromType<BCDevice::ID>()};

  QFile file(fileName);
  if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
  {
    // __fix throw
    QMessageBox::warning(nullptr, "Fehler", "Datei konnte nicht geöffnet werden.");
    return;
  }

  _xml.setDevice(&file);

  if (_xml.readNextStartElement())
  {
    if (_xml.name() != "Bike"_L1 )
      // fix throw
      _xml.raiseError(QObject::tr("The file is not an 'Bike' file."));
  }
  // ??
  Q_ASSERT(_xml.isStartElement() && _xml.name() == "Bike"_L1);

  while (!_xml.atEnd() && !_xml.hasError())
  {
    QXmlStreamReader::TokenType token = _xml.readNext();
    if (token == QXmlStreamReader::StartElement)
    {
      QString deviceType = _xml.attributes().value("Type"_L1).toString();
      printAttrs (_xml);
      // Wir wollen die Device-ID aus dem XML Tag ermitteln
      const char* deviceKey = _xml.attributes().value("Type"_L1).toLatin1().constData();
      bool ok;
      auto optDeviceID = bcDeviceEnum.keyToValue(deviceKey,&ok);
      //_currentDeviceID = BCDevice::ID( deviceID.value_or( BCDevice::ID::Invalid ) );
      //if( optDeviceID.has_value())
      if(ok)
      {
        qDebug() << " --- Device: " << _xml.name() << ": " << deviceType << " : " << optDeviceID;
        //BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID.value() );
        BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID );
        loadXmlBikeDeviceData(currentDeviceID);
      }

    }
  }

  /*
  if (xml.hasError())
  {
    QMessageBox::critical(nullptr, "Parsing Fehler", xml.errorString());
  }
  else
  {
    m_model->setDevices(parsedValues);
  }
  */

  // create & add new model to the model map

}

void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID)
{
  auto printAttrs = [](const QXmlStreamReader& xml)
  {
    QStringList parts;
    for (const auto &attr : xml.attributes()) {
      parts << attr.name().toString() + "=\"" + attr.value().toString() + "\"";
    }
    qDebug().noquote() << parts.join(" ");
  };

  printAttrs (_xml);
  Q_ASSERT(_xml.isStartElement() && _xml.name() == "Device"_L1);
  qDebug() << " XXX ---------------";

  // temporäre Wertliste für neues Device
  BCValueList currentValues;

  while( _xml.readNextStartElement() )
  {
    if( _xml.attributes().hasAttribute(BCTags::ID) )
    {
      //qDebug() << " --- found: " << _xml.name() << " : " << _xml.attributes().value(BCTags::ID);

      // füllen des Parameter packs
      BCValueParams params
      {
        .ID        = _xml.attributes().value(BCTags::ID).toString(),
        .Label     = _xml.attributes().value(BCTags::Label).toString(),
        .UnitLabel = _xml.attributes().value(BCTags::UnitLabel).toString(),
        .Factor    = _xml.attributes().value(BCTags::Factor).toString(),
        .Min       = _xml.attributes().value(BCTags::Min).toString(),
        .Max       = _xml.attributes().value(BCTags::Max).toString(),
        .IsWord    = _xml.attributes().value(BCTags::IsWord).toString(),
        .ValueType = _xml.attributes().value(BCTags::ValueType).toString(),
      };

      // nur gültige Werte sind vorhanden und können gespeichert werden
      std::optional<BCValuePtr> newValue = makeValue( deviceID, params );
      if(newValue)
      {
        // wir merken uns gleich den index in der Werteliste
        (*newValue)->indexRow = currentValues.size();
        currentValues.push_back( *newValue );
      }

    }
    // weiter zum nächsten Element
   _xml.skipCurrentElement();
  }

  // Wenn dieses Device fertig geladen wurde, soll das MainWindow es abholen
  if( !currentValues.isEmpty() )
    emit valueListReady( deviceID, currentValues );

}

std::optional<BCValuePtr> BCXmlLoader::makeValue( BCDevice::ID deviceID, const BCValueParams& params )
{

  static QHash<QString,BCValue::ValueType> s_valueTypes
  {
    { "Plain",  BCValue::ValueType::Plain },
    { "Bool",   BCValue::ValueType::Bool },
    { "Number", BCValue::ValueType::Number },
    { "Float",  BCValue::ValueType::Float }
  };

  auto setIfExists = [&]<typename T>( QStringView source, T& target )
  {
    if( !source.isEmpty() )
      {
        bool ok;
        double testVal = source.toDouble(&ok);
        if (ok)
          target = testVal;
      }
  };


  /*
    Wir brauchen:
    - einen gültige ID String um die enum ID herauszufinden.
    - einen gültige UnitType String um den ValueType herauszufinden.

  */

  // geht nicht auf qt6.8 von debian trixie
  //std::optional<quint64> IDVal = s_bcValueEnum.keyToValue64( params.ID.toLatin1().constData() );
  bool ok;
  static QMetaEnum s_bcValueEnum{QMetaEnum::fromType<BC::ID>()};
  int IDVal = s_bcValueEnum.keyToValue( params.ID.toLatin1().constData(), &ok );
  qDebug() << " --- should create: " <<  params.Label;
  //if( IDVal.has_value() )
  if( ok )
  {
    BCValuePtr newValuePtr = std::make_shared<BCValue>( deviceID, static_cast<BC::ID>(IDVal) );
    BCValue& newValue = *newValuePtr.get();

    setIfExists( params.Factor, newValue.factor );
    setIfExists( params.Min, newValue.min );
    setIfExists( params.Max, newValue.max );
    setIfExists( params.IsWord, newValue.isWord );

    newValue.label     = params.Label;
    newValue.unitLabel = params.UnitLabel;

    if( s_valueTypes.contains( params.ValueType ) )
      newValue.valueType = s_valueTypes[params.ValueType];

    /*
    QString ID;
    QString Label;
    QString UnitLabel;
    QString Factor;
    QString Min;
    QString Max;
    QString IsWord;
    QString ValueType;
    */

    qDebug() << " --- created: " <<  params.Label;
    newValue.dumpValue();

    return std::optional<BCValuePtr>( newValuePtr );
  }

  return std::nullopt;
}





