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

  BionxControl
  Copyright © 2025 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 <QToolButton>

#include "qassert.h"
#include <bcmainwindow.h>
#include <bcanimateddelegate.h>
#include <ui_bcmainwindow.h>


/**
 * @brief Das Mainwindow erzeugen
 * @param parent Das Elternwidget
 */

BCMainWindow::BCMainWindow(QWidget *parent)
  : QMainWindow(parent)
{
  // WICHTIG: Registriere sowohl das Struct als auch die LISTE des Structs
  qRegisterMetaType<BCValue>("BCValue");
  qRegisterMetaType<QList<BCValue>>("BCValueList");

  setupUi(this);
  initMainWindow();


}


/**
 * @brief Destruktor. Räumt den Workthread auf.
 */

BCMainWindow::~BCMainWindow()
{
  _worker.quit();       // Event Loop stoppen
  _worker.wait();       // Warten bis Thread wirklich fertig ist
}


/**
 * @brief Initialisiert alle Komponenten des MainWindows.
 */

void BCMainWindow::initMainWindow()
{

  auto configureAction = [&]( QToolButton* button, QAction* action, BCDevice::ID deviceID, const QString& panelTitle="" )
  {
    // Action an den Button binden
    button->setDefaultAction( action);
    // old school, not used
    //connect( action, &QAction::triggered, this, &BCMainWindow::onActionButtonTriggered );
    //connect( action, &QAction::toggled, this, &BCMainWindow::onActionButtonToggled );

    // new way: die DeviceID muss aber explizit vom Lambda eingefanden werden.
    connect( action, &QAction::triggered, this, [this,deviceID]()
    {
      onShowDevicePanel( deviceID );
    });

    if( _devicePanels.contains(deviceID) )
    {
       BCDeviceView* currentPanel = _devicePanels[deviceID];
      // den Panels ihren title geben
      currentPanel->setHeaderLabel( panelTitle );
      // ... und ihre device ID
      currentPanel->setDeviceID( deviceID );
       // Wenn ein Device (entspricht einem Datenmodel) fertig eingelesen wurde,
      // wird es weitergereicht.
      // Problem: alle Panels bekommen alle Datenmodelle angeboten.
      connect( &_dataManager, &BCDataManager::valueListReady, currentPanel, &BCDeviceView::onValueListReady );
    }
  };

  // Wir wollen die Devices den Views zuordnen können
  _devicePanels[BCDevice::ID::Console] = _consolePanel;
  _devicePanels[BCDevice::ID::Battery] = _batteryPanel;
  _devicePanels[BCDevice::ID::Motor]   = _motorPanel;

  // Die actions an die Buttons binden
  configureAction(_motorButton,   _motorAction,   BCDevice::ID::Motor,   "Motor Settings"_L1 );
  configureAction(_consoleButton, _consoleAction, BCDevice::ID::Console, "Console Settings"_L1 );
  configureAction(_batteryButton, _batteryAction, BCDevice::ID::Battery, "Battery Settings"_L1 );
  configureAction(_pimpButton,    _pimpAction,    BCDevice::ID::Pimp,    "Pimp my Ride"_L1 );

/*

  // Verwendung:

  connect(_delegate, &BCAnimatedDelegate::viewUpdateNeeded, _valueView->viewport(), QOverload<>::of(&QWidget::update));


  //_valueView->setItemDelegate(_delegate);

  // Highlight mit Fade-Out:
  //_delegate->onHighlightRow(2);  // 2 Sekunden Fade
  BCTransmitter* transmitter = _dataManager.getTransmitter();



*/
  //_delegate->onHighlightRow(2);  // 2 Sekunden Fade

  // besser: model::emit dataChanged
  // also:  emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole});
  //connect( &_dataManager, &BCMainWindow::valueTouched, _delegate, &BCAnimatedDelegate::onHighlightRow );
  connect( _connectButton, &QToolButton::clicked, &_transmitter, &BCTransmitter::onToggleConnectionState );
  connect( _syncButton,    &QToolButton::clicked, this, &BCMainWindow::onSyncFromDevice );

  connect( &_transmitter, &BCTransmitter::valueUpdated, this, &BCMainWindow::onValueUpdated );

  // die Daten des eBikes laden
  _dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1);

  _transmitter.moveToThread(&_worker);

  connect(this, &BCMainWindow::sendValueCommand, &_transmitter, &BCTransmitter::enqueueValueOp);

  // B) Ergebnisse empfangen (Runner -> Manager)
  //connect(&_transmitter, &BCTransmitter::valueStateChanged, this, &BCDataManager::onvalueStateChanged);
  //connect(&_transmitter, &BCTransmitter::messageLogged, this, &BCDataManager::onRunnerMessage);

  // C) Aufräumen: Wenn Thread endet, lösche den Runner
  connect(&_worker, &QThread::finished, &_transmitter, &QObject::deleteLater);
  // 5. Thread starten
  _worker.start();

  _consoleAction->trigger();
}


