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


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


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


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

#define CATCH_CONFIG_MAIN

#if defined ( CATCH2 )
#include <catch2/catch_test_macros.hpp>
#elif defined ( CATCH )
#include <catch/catch.hpp>
#else
#error "Either 'catch' or 'catch2' has to be installed"
#endif

#include <QObject>
#include <QDebug>

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

#include <pinmux.h>
#include <app.h>
#include <board.h>
#include "formaterCpp.h"
#include "writerPlatform.h"

#define TEST_ALL
#define STOP_ON_FAIL


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

#define SPLIT(a) a, #a ".json"

TEST_CASE( "sometest", "[default]" )
{
   CPinmux pinmux;
   CApp app;
   CBoard board;

   SECTION( "sometest, forgot" )
   {

      const char *boardString=
      R"JSON(
      {
          "name": "Nucleo_f303_1",
          "version": "1",
          "peripheries": [
                  { "type": "GPIO",   "periphery": "PA12", "pin": "D1", "irq": "EXTI9_5_IRQHandler" },
                  { "type": "GPIO",   "periphery": "PA11", "pin": "D0", "irq": "EXTI15_10_IRQHandler" },
                  { "type": "GPIO",   "periphery": "PA1", "pin": "D0", "irq": "EXTI15_10_IRQHandler" },
                  { "type": "GND", "name": "GND", "pin": "GND", "mode": "fabricate",
                           "attributes": { "pseudo": "yes" } },
                  { "type": "3V3", "name": "3V3", "pin": "3V3", "mode": "fabricate",
                           "attributes": { "pseudo": "yes", "type": "power" } },
                  {
                     "type": "CAN", "periphery": "CAN1.PA11", "irqs": "CEC_CAN_IRQHandler",
                     "consumes": [
                         { "periphery": "PA12",   "name": "TX", "mode": "skip" },
                         { "periphery": "PA11",   "name": "RX", "modeSPI1_PA12_PA11": "skip" }
                     ],
                     "attributes": {
                         "configBlock": "CAN1_PA12_PA11"
                     }
                  }
         ],
         "constructors": [
              {
                  "type": "CAN",
                  "arguments": [
                          { "type": "fixed", "value": "canConfig_<attribute:configBlock>" },
                          { "type": "fixed", "value": "HELLO_WORLD" }
                  ]
              },
              {
                  "type": "GPIO",
                  "arguments": [
                           { "type": "fixed", "value": "eGpioPeripheral<PeripheryBlock>, eGpioPin<PeripheryNumber>" }
                  ]
               }
         ],
         "final": "yes"
      }
      )JSON";
      board.loadByteArray( QByteArray(boardString), "intern" );

      app.loadByteArray(
               QByteArray(R"JSON(
               {
                   "name": "canio",
                   "periphery": [
                      { "type": "CAN", "name": "1" },
                      { "type": "GPIO", "name": "1" },
                      { "type": "GND", "name": "gnd" },
                      { "type": "3V3", "name": "3v3" }
                   ]
               }
      )JSON" ), "intern" );

      pinmux.setBoardApp(board, app);
      CConstructorPool constructors;
      constructors.loadByteArray( QByteArray(boardString), "intern" );
      CWriterPlatform platform(pinmux, constructors, "");

      try
      {
         pinmux.resolveApp();
      }
      catch( ... )
      {
         qWarning("Failed");
      }

      CPeripheryPtr pPeriphery=board.findPeripheryByPeripheryName( QString("CAN1.PA11") );
      REQUIRE( pPeriphery != nullptr );

      qWarning() << "END: " << platform.getPeripheralArgumentsString( *pPeriphery );

      int i1=0;
      for ( const QSharedPointer<CPeriphery> &periphery: board.listConst())
      {
         i1++;
         if(periphery->isUsed())
         {
            if( periphery->getAttribute( "pseudo" ) == "yes" )
               continue;

            if( periphery->doInstantiate() /*&& ( periphery.getType()==type ) */ )
            {
               QString arguments=platform.getPeripheralArgumentsString( *periphery );
               QString line;
               if(arguments=="Yes")
                  arguments.clear();
               line+=QString("%1(%2) /* %3 */").arg(platform.getInstanceName(*periphery)).arg(arguments).arg(periphery->getOrder());
               qWarning() << "      " // << ( isFirst ? ":" : "," )
                   << line << " " << i1 << "\n";
            }
         }
      }
   } // SECTION
};

