//----------------------------------------------------------------------------
///
/// \file   board.cpp
///
/// \brief  class describing the periphery of an MCU board
///
/// \date   20180129
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//----------------------------------------------------------------------------


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


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


#define  MAKRO_IDENT    35


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


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

#include <QMap>
#include <QFile>
#include <QTextStream>
#include <QSharedPointer>
#include <QDebug>

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

#include "writerWireing.h"
#include "peripheryHelper.h"
#include "board.h"
#include "app.h"
#include <malloc.h>


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

struct SWire
{
   QString mcuPin;
   QString mcuPeriphery;
   QString function;
   QString devPin;
   //QString devPeriphery;
   //QString devFunction;
};

bool operator < (SWire a, SWire b)
{
   a.devPin.remove("#");
   b.devPin.remove("#");
   return( a.devPin.toInt() < b.devPin.toInt() );
}


QList<SWire> wireList;

QString getDevPin(QString pinList, QString function)
{
   pinList.remove("[");
   pinList.remove("]");
   QStringList list=pinList.split(",");
   for(QString &part: list)
   {
      part.remove(" ");
      QStringList list2=part.split("@");
      if(list2.size() >=2)
      {
         QString devFunction=list2[0].trimmed();
         QString devPin=list2[1].trimmed();

         if(devFunction==function)
         {
            return(devPin);
         }
      }
   }
   return( QString() );
}

void getConsumeWiring(const CReference &reference)
{
   //QString _wiring;
   //QString pin;
   //QString peripheryName;

   // We want to reconstruct the sub-pins of complex periphery like SPI.
   // So when an periphery like an display consumes an SPI-Bus the pinning is
   // shown completely.
   if(!reference.getPeripheryLink())
   {
      qWarning() << QString("No periphery for reference '%1', id %2").arg(reference.getName()).arg(reference.getId());
      return;
   }

   if(!reference.getPeripheryLink()->getConsumesList().empty())
   {
      for ( const auto &consumes : reference.getPeripheryLink()->getConsumesListConst() )
      {
         //_wiring+=consumes.getWiring();

         if( consumes.getPeripheryLink()->attributeSet("pseudo") )
         {
            continue;
         }

         SWire wire;
         wire.function=consumes.getName();
         wire.mcuPeriphery=consumes.getPeripheryLink()->getPeripheryName();
         wire.mcuPin=consumes.getPeripheryLink()->getAttribute( "pin" );

         QString pin=reference.getAttribute( "pin" );

         //wire.devFunction=consumes.getName();
         wire.devPin=getDevPin(pin, wire.function);
         wireList+=wire;
      }
   }
   else
   {
      SWire wire;
      wire.mcuPeriphery=reference.getPeripheryLink()->getPeripheryName();
      wire.mcuPin=reference.getPeripheryLink()->getAttribute("pin");
      wire.function=reference.getName();
      wire.devPin=reference.getAttribute( "pin" );
      wireList+=wire;
      #if 0
      _wiring+=" -> ";
      _wiring+=reference.getName();
      if(!pin.isEmpty())
      {
         _wiring+="@";
         _wiring+=pin;
      }
      #endif
   }

   return;
}


int CWriterWireing::writeFiles( )
{
   QMap <QString, int> typeCounter;
   QString line;
   QFile file( getBasePath() + "arena_wireing.txt");
   if (!file.open(QFile::WriteOnly | QFile::Truncate))
      qFatal("Could not open header file for writing");
   QTextStream out(&file);

   for(const auto &periphery: board().listConst())
   {
      if(periphery->doInstantiate( true /*describe*/ ))
      {
         QString type=periphery->getPeripheryType();
         typeCounter[type]++;
      }
   }

   out << "Name: " << pinmux().getApp()->getName() << "\n";
   out << "Board id: " << pinmux().getBoard()->getId() << "\n";

   for( auto type: typeCounter.keys() )
   {
      int counter=0;

      for ( const QSharedPointer<CPeriphery> &periphery: board().listConst())
      {
         CReferencePtr pReference=periphery->getReferenceLink();
         if( periphery->doInstantiate( true /*describe*/ ) && ( periphery->getPeripheryType()==type ) )
         {
            wireList.clear();
            out << formatPeriphery(*periphery, pReference.data(), type, counter);
            counter++;
         }
      }
      out << "\n";
   }

   file.close();

   return(0);
}

#define  TABLE_START       "<table border=\"2\">"
#define  TABLE_END         "</table>"
#define  TABLE_ROW_START   "<tr>"
#define  TABLE_ROW_END     "</tr>"
#define  TABLE_CELL_START  "<td>"
#define  TABLE_CELL_END    "</td>"
#define  TABLE_HEADER_START  "<th>"
#define  TABLE_HEADER_END    "</th>"

QString CWriterWireing::peripheralPins(const CPeriphery &periphery, const QString indent)
{
   QString macro;
   QString  pin=periphery.getAttribute( "pin" );

   if( !pin.isEmpty() )
   {
      macro+=QString("%1%2\n").arg(indent).arg(pin);
   }

   for(const auto &consumes: periphery.getConsumesListConst() )
   {
      getConsumeWiring(consumes);
   }

   std::sort( wireList.begin(), wireList.end() );
   macro +=  TABLE_START;
   QString  pinDevice="Pin device";
   macro +=  TABLE_ROW_START;
   macro += TABLE_HEADER_START + pinDevice + TABLE_HEADER_END
         + TABLE_HEADER_START + "Function" + TABLE_HEADER_END
         + TABLE_HEADER_START + "Periphery MCU" + TABLE_HEADER_END
         + TABLE_HEADER_START + "Pin MCU" + TABLE_HEADER_END + "\n";
   macro +=  TABLE_ROW_END;

   for(const SWire &wire:wireList)
   {
      macro +=  TABLE_ROW_START;

      macro+= TABLE_CELL_START + wire.devPin + TABLE_CELL_END
            + TABLE_CELL_START + wire.function + TABLE_CELL_END
            + TABLE_CELL_START + wire.mcuPeriphery + TABLE_CELL_END
            + TABLE_CELL_START + wire.mcuPin + TABLE_CELL_END + "\n";

      macro +=  TABLE_ROW_END;
   }
   macro +=  TABLE_END;

   if(macro.isEmpty())
      macro+=indent+"None"+ "\n";

   return( macro );
}


QString CWriterWireing::formatPeriphery(const CPeriphery &periphery
                                  , const CReference *pReference
                                  , const QString &type, int counter)
{
   QString line;
   QString name, pname;

   if(pReference && (!pReference->getName().isEmpty()))
      name=pReference->getName();
   else
      name=periphery.getName();

   //QString pins=peripheralPins( periphery, "      " );
   //if(!pins.isEmpty())
   {
      line+=QString("\n");
      line+=QString("%1 '%2' ID:%3: \n").arg(type).arg(name).arg(periphery.getId());

      line+=QString("   Pins: \n")+peripheralPins( periphery, "      " );
   }

   return(line);
}


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