/*
void BCMainWindow::onValueListReady( BCDevice::ID deviceID )
{
  qDebug() << " --- onValueListReady!" << deviceID;
  if(  _devicePanels.contains(  deviceID ) )
  {
    BCDeviceView& panel = *_devicePanels[deviceID];
    BCValueList& victim = panel.exposeValueList();
    BCValueList& newValueList = _dataManager.getCurrentValueList();
    qDebug() << " --- Before: " << victim.size() << "  orig:" << newValueList.size();
    victim = std::exchange(newValueList, BCValueList());
    //_devicePanels[deviceID]->exchangeValueList( newValueList );
    qDebug() << " ---After: " << victim.size() << "  orig:" << newValueList.size();
  }
}
*/


void BCMainWindow::onShowDevicePanel( BCDevice::ID deviceID )
{
  qDebug() << " --- onShowDevicePanel:" << deviceID;
  if( _devicePanels.contains(  deviceID ) )
  {
    BCDeviceView* nxtPanel = _devicePanels[deviceID];
    if( nxtPanel != _currentPanel )
    {
      _stackedWidget->setCurrentWidget( nxtPanel );
      _currentPanel = nxtPanel;
      // knopf auch abschalten?
    }
  }
}

void BCMainWindow::onActionButtonTriggered( bool checked)
{
  //qDebug() << " --- onActionButtonTriggered: " <<  checked;

}

void BCMainWindow::onActionButtonToggled( bool checked)
{
  //qDebug() << " --- onActionButtonToggled: " <<  checked;
}


void BCMainWindow::onConnectButtonToggled(bool checked )
{
  //_dataManager.setDriverConnectionState( checked );
}

void BCMainWindow::onValueUpdated(BCDevice::ID deviceID, int index, BC::State state, const QString& newValue )
{
  qDebug() << "Reply: from: " << deviceID << " at: " << index << "finished. Success:" << state << " on:" << newValue;
  if( _devicePanels.contains(  deviceID ) )
  {
    BCDeviceView& panel = *_devicePanels[deviceID];
    panel.onValueUpdated( index, state, newValue );
  }
}

void BCMainWindow::onRunnerMessage(const QString &msg)
{
  qDebug() << "[Worker says]:" << msg;
}



// __fix move this to devicePanel??
void BCMainWindow::onSyncFromDevice()
{

  Q_ASSERT_X(_currentPanel, "onSyncFromDevice()", "_currentpanel ist null!");

  qDebug() << " ---Syncing";

  const BCValueList& currentList =_currentPanel->getValueListX();

  // alle einzeln? echt jetzt?

  for( const BCValue& value : currentList )
  {
    qDebug() << " --- value: " << value.label;

    // statt '_transmitter.enqueueValueCommand( value )' entkoppeln
    // wir das eleganter über emit sendValueCommand()

    //_transmitter.enqueueValueCommand( value );
    emit sendValueCommand( BC::OpID::ReadValue, &value);
    //emit valueTouched( value.indexRow );

    bc::processEventsFor(50);



  }

}
