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

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

#include <bcdrivertinycan.h>
#include <can_drv.h>


BCDriverTinyCan::BCDriverTinyCan( QObject* parent )
  : BCDriver(parent )
{

}


BCDriver::DriverState BCDriverTinyCan::loadDriver()
{

  //qDebug() << "CanBusControl " << cbc::Version <<  '\n' << "based on BigXionFlasher (c) 2011-2013 by Thomas Koenig <info@bigxionflasher.org> - www.bigxionflasher.org";

  struct ::TDeviceStatus status;

  if( ::LoadDriver( NULL ) < 0 )
    throw BCException( "Driver Error: 'LoadDriver'" );

   setState(BCDriver::DriverState::Loaded);

  if( ::CanInitDriver( NULL ) < 0 )
    throw BCException( "Driver Error: 'InitDriver'" );

  if( ::CanDeviceOpen( 0, NULL ) < 0 )
    throw  std::runtime_error( "Driver Error: 'DeviceOpen'" );

  ::CanSetSpeed( 0, CAN_125K_BIT );
  ::CanSetMode( 0, OP_CAN_START, CAN_CMD_ALL_CLEAR );
  ::CanGetDeviceStatus( 0, &status );

  setState(BCDriver::DriverState::Initialized);

  if( status.DrvStatus < DRV_STATUS_CAN_OPEN )
    throw BCException( "Driver Error: could not open device." );

  if( status.CanStatus == CAN_STATUS_BUS_OFF )
  {
    ::CanSetMode( 0, OP_CAN_RESET, CAN_CMD_NONE );
    throw BCException( "Driver Error: CAN Status 'BusOff'" );
  }

  setState(BCDriver::DriverState::Ready);

  return BCDriver::DriverState::Ready;

}


/*
    try
    {
        loadDriver();
        initBCDevice::ID::Console();
    }

    catch( std::exception& except )
    {
        ::CanDownDriver();
        ::UnloadDriver();

        // re-throw
        throw BCException( except.what() );
    }
    */


BCDriver::DriverState BCDriverTinyCan::initDriver()
{

  uint32_t console  = static_cast<uint32_t>(BCDevice::ID::Console);
  uint8_t  slaveFlag = static_cast<uint8_t>(BC::ID::Cons_Status_Slave);

  qDebug() << "XXX BCDriverTinyCan::Driver Init: putting BCDevice::ID::Console in slave mode ... ";
  // BCDevice::ID::Console already in slave mode. good!
  if( readRawByte( console, slaveFlag ) )
    return DriverState::Ready;

  qDebug() << "BCDriverTinyCan::BCDriverTinyCan::XXX Driver Init: putting BCDevice::ID::Console in slave mode ... ";

  unsigned int retry = cTimeOuts;
  emit statusHint( "Driver Init: putting BCDevice::ID::Console in slave mode ... " );

  uint32_t isSlave = 0;
  do
  {
    writeRawByte( console, slaveFlag, 1 );
    isSlave = readRawByte( console, slaveFlag );
    bc::delay_millis( 200 );

  } while( retry-- && !isSlave );

  bc::delay_millis( 500 ); // give the Console some time to settle
  //if( !isSlave )
  emit statusHint( QString("putting BCDevice::ID::Console in slave mode ") + (isSlave ? "done" : "failed") );

  // ist das jetzt irgendwie schlimm, wenn wir keine slave BCDevice::ID::Console haben
  //return isSlave ? BCDriver::connected

  return DriverState::Ready;


}


uint32_t BCDriverTinyCan::readRawByte( uint32_t deviceID, uint8_t registerID ) const
{

  uint32_t myRandomByte = static_cast<uint32_t>(QRandomGenerator::global()->bounded(256));
  return myRandomByte;

  if( getState() != DriverState::Ready)
    throw BCException( "readRawValue error: driver not loaded." );

  //uint32_t result = transmitter.readRawValue( deviceID, registerID );

  //value.fromValue<uint32_t>( myRandomByte );

  TCanMsg msg;



  // msg verpacken
  msg.MsgFlags = 0L;
  msg.Id = deviceID;
  msg.MsgLen = 2;
  msg.MsgData[0] = 0x00;
  msg.MsgData[1] = registerID;

  // msg verschicken
  ::CanTransmit( 0, &msg, 1 );

  int retries = cRetries; // 5?
  // cTimeOuts (== 20) mal cTIMEOUT_MS (== 10 ms ) Versuche ...
  int timeOuts = cTimeOuts; // 20 ?

  // ... warten bis der Sendepuffer leer ist
  while( timeOuts-- && ::CanTransmitGetCount( 0 ) )
    bc::delay_millis( cTIMEOUT_MS );

  if( timeOuts == -1 )
    throw BCException( "readRawValue error: could not send value" );

retry:

  // cTimeOuts (== 20) mal cTIMEOUT_MS (== 10 ms ) Versuche ...
  timeOuts = cTimeOuts;
  // ... warten, bis der Empfangspuffer nicht mehr leer ist
  while( timeOuts-- && !::CanReceiveGetCount( 0 ) )
    bc::delay_millis( cTIMEOUT_MS );

  if( timeOuts == -1 )
    throw BCException( "getValue error: no response from node" );

  // message empfangen
  int err = ::CanReceive( 0, &msg, 1 );
  qDebug() << "HÄÄ ?" << err << "reg: "<< registerID <<"  timeOuts: " << timeOuts;

  if( err < 0 )
    //throw BCException( "getValue error: could not receive value" );
    qDebug( "getValue error: could not receive value" );

  qDebug() << "HÄÄ 2" <<msg.Id;
  qDebug() << "HÄÄ 2" <<msg.MsgLen;
  qDebug() << "HÄÄ 2" <<msg.MsgData[1];

  //if( err > 0 )
  if( --retries && ( msg.Id != BIB || msg.MsgLen != 4 || msg.MsgData[1] != registerID ) )
    goto retry;

  if( !timeOuts )
    throw BCException( "CAN --response errror" );

  return (uint32_t) msg.MsgData[3];

}


// void BCDriverTinyCan::setValue( unsigned char receipient, unsigned char reg, unsigned char value )
void BCDriverTinyCan::writeRawByte( uint32_t deviceID, uint8_t registerID ,uint8_t value ) const
{

  if( getState() != DriverState::Ready)
    throw BCException( "writeRawValue error: driver not loaded." );

  qDebug() << " --- BCDriverTinyCan writeRawValue: " << value;
  return;


  struct TCanMsg msg;
  int timeout_count = cTIMEOUT_COUNT;

  msg.MsgFlags = 0L;
  msg.Id = deviceID;
  msg.MsgLen = 4;
  msg.MsgData[0] = 0x00;
  msg.MsgData[1] = registerID;
  msg.MsgData[2] = 0x00;
  msg.MsgData[3] = value;

  ::CanTransmit( 0, &msg, 1 );

  while( timeout_count-- && ::CanTransmitGetCount( 0 ) )
    bc::delay_millis( cTIMEOUT_MS );

  if( timeout_count == -1 )
    emit statusHint( QString( "error: could not send value to %1" ).arg( deviceID ) );

}

