//----------------------------------------------------------------------------
//
// \brief   Minutnik
//
//          The CANDis platform.
//          I2C-Slaves:
//             0x6F: MCP7940 (RTC)
//             0x50: EEPROM
//             0x48: Touch TSC2007
//
// \author  Maximilian Seesslen <mes@seesslen.net>
//
//----------------------------------------------------------------------------


//---Documentation------------------------------------------------------------



//---Includes-----------------------------------------------------------------


#undef LEPTO_LOG_DEBUG

#include <minutnik.hpp>

#include <lepto/log.h>
#include <biwak/biwak.h>
#include <biwak/log_private.h>
#include <biwak/barrel_organ.h>
#include <biwak/output_blink_modes.h>

#include <ocelli/screen_standby.h>
#include <ocelli/screen_alarm.h>
#include <ocelli/screen_navi.hpp>

#include <screen_mainmenu.h>
#include <screen_items.h>
#include <screen_itemList.h>
#include <screen_info.h>
#include <screen_minutnik.h>
#include <screen_wine.h>
#include <screen_chart.h>
#include <screen_settings.hpp>
#include <calibrationApp.h>
#include <settings.h>


//---Defines------------------------------------------------------------------


//---Implementation-----------------------------------------------------------


void SEepromConfigCustom::dump() const
{
   lInfo(" Intf   : %s", interface == EInterface::Usb ? "USB" : "CAN");
   lInfo(" Snc-RTC: %d", fields.periodicSyncRtc);
   lInfo(" Scrsvr : %d", fields.screensaver);
   lInfo(" Tot-Off: %d", fields.totalOff);
   lInfo(" Node-ID: %d", nodeId);
   lInfo(" Buddy0 : %d", buddies[0]);
   lInfo(" Buddy1 : %d", buddies[1]);
};

CMinutnik::CMinutnik()
   :m_externRtc( m_i2c1, m_rtc1 )
   ,m_boBuzzer( &m_pwmBuzzer )
   ,m_boBacklight( &m_pwmBacklight )
   ,m_touch(m_i2c1, m_inputTouchInt)
   #if defined STM32
   ,m_flash(m_spiFlash, m_outputSpiCS.getPeriphery(), m_outputSpiCS.getPin() )
   #else
   ,m_flash( stringify(MEDIA_FILE) )
   #endif
   ,m_ocelliApp( m_gdisplayIli93411, &m_touch
            ,&m_boBacklight, &m_boBuzzer)
   ,m_pScreenMinutnik(nullptr)
   ,m_lrtimerRtcSync( 0 )
{
   m_pCan1 = nullptr;
   m_pUsb1 = nullptr;
   m_pwmBacklight.getTimer().updateFrequency( 1 );
   m_pwmBacklight.setPwmValue( 0 );
   
   m_flash.acquireMemory( CONFIG_MEDIA_FLASH_SIZE );
   m_flash.acquireMemory( CONFIG_FIRMWARE_FLASH_SIZE );
   
   m_pRetainCalibration = new CRetain( m_flash, 0, 1 );
   m_pRetainCalibration->setData( &m_calibration, sizeof(m_calibration)
                  , CALIBRATION_LAYOUT_MAJOR, CALIBRATION_LAYOUT_MINOR);
   return;
}

CMinutnik::~CMinutnik()
{
   return;
}

