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

  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 <QTimer>

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

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

BCMainWindow::BCMainWindow(QWidget *parent)
  : QMainWindow(parent)
{
  // __fix! in der Form nötig?
  qRegisterMetaType<BCValue>("BCValue");
  qRegisterMetaType<QList<BCValue>>("BCValueList");

  setupUi(this);

  // den pimp-my-ride-button schalten wir vorerst aus.
  _pimpButton->hide();

  // Wir schreiben den 'initMainWindow()' Aufruf mit Hilfe des
  // timers in die Event-Queue, damit er erst ausgeführt wird,
  // wenn das Fenster sichtbar ist.

  QTimer::singleShot(0, this, [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()
{

  // Lambda um die buttons mit  ihren Actions zu verbinden
  auto configureAction = [&]( QToolButton* button, QAction* action, BCDevice::ID deviceID  )
  {
    // Action an den Button binden
    button->setDefaultAction( action);
    // 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];
        // ... 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, &BCXmlLoader::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;
  //_devicePanels[BCDevice::ID::Pimp]    = _pimpPanel;

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

  /*
  bool m_isDarkMode = false;
  QString icon = m_isDarkMode ? "☀️" : "🌙";
  fitzeButton->setText(icon);

  QString style = QString(
                      "QPushButton {"
                      "  background-color: %1;"
                      "  border: 1px solid %2;"
                      "  border-radius: 6px;"
                      "  font-size: 12pt;"
                      "  padding: 0px;"
                      "}"
                      "QPushButton:hover {"
                      "  background-color: %3;"
                      "}"
                      ).arg(m_isDarkMode ? "#2B2B2B" : "#FFFFFF")
                      .arg(m_isDarkMode ? "#3F3F3F" : "#E1DFDD")
                      .arg(m_isDarkMode ? "#3A3A3A" : "#F9F9F9");

  fitzeButton->setStyleSheet(style);
  */

  initStatusBar();

  // besser: model::emit dataChanged
  // also:  emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole});
  connect( _connectButton, &QToolButton::clicked, &_transmitter, &BCTransmitter::onToggleDriverConnection );
  connect( _syncButton,    &QToolButton::clicked, this, &BCMainWindow::onSyncDeviceView );
  connect( &_transmitter,  &BCTransmitter::valueUpdated, this, &BCMainWindow::onValueUpdated );

  connect(this, &BCMainWindow::requestValueUpdate, &_transmitter, &BCTransmitter::enqueueValue);
  connect(&_worker, &QThread::finished, &_transmitter, &QObject::deleteLater);
  connect( &_transmitter, &BCTransmitter::driverStateChanged, this, &BCMainWindow::onDriverStateChanged );

  // transmitter starten
  _transmitter.moveToThread(&_worker);
  _worker.start();

  // die Daten des eBikes laden
  _dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1);
  //_consoleAction->trigger();
  _batteryAction->trigger();

}


void BCMainWindow::initStatusBar()
{
  QStatusBar *statBar = statusBar();

  BCDriverStateWidget* connector = new BCDriverStateWidget(this);
  connect( &_transmitter, &BCTransmitter::driverStateChanged, connector, &BCDriverStateWidget::onDriverStateChanged );
  statBar->addPermanentWidget(connector);

  statBar->showMessage("Ready");
  BCThemeSwitchButton *themeBtn = new BCThemeSwitchButton(this);
  statBar->addPermanentWidget(themeBtn);
  connect(themeBtn, &BCThemeSwitchButton::themeChanged, this, [this](bool isDark)
  {
    QString message = isDark ? "Dark Mode Activated" : "Light Mode Activated";
    statusBar()->showMessage( message, 3000);
  });
}


/**
 * @brief Setzt den Headerlabel ( == die Device-Bezeichnung )
 * @param headerLabel Der headerLabel
 */

void BCMainWindow::setHeaderLabel( const QString& headerText)
{
  _headerLabel->setText( "   BionxControl: " + headerText );
}


void BCMainWindow::autoConnect()
{
  // __fix!
   // if( !connect)
   //   fallBack
}

void BCMainWindow::onDriverStateChanged( BCDriver::DriverState state, const QString& message )
{
  qDebug() << " --- on DriverStatusChanged: " << state << ":" << message;
  _statusbar->showMessage( message, 8000 );
}

void BCMainWindow::onShowDevicePanel( BCDevice::ID deviceID )
{
  qDebug() << " --- onShowDevicePanel:" << deviceID;
  if( _devicePanels.contains(  deviceID ) )
  {
    BCDeviceView* nxtPanel = _devicePanels[deviceID];
    if( nxtPanel != _currentPanel )
    {
      _currentPanel = nxtPanel;
       qDebug() << " --- Firz: " << _currentPanel->property( BCKeyHeaderLabel );
       setHeaderLabel( _currentPanel->property( BCKeyHeaderLabel ).toString() );
      _stackedWidget->setCurrentWidget( nxtPanel );

      // knopf auch abschalten?
    }
  }
}


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

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


/**
 * @brief SLOT, der aufgerufen wird, um das akutelle Device (Battery, Motor, ... )
 * zu synchronisieren, d.h. die aktuellen Werte über den CAN-Bus abzufragen.
 */

void BCMainWindow::onSyncDeviceView()
{

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

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

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

  // alle einzeln? echt jetzt?

  for( const BCValuePtr& value : currentList )
  {
    qDebug() << " --- begin sync of value: " << value->label;
    // wir setzen auf 'lesen'
    value->state.setFlag( BCValue::State::ReadMe );
    // statt '_transmitter.enqueueValueCommand( value )' entkoppeln
    // wir das eleganter über emit requestValueUpdate()
    emit requestValueUpdate( value);

  }

}
