//----------------------------------------------------------------------------
///
/// \file   main.cpp
///
/// \brief  main file for pinmux
///
/// \date   20180129
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//----------------------------------------------------------------------------


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


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


//---General--------------------------

#include <qt5/QtCore/QFile>
#include <QJsonParseError>
#include <QJsonDocument>
#include <QDebug>
#include <QCommandLineParser>
#include <QFileInfo>
#include <QDir>
#include <QString>

//--- Own ----------------------------

#include "../src/board.h"
#include "../src/app.h"
#include "../src/pinmux.h"
#include "app_shared.h"
#include "app_arguments.h"
#include "writerPlatform.h"

#define TEST_ALL
#define STOP_ON_FAIL

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


class CPinmuxTest
{

private:
   CBoard board;
   CApp app;
   CPinmux pinmux;
   int passed;
   int failed;

public:
   CPinmuxTest()
      :passed(0)
      ,failed(0)
   {
      QFileInfo fileInfo(__FILE__);
      QString path( fileInfo.dir().path() );
      pinmux.loadFile( path + "/../../boards/biwak.json" );
      app.setBoardId("test");
   }
   bool runTest(const char *boardArray, const char *boardFileName,
           const char *appArray, const char *appFileName,
           bool shallFail)
   {
      board.clear();
      app.clear();
      if( ( ! board.loadByteArray( QByteArray(boardArray), boardFileName ) )
      ||  ( ! app.loadByteArray( QByteArray(appArray), appFileName ) ) )
      {
         qWarning() << "### FAILED1: " << boardFileName << " / " << appFileName;
         failed++;
         #if defined ( STOP_ON_FAIL )
            qFatal("Finish due to 'STOP_ON_FAIL'");
         #else
         return(true);
         #endif
      }
      bool fail=false;
      pinmux.setBoardApp(board, app);

      try
      {
         pinmux.resolveApp();
      }
      catch( ... )
      {
         fail=true;
      }
      if( fail == shallFail )
         passed++;
      else
      {
         qWarning() << "### FAILED2: " << boardFileName << " / " << appFileName;
         qWarning() << "### Test " << ( fail ? "failed" : "succeeded" ) << " but "
            << ( shallFail ? "should fail" : "should succeed");
         board.dump("Board");
         failed++;
         #if defined ( STOP_ON_FAIL )
            qFatal("Finish due to 'STOP_ON_FAIL'");
         #endif
      }
      pinmux.generateIrqList();

      return(fail);
   }
   void printResult()
   {
      qWarning("Failed: %d", failed);
      qWarning("Passed: %d", passed);
   }
   void testArguments(const QString &type, int index, const QString &value)
   {
      bool done=false;
      for ( const QSharedPointer<CPeriphery> &periphery: board.list())
      {
         if( periphery->isUsed() && ( periphery->getType()==type ) )
         {
            //if(!periphery.getReferenceLink())
            //   qFatal("Periphery not resolved: %s/%d", qPrintable(periphery.getName()), periphery.getId());

            QString isValue=pinmux.getPeripheralArguments(*periphery)[index];
            qWarning()  << "Value: is:" << isValue << " / should:" << value;
            if( isValue == value )
               passed++;
            else
            {
               failed++;
               qWarning()  << "Complete list: " << pinmux.getPeripheralArguments(*periphery);
               #if defined ( STOP_ON_FAIL )
                  board.dump("Foo");
                  qFatal("Finish due to 'STOP_ON_FAIL'");
               #endif
            }
            done=true;
         }
      }
      if(!done)
         failed++;

   }

   void testArguments( QString name, int count )
   {
      CPeriphery *periphery=board.sloppyFindPeripheryByName(name);
      bool success=false;
      int isCount=0;
      if(periphery)
      {
         //success=periphery->getArguments().count()==count;
         isCount=pinmux.getPeripheralArguments(*periphery).count();
         success= ( isCount==count );
      }

      if( success )
         passed++;
      else
      {
         failed++;
         qWarning() << "Expected " << count << " arguments, "
                    << ( periphery
                        ? QString("%1 available").arg( isCount )
                        : "Periphery not found" );
         qWarning() << "String: " << pinmux.getPeripheralArgumentsString(*periphery);
         #if defined ( STOP_ON_FAIL )
            board.dump("Foo");
            qFatal("Finish due to 'STOP_ON_FAIL'");
         #endif
      }
   }

   void testIrqs( int count )
   {
      if(pinmux.getIrqHandlers().size() == count )
         passed++;
      else
      {
         failed++;
         qWarning() << "Expected " << count << " irqs, " << pinmux.getIrqHandlers();
         #if defined ( STOP_ON_FAIL )
            //board.dump("Foo");
            qFatal("Finish due to 'STOP_ON_FAIL'");
         #endif
      }
   }

   void dumpBoard()
   {
      board.dump("Board");
      qWarning() << "Handlers: " << pinmux.getIrqHandlers();
   }