void CMinutnik::prepareApplication()
{
   loadSettings();
   OIconRef::setFlash( &m_flash );
   m_forceBootLoader= ! isThemeValid();
   
   // Run bootloader anyway:
   // m_forceBootLoader = true;

   lInfo("Media-Theme valid: %s", m_forceBootLoader ? "No" : "Yes");
   
   //m_config.interface = EInterface::Can;
       
   if ( ! m_forceBootLoader )
   {
      introStart();
      setupUi();
      introEnd();
   }
   
   // Perform 'Intro' before interfaces or CAN RX buffer may overflow
   /*
   if ( ( CUSTOM_CONFIG( interface ) == EInterface::Usb ) || m_forceBootLoader || m_forceTouchCalibration )
   {
      lInfo("Starting USB");
      startUSB();
   }
   else
   {
      lInfo("Starting CAN");
      startCAN();
   }
   */
   
   if ( m_forceBootLoader )
   {
      // Don't use the USB-Serial for debug output
      setStdio( &m_uartDebug );
      #if defined STM32
         m_pPhonyBl = new CPhonyBL( *m_pUsbHid );
         setupBootloaderUi();
      #endif
   }

   #if defined STM32
   if( m_forceTouchCalibration )
   {
      // USB is already stdout
      performTouchCalibration();
   }
   #else
      // m_ocelliApp.setCurrentScreen( m_pScreenMinutnik );
      // m_ocelliApp.setCurrentScreen( m_pScreenWine );
      m_ocelliApp.setCurrentScreen( m_pScreenMinutnik );
   #endif
   
   logEventLoop();
}


void CMinutnik::setupEepromConfigs()
{
   // Thats already done in constructor.
   //configEepromAddressWidth();
   #if IS_ENABLED( CONFIG_VAGUS_USE_BOOTCOUNTER )
      bool faultyConfig=false;
   #endif

   #if 0 // Does not make sense here; headers already read in constructor
   #if ! IS_ENABLED( BIWAK_USE_I2C_RETRIES )
      // It happened that up to 1 second after reset the i2c eeprom could not
      // be read. If this is not catched by retries, test-read data till
      // successfull.
      char data8;
      while( m_eeprom1.readData( 0, &data8, 0x1 ) ){}
      m_eeprom1.dump();
   #endif
   #endif

   if( m_pRetainHwData->restore() < 0 )
   {
      lCritical( LDS("CNRHW", "Could not restore HWData") );
   }

   if( m_pRetainConfig->restore() < 0 )
   {
      lCritical( LDS("CNRCF", "Could not restore Config") );
      setDefaultConfig();
   }

   // Bad, but could be stored in EEPROM somehow
   if( ( VAGUS_CONFIG( nodeId ) == 0 ) || ( VAGUS_CONFIG( nodeId ) > 127 ) )
   {
      //exception();
      VAGUS_CONFIG( nodeId ) = 5;
   }
}

int CMinutnik::storeConfig()
{
   int container=m_pRetainConfig->store();
   if( container < 0 )
   {
      lCritical( LDS( "CNSC", "Could not store config" ) );
   }

   return( container );
}


int CMinutnik::restoreConfig()
{
   int container=m_pRetainConfig->restore();
   if( container < 0 )
   {
      lCritical( LDS( "CNRC", "Could not restore config" ) );
   }

   return( container );
}


int CMinutnik::resetConfig()
{
   return( 0 );
}


void CMinutnik::loadSettings()
{
   int container;
   biwakEventLoop();
   setupEepromConfigs();
   
   biwakEventLoop();
   loadCalibrations();
   container=m_pRetainConfig->restore();

   if( ( container = restoreConfig() ) < 0 )
   {
      lWarning("Reset Config");
      resetConfig();
   }
   else
   {
      lInfo("Restore config from Container %d", container);
      //CUSTOM_CONFIG( dump() );
   }

   return;
}

void CMinutnik::startCAN()
{
   m_outputSelect.setValue( SELECT_USE_CAN );
   instantiateCan1();
   setStdio( &m_uartDebug );
}

void CMinutnik::startUSB( )
{
   m_outputSelect.setValue( SELECT_USE_USB );
   instantiateUsb1();
   #if defined STM32
   m_pUsbDevice=new CUsbDevice( *m_pUsb1 );
   m_pUsbHid=new CUsbClass( *m_pUsb1, *m_pUsbDevice, usbClassCdc);
   m_pUsbDevice->registerClass( m_pUsbHid );
   m_pUsb1->start( m_pUsbDevice );
   
   // Setting USB to stdout blocks when device is not opened from host.
   setStdio( m_pUsbHid );
   #endif
}

