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

  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 <QThread>
#include <QDebug>

#include <bctransmitter.h>

/**
 * @brief Kosntruktion. Aktiviert erstmal den Dummy-Driver.
 */

BCTransmitter::BCTransmitter(QObject *parent)
  : QObject(parent), _isBusy(false)
{
  //_canDriver = new BCDriverTinyCan{this};
  _canDriver = &_dummyDriver;

}

/**
 * @brief Steuert die Verbindung mit dem 'echten' CAN-Bus Treiber.
 * @param connect true:  Vesuche den CAN-Bus Treiber zu laden und zu verbinden
 *                false: Disconnect & Cleanup
 */

void BCTransmitter::onToggleDriverConnection( bool connect )
{
  qDebug() << " --- onToggleDriverConnection: " << connect;
  // FIX! Ende der current op abwarten!

  // Hier sind wir noch in GUI Thread
  QMutexLocker locker(&_mutex);
  // weitere operation stoppen
  _isBusy = true;
  connect ? connectCanDriver() : disconnectCanDriver();
  _isBusy = false;
}

void BCTransmitter::connectCanDriver()
{
  // hier gehts nur um den echten Treiber

  // Treiber laden und/oder starten:
  BCDriver::DriverStateResult result; //(defaults to 'NotPresent')
  if( _tinyCanDriver.getDriverState() != BCDriver::DriverState::DeviceReady )
    result = _tinyCanDriver.loadAndStartDriver();

  QString message("FitzeFatze!");
  // hat geklappt
  if( _tinyCanDriver.getDriverState() >= BCDriver::DriverState::Opened )
  {
    uint32_t console = static_cast<uint32_t>(BCDevice::ID::Console);
    uint8_t  hwRev   = static_cast<uint8_t> (BC::ID::Cons_Rev_Hw);

    TransmitResult hwVersion =  _tinyCanDriver.readRawByte( console, hwRev);
    if( hwVersion.has_value() )
    {
      message = " ---- HAIL to the king!";
      qDebug() << message;
      // swap driver
      _canDriver = &_tinyCanDriver;
    }
    else
    {
      qDebug() << "Console not responding";
    }
  }
  else
  {
    message = result.error();
  }
  emit driverStateChanged( _tinyCanDriver.getDriverState(), message );
}

void BCTransmitter::disconnectCanDriver()
{
  _tinyCanDriver.resetDriver();
  _canDriver = &_dummyDriver;
   emit driverStateChanged( _tinyCanDriver.getDriverState(), "Disconnected" );
}


void BCTransmitter::enqueueValue( BCValuePtrConst value)
{
  // Hier sind wir noch in GUI Thread
  QMutexLocker locker(&_mutex);
  _valueQueue.enqueue( value );

  // wir wollen nicht den ganzen Value verschicken, erstrecht
  // wollen wir nicht den Value in verschiedenen Threads gleichzeitig
  // in die Hand nehmen, also hantieren wir nur mit den Inidizes.

  // Trigger processing im Event-Loop des Worker Threads
  // invokeMethod mit QueuedConnection entkoppelt den Aufruf,
  // damit enqueueValue sofort zurückkehrt (non-blocking für den Aufrufer).

  QMetaObject::invokeMethod(this, "processValue", Qt::QueuedConnection);

  /*
  QMetaObject::invokeMethod(this, [this]()
  {
    processValue();
  }, Qt::QueuedConnection );
  */
}

void BCTransmitter::processValue()
{

  if (_isBusy)
    return;

  _isBusy = true;

  while (true)
  {
    BCValuePtrConst valuePtr{};
    {
      QMutexLocker locker(&_mutex);
      if (_valueQueue.isEmpty())
      {
        _isBusy = false;
        break; // Schleife verlassen, warten auf neue Events
      }
      valuePtr =_valueQueue.dequeue();
    } // Mutex wird hier freigegeben! WICHTIG: Execute ohne Lock!

    // Kosmetik
    const BCValue& value = *(valuePtr.get());

    // Value ist 'under construction'
    //emit valueUpdated( value.deviceID, value.indexRow, BCValue::State::Locked );


    uint32_t devID = static_cast<uint32_t>(value.deviceID);
    uint8_t  regID = static_cast<uint8_t> (value.registerID);

    QString newVisibleValue;
    BCValue::State newState = BCValue::State::NoState;

    if(value.state.testFlag( BCValue::State::WriteMe ) )
    {


    }
    // oder sollen wir hier beides erlauben ? readFlag & writeFlag ?
    // Was kommt dann zuerst? Schreiben und lesen als verify ?

    else if( value.state.testFlag( BCValue::State::ReadMe ) )
    {
      // wir sind hier im anderen thread! nicht einfach so reinschreiben, nur lesen
      TransmitResult result = value.isWord ? readWordValue( devID, regID ) : readByteValue( devID, regID );
      if( result.has_value() )
      {
        newVisibleValue = value.formatValue( result.value() );
        newState = BCValue::State::InSync;
      }
      else
      {
        newState = BCValue::State::Failed;
      }
    }

    emit valueUpdated( value.deviceID, value.indexRow, newState, newVisibleValue );

    // __fix
    bc::processEventsFor(50);

    //emit valueStateChanged(cmd.id, true);
    //emit valueStateChanged(0, true);
  }
}


TransmitResult BCTransmitter::readByteValue( uint32_t deviceID, uint8_t registerID )
{
  //qDebug() << " --- YES: Read ByteValue: " << registerID;
  // Wir lesen nur ein Byte und gut.
  return _canDriver->readRawByte( deviceID, registerID );
}


TransmitResult BCTransmitter::readWordValue( uint32_t deviceID, uint8_t registerID )
{
  //qDebug() << " --- YES: Read WordValue: " << registerID;

  uint32_t result{};
  // hi byte Leseversuch.
  TransmitResult value = _canDriver->readRawByte( deviceID, registerID );
  // Fehler? dann weg
  if( !value)
    return std::unexpected( value.error() );
  // hi byte speichern
  result = *value << 8;
  // low byte, liegt im followup register: +1
  value = _canDriver->readRawByte( deviceID, registerID+1 );
  if( !value)
    return std::unexpected( value.error() );
  result += *value;
  return result;
}