TEST_CASE( "Constructors", "[default]" ) {
   CPinmux pinmux;
   CApp app;
   CBoard board;

   SECTION( "configBlock, CAN, GPIO" )
   {
      const char *boardString=
      R"JSON(
      {
          "name": "Nucleo_f303_2",
          "version": "1",
          "peripheries": [
                  { "type": "GPIO",   "periphery": "PA12", "pin": "D1", "irq": "EXTI9_5_IRQHandler" },
                  { "type": "GPIO",   "periphery": "PA11", "pin": "D0", "irq": "EXTI15_10_IRQHandler" },
                  { "type": "GPIO",   "periphery": "PA1", "pin": "D0", "irq": "EXTI15_10_IRQHandler" },
                  { "type": "GND", "name": "GND", "pin": "GND", "mode": "fabricate",
                           "attributes": { "pseudo": "yes" } },
                  { "type": "3V3", "name": "3V3", "pin": "3V3", "mode": "fabricate",
                           "attributes": { "pseudo": "yes" } },
                  {
                     "type": "CAN", "periphery": "CAN1.PA11", "irqs": "CEC_CAN_IRQHandler",
                     "consumes": [
                         { "periphery": "PA12",   "name": "TX", "mode": "skip" },
                         { "periphery": "PA11",   "name": "RX", "mode": "skip" }
                     ],
                     "attributes": {
                         "configBlock": "CAN1_PA12_PA11"
                     }
                  }
         ],
         "constructors": [
              {
                  "type": "CAN",
                  "arguments": [
                          { "type": "fixed", "value": "canConfig_<attribute:configBlock>" },
                          { "type": "fixed", "value": "HELLO_WORLD" }
                  ]
              },
              {
                  "type": "GPIO",
                  "arguments": [
                           { "type": "fixed", "value": "eGpioPeripheral<PeripheryBlock>, eGpioPin<PeripheryNumber>" }
                  ]
               }
         ],
         "final": "yes"
      }
      )JSON";
      board.loadByteArray( QByteArray(boardString), "intern" );

      app.loadByteArray(
               QByteArray(R"JSON(
               {
                   "name": "canio",
                   "periphery": [
                      { "type": "CAN", "name": "1" },
                      { "type": "GPIO", "name": "1" },
                      { "type": "GND", "name": "gnd" },
                      { "type": "3V3", "name": "3v3" }
                   ]
               }
      )JSON" ), "intern" );

      pinmux.setBoardApp(board, app);
      pinmux.resolveApp();
      CConstructorPool constructors;
      constructors.loadByteArray( QByteArray(boardString), "intern" );

      CWriterPlatform platform(pinmux, constructors, "");
      CPeripheryPtr p=board.findPeripheryByPeripheryName( QString("CAN1.PA11") );
      REQUIRE( platform.getPeripheralArgumentsString( *p )
               == "canConfig_CAN1_PA12_PA11, HELLO_WORLD");
      p=board.findPeripheryByPeripheryName( QString("PA1") );
      REQUIRE( platform.getPeripheralArgumentsString( *p )
               == "eGpioPeripheralA, eGpioPin1");
   }

   SECTION( "configBlock, SPI, speed attribute from periphery" )
   {
      const char *boardString=
      R"JSON(
      {
          "name": "Nucleo_f303_3",
          "version": "1",
          "peripheries": [
                  { "type": "GPIO",   "periphery": "PA12", "pin": "D1", "irq": "EXTI9_5_IRQHandler" },
                  { "type": "GPIO",   "periphery": "PA11", "pin": "D0", "irq": "EXTI15_10_IRQHandler" },
                  {
                     "type": "SPI", "periphery": "SPI1.PA11", "irqs": "SPI_IRQHandler",
                     "consumes": [
                         { "periphery": "PA12",   "name": "TX", "mode": "skip" },
                         { "periphery": "PA11",   "name": "RX", "mode": "skip" }
                     ],
                     "attributes": {
                         "configBlock": "SPI1_PA12_PA11",
                         "speedo": "10Khz",
                         "implementation": "10Khz"
                     }
                  }
         ],
         "constructors": [
                 {
                     "type": "SPI",
                     "arguments": [
                             { "type": "fixed", "value": "spiConfig_<attribute:configBlock>" },
                             { "type": "fixed", "value": "spiConfig_<attribute:speedo>" }
                     ]
                 }
         ],
         "final": "yes"
      }
      )JSON";

      app.clear();
      board.clear();
      board.loadByteArray( QByteArray(boardString), "intern_SPI" );
      app.loadByteArray(
               QByteArray(R"JSON(
               {
                   "name": "canio",
                   "periphery": [
                      { "type": "SPI", "name": "1",
                           "attributes": {
                               "XXspeedo": "24Mhz"
                           }
                      }
                   ]
               }
      )JSON" ), "intern" );

      pinmux.setBoardApp(board, app);
      pinmux.resolveApp();
      CConstructorPool constructors;
      constructors.loadByteArray( QByteArray(boardString), "intern" );
      CWriterPlatform platform(pinmux, constructors, "");
      CPeripheryPtr p=board.findPeripheryByPeripheryName( QString("SPI1.PA11") );

      qWarning() << "OUTPUT: " << platform.getPeripheralArgumentsString( *p );

      REQUIRE( platform.getPeripheralArgumentsString( *p )
               == "spiConfig_SPI1_PA12_PA11, spiConfig_10Khz");
   }
   SECTION( "configBlock, implementation" )
   {
      const char *boardString=
      R"JSON(
      {
          "name": "Nucleo_f303_4",
          "version": "1",
          "peripheries": [
                  { "type": "GPIO",   "periphery": "PA12", "pin": "D1", "irq": "EXTI9_5_IRQHandler" },
                  { "type": "GPIO",   "periphery": "PA11", "pin": "D0", "irq": "EXTI15_10_IRQHandler" },
                  {
                     "type": "SPI", "periphery": "SPI1.PA11", "irqs": "SPI_IRQHandler",
                     "consumes": [
                         { "periphery": "PA12",   "name": "TX", "mode": "skip" },
                         { "periphery": "PA11",   "name": "RX", "mode": "skip" }
                     ],
                     "attributes": {
                         "configBlock": "SPI1_PA12_PA11",
                         "speedo": "10Khz",
                         "implementation": [
                              "const SCanConfig canConfig_CAN1_PA12_PA11",
                              "{",
                              "   .can              = CAN,",
                              "};"
                         ]
                     }
                  }
         ],
         "constructors": [
                 {
                     "type": "SPI",
                     "arguments": [
                             { "type": "fixed", "value": "spiConfig_<attribute:configBlock>" },
                             { "type": "fixed", "value": "spiConfig_<attribute:speedo>" }
                     ]
                 }
         ],
         "final": "yes"
      }
      )JSON";

      app.clear();
      board.clear();
      board.loadByteArray( QByteArray(boardString), "intern_SPI" );
      app.loadByteArray(
               QByteArray(R"JSON(
               {
                   "name": "canio",
                   "periphery": [
                      { "type": "SPI", "name": "1",
                           "attributes": {
                               "XXspeedo": "24Mhz"
                           }
                      }
                   ]
               }
      )JSON" ), "intern" );

      pinmux.setBoardApp(board, app);
      CConstructorPool constructors;
      constructors.loadByteArray( QByteArray(boardString), "intern" );
      CWriterPlatform platform(pinmux, constructors, "");
      pinmux.resolveApp();

      CPeripheryPtr p=board.findPeripheryByPeripheryName( QString("SPI1.PA11") );
      qDebug() << p->getJsonObject();
      REQUIRE( p->getAttribute( "implementation" ) == "const SCanConfig canConfig_CAN1_PA12_PA11\n{\n   .can              = CAN,\n};\n");
      REQUIRE( platform.getPeripheralArgumentsString( *p )
               == "spiConfig_SPI1_PA12_PA11, spiConfig_10Khz");
   }
   SECTION( "configBlock, consumed direct" )
   {
      const char *boardString=
      R"JSON(
      {
          "name": "Testcase_OSC",
          "version": "1",
          "peripheries": [
                  { "type": "GPIO",   "periphery": "PA12", "pin": "D1", "irq": "EXTI9_5_IRQHandler" },
                  { "type": "GPIO",   "periphery": "PA11", "pin": "D0", "irq": "EXTI15_10_IRQHandler" },
                  {
                    "type": "OSC32", "name": "LSE", "periphery": "OSC32_PC14",
                    "consumes": [
                         { "periphery": "PA12",  "name": "OSC32_IN", "pin": "OSC#4" },
                         { "periphery": "PA11",  "name": "OSC32_OUT", "pin": "OSC#5" }
                     ]
                 }
         ],
         "constructors": [
                 {
                     "type": "OSC32",
                     "arguments": [
                             { "type": "fixed", "value": "fixedValue" },
                             { "type": "consumes", "value": "direct" }
                     ]
                 },
                 {
                     "type": "GPIO",
                     "arguments": [
                         { "type": "fixed", "value": "eGpioPeripheral<PeripheryBlock>" },
                         { "type": "fixed", "value": "eGpioPin<PeripheryNumber>" }
                     ]
                 }
         ],
         "final": "yes"
      }
      )JSON";

      app.clear();
      board.clear();
      board.loadByteArray( QByteArray(boardString), "intern_BUTTON" );
      app.loadByteArray(
               QByteArray(R"JSON(
               {
                   "name": "canio",
                   "periphery": [
                      { "type": "OSC32", "name": "1" }
                   ]
               }
               )JSON" ), "intern" );

      pinmux.setBoardApp(board, app);
      CConstructorPool constructors;
      constructors.loadByteArray( QByteArray(boardString), "intern" );
      CWriterPlatform platform(pinmux, constructors, "");
      pinmux.resolveApp();

      CPeripheryPtr p=board.findPeripheryByPeripheryName( QString("OSC32_PC14") );
      REQUIRE( p != nullptr );
      qWarning() << "Output: " << platform.getPeripheralArgumentsString( *p );
      REQUIRE( platform.getPeripheralArgumentsString( *p )
               == "fixedValue, eGpioPeripheralA, eGpioPin12, eGpioPeripheralA, eGpioPin11");
   }

   SECTION( "configBlock, consumed indirect" )
   {
      const char *boardString=
      R"JSON(
      {
          "name": "Nucleo_f303_5",
          "version": "1",
          "peripheries": [
                  { "type": "GPIO",   "periphery": "PA7", "pin": "D1", "irq": "EXTI9_5_IRQHandler" },
                  { "type": "TIMER",   "periphery": "TIM3", "pin": "D0", "irq": "EXTI15_10_IRQHandler", "relation": "shared" },
                  {
                      "type": "PWM", "periphery": "PWM_TIM3_CH1_PA7",
                      "consumes": [
                          { "periphery": "PA7",       "name": "_TIM3_CH1" },
                          { "periphery": "TIM3", "name": "3",  "irqs": "yes", "relation": "shared" }
                      ],
                      "arguments": [ { "value": "eTimerChannel1", "position": 3 } ],
                      "attributes": {
                          "configBlock": "pwmLineConfig_PWM_TIM3_PA7"
                      }
                  }
         ],
         "constructors": [
                 {
                     "type": "PWM",
                     "arguments": [
                        { "type": "fixed", "value": "<attribute:configBlock>" },
                        { "type": "consumes", "value": "indirect" }
                     ]
                 },
                 {
                     "type": "TIMER",
                     "arguments": [
                            { "type": "fixed", "value": "<attribute:configBlock>" }
                     ]
                 },
                 {
                     "type": "GPIO",
                     "arguments": [
                         { "type": "fixed", "value": "eGpioPeripheral<PeripheryBlock>" },
                         { "type": "fixed", "value": "eGpioPin<PeripheryNumber>" }
                     ]
                 }
         ],
         "final": "yes"
      }
      )JSON";

      app.clear();
      board.clear();
      board.loadByteArray( QByteArray(boardString), "intern_BUTTON" );
      app.loadByteArray(
               QByteArray(R"JSON(
               {
                   "name": "canio",
                   "periphery": [
                      { "type": "PWM", "name": "1" }
                   ]
               }
               )JSON" ), "intern" );

      pinmux.setBoardApp(board, app);
      pinmux.resolveApp();
      CConstructorPool constructors;
      constructors.loadByteArray( QByteArray(boardString), "intern" );
      CWriterPlatform platform(pinmux, constructors, "");

      CPeripheryPtr p=board.findPeripheryByPeripheryName( QString("PWM_TIM3_CH1_PA7") );
      REQUIRE( p != nullptr );
      qWarning() << "Output: " << platform.getPeripheralArgumentsString( *p );
      REQUIRE( platform.getPeripheralArgumentsString( *p )
               == "pwmLineConfig_PWM_TIM3_PA7, m_pwm1_timer3" );
   }


   SECTION( "configBlock, consumed indirect" )
   {
      const char *boardString=
      R"JSON(
      {
          "name": "Nucleo_f303_6",
          "version": "1",
          "peripheries": [
                  { "type": "GPIO",   "periphery": "PA0", "pin": "D1", "irq": "EXTI9_5_IRQHandler" },
                  { "type": "INPUT", "periphery": "INPUT_PA0", "name": "1", "modeXX": "-",
                     "consumes": [  { "periphery": "PA0",   "name": "0", "useIrq": "yes" },
                                    { "periphery": "EXTI0",   "name": "0" } ]
                  },
                  {   "type": "EXTI", "periphery": "EXTI0", "name": "EXTI0",
                      "attributes": {
                           "pseudo": "yes"
                      }
                  },
                  {
                     "type": "WKUP", "periphery": "WKUP1", "name": "", "priority": "1",
                     "consumes": [  { "periphery": "INPUT_PA0", "name": "1" } ]
                  }
         ],
         "constructors": [
                 {
                     "type": "PWM",
                     "arguments": [
                        { "type": "fixed", "value": "<attribute:configBlock>" },
                        { "type": "consumes", "value": "indirect" }
                     ]
                 },
                 {
                     "type": "TIMER",
                     "arguments": [
                            { "type": "fixed", "value": "<attribute:configBlock>" }
                     ]
                 },
                 {
                     "type": "GPIO",
                     "arguments": [
                         { "type": "fixed", "value": "eGpioPeripheral<PeripheryBlock>" },
                         { "type": "fixed", "value": "eGpioPin<PeripheryNumber>" }
                     ]
                 }
         ],
         "final": "yes"
      }
      )JSON";

      app.clear();
      board.clear();
      board.loadByteArray( QByteArray(boardString), "intern_WKUP" );
      app.loadByteArray(
               QByteArray(R"JSON(
               {
                   "name": "canio",
                   "periphery": [
                      { "type": "WKUP", "name": "1" }
                   ]
               }
               )JSON" ), "intern" );

      pinmux.setBoardApp(board, app);
      pinmux.resolveApp();

      CPeripheryPtr p=board.findPeripheryByPeripheryName( QString("INPUT_PA0") );
      REQUIRE( p != nullptr );
      qWarning() << "Output: " << p->doInstantiate();
      REQUIRE( p->doInstantiate() == false );
   }

   SECTION( "[4] relation modes, [4.5] fabricate" )
   {
      const char *boardString=
      R"JSON(
      {
          "name": "Nucleo_f303_7",
          "version": "1",
          "peripheries": [
                 {   "type": "GND", "name": "GND", "pin": "GND", "relation": "fabricate", "attributes": { "pseudo": "yes" } }
         ],
         "constructors": [
                 {
                     "type": "GND",
                     "arguments": [
                     ]
                 }
         ],
         "final": "yes"
      }
      )JSON";

      app.clear();
      board.clear();
      board.loadByteArray( QByteArray(boardString), "intern_WKUP" );
      app.loadByteArray(
               QByteArray(R"JSON(
               {
                   "name": "canio",
                   "periphery": [
                      { "type": "GND", "name": "1" },
                      { "type": "GND", "name": "2" },
                      { "type": "GND", "name": "3" }
                   ]
               }
               )JSON" ), "intern" );

      pinmux.setBoardApp(board, app);
      pinmux.resolveApp();

      REQUIRE( true == true );
   }

}

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