void CMinutnik::introStart()
{
   m_pwmBacklight.setPwmValue(0);
   const OIconRef& ref = iconRef_minutnikSplash_flash;
   
   if( ref.find( ) )
   {
      ref.draw(0,0);
   }
   else
   {
      lWarning("Could not find splash screen");
      return;
   }
   CBarrelOrganFade bo( &m_pwmBacklight );
   bo.setFadeMode( OApp::fadeIn );
   while( bo.isActive() )
   {
      bo.eventLoop();
   }
#if 1
   lrtimer_t t=lrNow();
   while( ! lrElapsedMSeconds( &t, MSEC_PER_SEC / 3 ) )
   {
      bo.eventLoop();
   }
   bo.setFadeMode( OApp::fadeOut );
   while( bo.isActive() )
   {
      bo.eventLoop();
   }
#endif
}

void CMinutnik::introEnd()
{
}

void CMinutnik::addScreen( OScreen* screen, EScreen escreen )
{
   m_screenList[ (int)escreen ] = screen;
}


void CMinutnik::setupUi()
{
   m_ocelliApp.setNaviScreen( m_pScreenNavi = new OScreenNavi );
   m_pScreenStandby = new OScreenStandby( &m_rtc1 );
   m_ocelliApp.setStandbyScreen( CUSTOM_CONFIG( fields.screensaver ) ? m_pScreenStandby : nullptr);
   m_pScreenStandby->m_signalLeaveStandby.connect(this, &CMinutnik::leaveStandby );
   
#if 0
   const SIconDesc desc( EImageType::Splash, EImageSize::FullScreen, EImageContent::WineSplash);
   OIconRef ref( desc );
   if( ref.find() )
   {
      // The buttons of navigation screen are actually 60 pixels high
      // The splash screen is 240x256
      ref.draw(0,0);
   }
   else
   {
      lWarning( "Splash not found" );
   }
#endif

   addScreen( m_pScreenMinutnik=new CScreenMinutnik( m_boBuzzer ), EScreen::minutnik );
   addScreen( m_pScreenAlarm=new OScreenAlarm, EScreen::alarm );
   addScreen( m_pScreenItems=new CScreenItems, EScreen::items );
   addScreen( m_pScreenItemList=new CScreenItemList( *m_pScreenMinutnik ), EScreen::itemList );
   addScreen( m_pScreenInfo=new CScreenInfo( m_hwData.boardInfo ), EScreen::info);
   addScreen( m_pScreenMainMenu=new CScreenMainMenu, EScreen::mainMenu );
   
   m_pScreenAlarm->setSelectable( false );
   m_pScreenMainMenu->signalSetScreen.connect(this, &CMinutnik::slotSetScreen);

   addScreen( m_pScreenSettings=new CScreenSettings( CUSTOM_CONFIG_INSTANCE ), EScreen::settings );
   m_pScreenSettings->signalSettingsChanged.connect(this, &CMinutnik::slotSettingsChanged);

   m_pScreenMinutnik->m_signalAlarm.connect(this, &CMinutnik::slotAlarm);
   m_pScreenItemList->m_signalAlarm.connect(this, &CMinutnik::slotAlarm);
   m_pScreenMinutnik->signalSetScreen.connect(this, &CMinutnik::slotSetScreen);

   m_pScreenNavi->m_buttonBack.eventSignal.connect(this, &CMinutnik::slotBack);
   m_pScreenNavi->m_buttonInfo.eventSignal.connect(this, &CMinutnik::slotInfo);
   m_pScreenNavi->m_buttonMenu.eventSignal.connect(this, &CMinutnik::slotMenu);
   m_pScreenNavi->m_buttonStandby.eventSignal.connect(this, &CMinutnik::slotStandby);
   m_pScreenItems->signalAddItem.connect(m_pScreenMinutnik, &CScreenMinutnik::slotAddItem);
   
   m_pScreenAlarm->buttonOk().eventSignal.connect(this, &CMinutnik::slotQuitAlarm);

   m_ocelliApp.setCurrentScreen( m_pScreenStandby );
   
   return;
}