   void testFiles(const QString &boardFile, const QString &appFile)
   {
      if( ! board.loadFile( boardFile ) )
         throw("Could not load board file");
      app.setBoardId( board.getId() );

      if ( ! app.loadFile( appFile ) )
         throw("Could not load app file");

      pinmux.loadFile( boardFile );
      pinmux.setBoardApp(board, app);
      pinmux.resolveApp();
   }
   CPinmux &getPinmux()
   {
      return(pinmux);
   }
};

#define SUCCESS   0
#define FAIL      1

// creates runTest() parameter from json string name; SPLIT(hello) -> hello, "hello.json"
#define SPLIT(a) a, #a ".json"
// # ".json"


int main(int argc, char *argv[])
{
   CPinmuxTest pinmuxTest;



   /*
   qWarning("--------- Board:");
   board.dump("board");
   qWarning("--------- APP:");
   app.dump();
   qWarning("---------");
   */

#if 1
   pinmuxTest.testFiles(
               "/home/mase/wp/src/campo/libarena/boards/st_nucleo_f303.json",
               "/home/mase/wp/src/kastella/arena/apps/kastella_so.json"
            );
   CWriterPlatform writerPlatform( pinmuxTest.getPinmux() );
   writerPlatform.writeHeader("foo.h");
   writerPlatform.writeImplementation("foo.cpp");
   exit(0);

#endif

#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(boardSingleSpi), SPLIT(appDualDisplayShared), SUCCESS );
   pinmuxTest.runTest( SPLIT(boardSingleSpi), SPLIT(appDualDisplayExclusive), FAIL );
   pinmuxTest.runTest( SPLIT(boardSingleSpi), SPLIT(appDualDisplayUnsharedShared), FAIL );
   pinmuxTest.runTest( SPLIT(boardSingleSpi), SPLIT(appDualDisplayShEx), FAIL );
   pinmuxTest.runTest( SPLIT(boardSingleSpi), SPLIT(appDualDisplayShared), SUCCESS );
   pinmuxTest.runTest( SPLIT(boardDualSpi), SPLIT(appDualDisplayExclusive), SUCCESS );
   pinmuxTest.runTest( SPLIT(boardDualSpi), SPLIT(appDualDisplayUnsharedShared), SUCCESS );
   pinmuxTest.runTest( SPLIT(boardDualSpi), SPLIT(appDualDisplayShEx), SUCCESS );
#endif

#if defined ( TEST_ALL )
   // Boards arguments shall be used for periphery if app does not provide them
   pinmuxTest.runTest( SPLIT(boardSingleSpiArguments), SPLIT(appDisplayArguments), SUCCESS );
   pinmuxTest.testArguments("SPI", 1, "Speed2");
   // Apps arguments shall be used for periphery if available
   pinmuxTest.runTest( SPLIT(boardSingleSpiArguments), SPLIT(appDisplay2Arguments), SUCCESS );
   pinmuxTest.testArguments("SPI", 1, "Speed3");
#endif

#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(boardButtons), SPLIT(appButtons), SUCCESS );
   pinmuxTest.testIrqs( 3 );
   //pinmuxTest.dumpBoard();
#endif

#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(boardSpiI2c), SPLIT(appSpiI2C), FAIL );
   pinmuxTest.runTest( SPLIT(boardSpiI2c), SPLIT(appDisplayI2C), FAIL );
#endif

#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(boardFabricate), SPLIT(appFabricate), SUCCESS );
#endif

#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(boardDma), SPLIT(appDma), SUCCESS );
#endif

#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(boardSingleSpi), SPLIT(appDualDisplayUnsharedShared), FAIL );
   pinmuxTest.runTest( SPLIT(boardSingleGpio), SPLIT(appSingleGpio), SUCCESS );
#endif

#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(boardSinglePwm), SPLIT(appSinglePwm), SUCCESS );
   pinmuxTest.dumpBoard();
#endif

#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(boardSingleDac), SPLIT(appSingleDac), SUCCESS );
   pinmuxTest.dumpBoard();
#endif

#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(boardSingleAdc), SPLIT(appSingleAdc), SUCCESS );
   pinmuxTest.testArguments( "ADC_LINE1", 3 );
   pinmuxTest.dumpBoard();
#endif

//#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(board2Gpios), SPLIT(app2GpiosPrefered), SUCCESS );
   pinmuxTest.dumpBoard();
//#endif

#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(boardPwmPrefered), SPLIT(appPwmPrefered), SUCCESS );
   pinmuxTest.dumpBoard();
#endif

#if defined ( TEST_ALL )
   pinmuxTest.runTest( SPLIT(boardEadisplayPrefered), SPLIT(appEadisplayPrefered), SUCCESS );
   pinmuxTest.dumpBoard();
#endif

   pinmuxTest.runTest( SPLIT(board2EadisplayPrefered), SPLIT(app2EadisplayPrefered), SUCCESS );
   pinmuxTest.dumpBoard();

   pinmuxTest.printResult();
   return(0);
}


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