void CMinutnik::eventLoop()
{
   if ( m_pCan1 != nullptr )
   {
      //CVagus::eventLoop();
   }
   
   m_boBuzzer.eventLoop();
   m_boBacklight.eventLoop();
   m_touch.eventLoop();

   // Must not use vagus can eventloop
   if ( m_pCan1 != nullptr )
   {
      m_pCan1->eventLoop();
      SCanMessage *message;
      #if IS_ENABLED( CONFIG_VAGUS_CAN_EVENTLOOP )
         #error This is obsolete. Vagus handles CAN messages.
      #endif
      while( ( message=m_pCan1->m_rxBuffer.bottomEntry() ) )
      {
         m_ledUser.setBlinkMode( BlinkModes::shot );
         //digest( message );
         lInfo( "CAN RX ID=0x%X; LEN=%d", message->getId(), message->getLen() );
         signalCANMessage1.emitSignal( message );
         signalCANMessage2.emitSignal( message );
         m_pCan1->m_rxBuffer.dropEntry();
      }
   }
   
   m_ocelliApp.eventLoop();

   if( m_pScreenMinutnik && ( m_ocelliApp.getCurrentScreen() != m_pScreenMinutnik ) )
   {
      m_pScreenMinutnik->hiddenEventLoop();
   }

   #if defined STM32
      if( m_pUsbHid )
      {
         m_pUsbHid->eventLoop();
      }
   #endif
   
   #if 0
   if( m_pPhonyBl )
   {
      m_ledUser.eventLoop();
      #if defined STM32
         m_pUsbHid->eventLoop();
         
         if( m_pUsbHid->dataAvailable())
         {
            m_ledUser.setBlinkMode( BlinkModes::shot );
            m_pPhonyBl->eventLoop();
         }
      #endif
   }
   #endif
   
   if( CUSTOM_CONFIG( fields.periodicSyncRtc ) )
   {
      if(lrElapsedSeconds( &m_lrtimerRtcSync, SEC_PER_MIN * 15 ))
      {
         m_externRtc.hwToRtc();
      }
   }
}


int CMinutnik::printHWInfo()
{
   int container;
   int sta=0;

   container=m_pRetainHwData->restore();
   if( container<0 )
   {
      fputs("Could not load eeprom data\n", stdout);
      sta=-1;
   }
   else
   {
      printf("Container:   %d", container);
      // printf("Data size:   0x%X", m_pRetainHwData->getTotalSize());
      // printf("Spare:       0x%X", m_pRetainHwData->spare());
      printf("ArticleID:   0x%X", m_hwData.boardInfo.main.articleId);
      printf("Boardcode:   0x%X", m_hwData.boardInfo.main.boardCode);
      printf("Revision :   0x%X", m_hwData.boardInfo.main.boardRevision);
      printf("Variant:     0x%X", m_hwData.boardInfo.product.variant);
      printf("Serialno.:   0x%X", (int)m_hwData.boardInfo.production.serialNumber);
   }

   return(sta);
}


void CMinutnik::slotAlarm(const CMinutnikCore::SItem &item)
{
   m_pScreenAlarm->setTexts(item.description, item.finished);
   m_boBuzzer.setBeepMode( Barrel::Beep::sireneFast );
   m_ocelliApp.setCurrentScreen(m_pScreenAlarm);
   return;
}


void CMinutnik::slotBack(const Ocelli::SEvent &event)
{
   m_ocelliApp.setPreviousScreen();
   return;
}


void CMinutnik::slotInfo(const Ocelli::SEvent &event)
{
   m_ocelliApp.setCurrentScreen(m_pScreenInfo);
   return;
}


void CMinutnik::slotMenu(const Ocelli::SEvent &event)
{
   m_ocelliApp.setCurrentScreen(m_pScreenMainMenu);
   return;
}


void CMinutnik::slotStandby(const Ocelli::SEvent &event)
{
   m_ocelliApp.setCurrentScreen(m_pScreenStandby);
   return;
}


void CMinutnik::slotSetScreen(const EScreen screen)
{
   lDebug("Switch to screen: '%d'", (int)screen);

   if( (int)screen < (int)EScreen::end )
   {
      if( m_screenList[ (int)screen ] )
      {
         m_ocelliApp.setCurrentScreen( m_screenList[ (int)screen ] );
      }
      else
      {
         lWarning("Screen not in list: %d", (int)screen);
      }
   }
   else
   {
      lWarning("Unknown screen id: %d", (int)screen);
   }

   return;
}


void CMinutnik::slotQuitAlarm(const Ocelli::SEvent& event)
{
   m_boBuzzer.stop();
   OApp::instance()->setPreviousScreen();
}


int CMinutnik::commandConfig(int argc, const char *argv[])
{
   bool showUsage=false;
   if(argc==1)
   {
      CUSTOM_CONFIG( dump() );
   }
   else if(argc>=2)
   {
      if( !strcmp(argv[1], "reset") )
      {
         resetConfig();
      }
      if( !strcmp(argv[1], "can") )
      {
         CUSTOM_CONFIG( interface ) =EInterface::Can;
         storeConfig();

      }
      if( !strcmp(argv[1], "usb") )
      {
         CUSTOM_CONFIG( interface ) =EInterface::Usb;
         storeConfig();
      }
      if( !strcmp(argv[1], "nodeid") )
      {
         if(argc>=3)
         {
            showUsage=true;
         }
         else
         {
            char * pEnd;
            int nodeId = strtol (argv[2],&pEnd,0);
            if( nodeId==0 )
            {
               lWarning("NodeID must be > 0\n");
            }
            else
            {
               m_config.nodeId=nodeId;
               storeConfig();
            }
         }
      }
      else
      {
         showUsage=true;
      }
   }

   if(showUsage)
   {
      usageConfig();
   }
   return(0);
}


void CMinutnik::setDefaultConfig()
{
#if 0
   CUSTOM_CONFIG( nodeId ) =0x55;
   CUSTOM_CONFIG( buddies[0] ) =0x52;      // Living room
   CUSTOM_CONFIG( buddies[1] ) =0x51;      // Sleeping room
   CUSTOM_CONFIG( interface ) = EInterface::Can;
   CUSTOM_CONFIG( fields.screensaver ) = true;
#endif
   return;
}


void CMinutnik::usageConfig()
{
   printf("Usage: \n"
          "   config                Show current configuration\n"
          "   config reset          Reset the configuration to defaults\n"
          "   config nodeid <id>    Set NodeID\n"
          "   config <usb|can>      Set data interface to either CANBus or USB\n"
   );
}

#if 0
void CMinutnik::setupBootloaderUi( )
{
   //m_pScreenBootloader=new CScreenBootloader( m_pPhonyBl, m_flash.size() );

   m_ocelliApp.setCurrentScreen( m_pScreenBootloader );
   // Fake writing a single byte to adjust flash level
   m_pScreenBootloader->slotWrite( OIconRef::usage() - 1 );
}
#endif

void CMinutnik::slotReboot(const Ocelli::SEvent& event)
{
   if( event.type == Ocelli::EEventType::clicked )
   {
      systemReset();
   }
}

#if 0

void CMinutnik::slotSwitch(enum CScreenIllumination::ESwitch _switch, bool value)
{
   Tdt::EObject object;
   Tdt::EUnit unit=Tdt::EUnit::permilPwm;
   Tdt::SValue messageValue;
   messageValue._int=value*1000;

   switch(_switch)
   {
      case( CScreenIllumination::ESwitch::eMain):
      {
         if( CUSTOM_CONFIG( fields.totalOff ) )
         {
            object=Tdt::EObject::powerSwitchMain;
            break;
         }
         else
         {
            slotSwitch( CScreenIllumination::ESwitch::eTv, false);
            slotSwitch( CScreenIllumination::ESwitch::ePlant, false);
            m_pScreenIllumination->slotIlluminationChanged(0);
            m_pScreenIllumination->m_pButtonSwitchPlants->setSwitchValue(false);
            m_pScreenIllumination->m_pButtonSwitchTv->setSwitchValue(false);
            m_pScreenIllumination->m_slider->setSliderValue(0);
            return;
            break;
         }
      }
      case( CScreenIllumination::ESwitch::eTv):
         object=Tdt::EObject::powerSwitchMultimedia;
         unit=Tdt::EUnit::_switch;
         messageValue._bool=value;
         break;
      case( CScreenIllumination::ESwitch::ePlant):
         object=Tdt::EObject::powerSwitchPlantLight;
         break;
      default:
         lWarning("Unknown switch: %d", _switch);
         return;
         break;
   }

   Tdt::CMessage message( 0x20,
                          Tdt::EFunctionCode::writeObjectBroadCast,
                           object,
                           unit,
                           messageValue );
   #if defined (STM32)
      if( m_pCan1 )
      {
         m_pCan1->transmit( message );
      }
   #endif

}

#endif

void CMinutnik::leaveStandby()
{
   m_ocelliApp.setPreviousScreen();
   return;
}

#if 0
bool CMinutnik::digestSendObject( const Tdt::CMessage &msg )
{
   //CVagus::digestSendObject( msg );
   
   lDebug("SendObject; ID: %d; OBJ: %d; Unit: %d", msg.getNodeId(), msg.getTdtObject()
         , msg.getTdtUnit());
   
   switch( msg.getTdtObject() )
   {
      case Tdt::EObject::environmentTemperature0 ... Tdt::EObject::environmentTemperature2:
      {
         int index=msg.getTdtObject() - Tdt::EObject::environmentTemperature0;
         lDebug("Temp.: %d", msg.getTdtValue()->_int );
         if( ( msg.getNodeId() == CUSTOM_CONFIG( buddies[0] ) ) &&
             ( index == CUSTOM_CONFIG( buddies_index[0] ) ) )
         {
            if( msg.getTdtUnit() == Tdt::EUnit::centiCelsius )
            {
               m_pScreenStandby->setTemperature1( msg.getTdtValue()->_int );
               int id=msg.getNodeId();
               m_pScreenThermometer->addTemperature(
                        id, msg.getTdtValue()->_int );
               m_pScreenChart->addTemperature(
                        id, msg.getTdtValue()->_int );
            }
         }
         if( ( msg.getNodeId() == CUSTOM_CONFIG( buddies[1] ) ) &&
             ( index == CUSTOM_CONFIG( buddies_index[1] ) ) )
         {
            if( msg.getTdtUnit() == Tdt::EUnit::centiCelsius )
            {
               lInfo( "Temp2" );
               m_pScreenStandby->setTemperature2( msg.getTdtValue()->_int );
            }
         }
         break;
      }
      case Tdt::EObject::environmentFoodTemperature:
         m_pScreenStandby->setTemperature3( msg.getTdtValue()->_int );
         OApp::instance()->_touchEvent();
         lInfo("Food %d; %d°C", msg.getNodeId(), msg.getTdtValue()->_int );
         break;
         
      default:
      {
         // lWarning( "UKOB: 0x%X", (int)msg.getTdtObject() );
         return(false);
         break;
      }
   }
   return(true);
}
#endif

#if 0

/**
 * @brief   Custom digestWriteObject from vagus
 * @param   msg   the TDT message
 * @return  true: value was interpreted / object was written
 *          false: object unknown
 */
bool CMinutnik::digestWriteObject( const Tdt::CMessage &msg )
{
   int targetValue = msg.getTdtValue()->_int;

   switch( msg.getTdtObject() )
   {
      case (Tdt::EObject::date):
      case (Tdt::EObject::time):
      {
         // Values have already been interpreted by vagus
         m_externRtc.rtcToHw();
         break;
      }
      case Tdt::EObject::displayNode0 ... Tdt::EObject::displayNode2:
      {
         CUSTOM_CONFIG( buddies[ msg.getTdtObject() - Tdt::EObject::displayNode0 ] )
                     = targetValue;
         storeConfig();
         break;
      }
      case Tdt::EObject::displayNodeObjectOffset0 ... Tdt::EObject::displayNodeObjectOffset2:
      {
         CUSTOM_CONFIG( buddies_index[ msg.getTdtObject() - Tdt::EObject::displayNodeObjectOffset0 ] )
                     = targetValue;
         storeConfig();
         break;
      }
      case( Tdt::EObject::powerSwitchMultimedia ):
      {
         m_pScreenIllumination->m_pButtonSwitchTv->setSwitchValue( msg.getTdtValue()->_bool );
         break;
      }
      case( Tdt::EObject::powerSwitchPlantLight ):
      {
         m_pScreenIllumination->m_pButtonSwitchPlants->setSwitchValue( msg.getTdtValue()->_uint > 0 ? true : false );
         break;
      }
      case(Tdt::EObject::powerSwitchAmbientLightAll):
      case(Tdt::EObject::powerSwitchAmbientLightLivingRoom):
      {
         m_pScreenIllumination->m_slider->setSliderValuePromil(
             msg.getTdtValue()->_uint, false );
         lInfo("Someon set ambient light to %d promil",
               msg.getTdtValue()->_uint );
         break;
      }
      default:
      {
         return(false);
         break;
      }
   }
      
      return(true);
}

#endif

void CMinutnik::slotSettingsChanged()
{
   storeConfig();
   //m_config.dump();

   m_ocelliApp.setStandbyScreen( CUSTOM_CONFIG( fields.screensaver ) ? m_pScreenStandby : nullptr);
}


bool CMinutnik::isBootloaderActive()
{
   //return( m_pPhonyBl != nullptr );
   return(false);
}


bool CMinutnik::isAlarmActive()
{
   return( m_boBuzzer.isActive() );
}

#if 0
void CMinutnik::sendConfig() /* virtual override final */
{
   // NodeID is send by vagus
   
   return;
}
#endif

int CMinutnik::eraseFlash(int argc, const char *argv[])
{
   lInfo("Erasing media flash");
   m_flash.eraseChip();
   return(0);
}


void CMinutnik::loadCalibrations()
{
   int container;
   container=m_pRetainCalibration->restore();
   if( m_inputTouchInt.getValue() == false )
   {
      m_forceTouchCalibration=true;
   }
   
   if( ( container < 0 ) || m_forceTouchCalibration )
   {
      lInfo("Touch calibration invalid");
      // Either have the calibration app included or use fallback values
      #if 1
         // Calibrate when 
         performTouchCalibration();
      #else
         m_touch.calibration().setCalibration(
            m_gdisplayIli93411.width(), m_gdisplayIli93411.height(),
            0x0D45, 0x024D, 0x0DE4, 0x0166 );
      #endif
   }
   else
   {
      lInfo("Touch calibration valid");
      // Old default values: 0x0D45, 0x024D, 0x0DE4, 0x0166
      m_touch.calibration().setCalibration(
            m_gdisplayIli93411.width(), m_gdisplayIli93411.height(),
            m_calibration.xLeft, m_calibration.xRight,
            m_calibration.yTop, m_calibration.yBottom );
      lInfo("   %dx%d", m_gdisplayIli93411.width(), m_gdisplayIli93411.height() );
      lInfo("   X: 0x%X - 0x%X", m_calibration.xLeft, m_calibration.xRight );
      lInfo("   Y: 0x%X - 0x%X", m_calibration.yTop, m_calibration.yBottom);
   }
   return;
}


void CMinutnik::performTouchCalibration()
{
   m_pwmBacklight.setPwmValue(1000);
   CCalibrationApp app( getDisplay(), getTouch() );
   
   // stdout is forced to be on USB so you have access to it even if device is
   // assembled completely.
   // setStdio( &m_uartDebug );
   
   while(! app.finished() )
   {
      app.eventLoop();
      //eventLoop();
      biwakEventLoop();
   }
   m_calibration.xLeft=app.getCalibrationXLeft();
   m_calibration.xRight=app.getCalibrationXRight();
   m_calibration.yTop=app.getCalibrationYTop();
   m_calibration.yBottom=app.getCalibrationYBottom();
   m_pRetainCalibration->store();
   systemReset();
}


int CMinutnik::forceTouchCalibration(int argc, const char *argv[])
{
   lInfo("forcing touch calibration");
   m_pRetainCalibration->destroyAll();
   systemReset();
   
   return(0);
}


//---fin----------------------------------------------------------------------
