//----------------------------------------------------------------------------
///
/// \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>

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

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


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


int CWriterDesc::writeFiles( )
{
   QMap <QString, int> typeCounter;
   QString line;
   QFile file( getBasePath() + "arena_description.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 << "Loops: " << pinmux().getLoops() << "\n";
   out << "Overloading attributes: " << pinmux().getBoard()->getOverloadingAttributes() << "\n";
   out << "Board id: " << pinmux().getBoard()->getId() << "\n";
   out << "Board peripheries: " << pinmux().getBoard()->list().count() << "\n";
   out << "App references: " << pinmux().getApp()->referenceList.count() << "\n";
   // Dont show malloc info by default
   #if 0
      struct mallinfo minfo=mallinfo();
      out << "pinmuxer heap usage: \n";
      out << "   Total allocated space (bytes): " << minfo.uordblks << "\n";
      out << "   Total free space (bytes): " << minfo.fordblks << "\n";
      out << "\n";
   #endif


   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 ) )
         {
            out << formatPeriphery(*periphery, pReference.data(), type, counter);
            counter++;
         }
      }
      out << "\n";
   }

   file.close();

   writeBoardList();
   writeAppList();

   return(0);
}

QString CWriterDesc::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() )
   {
      macro+=indent;
      macro+=QString("%1").arg(getWiring(consumes));
      macro+="\n";
   }

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

   return( macro );
}

QString CWriterDesc::predefined(const CPeriphery &periphery)
{
   QString pref;
   pref+=periphery.getPeripheryName();
   cutFabricateMarker( pref );

   for(const auto &consumes: periphery.getConsumesListConst() )
   {
      pref+=" ";
      if( consumes.getPeripheryLink() )
      {
         pref += predefined(*consumes.getPeripheryLink());
         cutFabricateMarker( pref );
      }
      else
      {
         pref+="NULL-Comsumer";
      }
   }

   return(pref);
}

QString CWriterDesc::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("%1 '%2' ID:%3: \n").arg(type).arg(name).arg(periphery.getId());
      line+=QString("   Pins: \n")+peripheralPins( periphery, "      " );
      line+=QString("   Predefined: \n" "      ");
      line+=QString("\"predefined\": { \"") + board().getId() + "\": \""+ predefined( periphery ) + "\" }\n";
   }

   return(line);
}


void CWriterDesc::writeBoardList( )
{
   writeBoardList( 0 );  // unused
   writeBoardList( 1 );  // used
   writeBoardList( 2 );  // all
}

void CWriterDesc::writeBoardList(int usedCondition)
{
   QMap <QString, int> typeCounter;
   QString line;
   assert ( usedCondition<=2 );
   const char *fileNameSup[]= { "_unused", "_used", "" };
   QFile file( getBasePath() + QString("arena_board%1.txt").arg(fileNameSup[usedCondition]));
   if (!file.open(QFile::WriteOnly | QFile::Truncate))
      qFatal("Could not open header file for writing");
   QTextStream out(&file);

   out << "Dumping board pinmux: \n\n";

   for(const auto &periphery: board().listConst())
   {
      if( ( usedCondition > 1 ) || (periphery->isUsed()==usedCondition) )
      {
         out << "   " << periphery->printable() << "\n";
         for(const auto &consumes: periphery->getConsumesListConst() )
         {
            out << "      " << consumes.printable() << "\n";
         }
      }
   }

   file.close();
}


void CWriterDesc::writeAppList( )
{
   QMap <QString, int> typeCounter;
   QString line;
   QFile file( getBasePath() + QString("arena_app.txt") );

   if (!file.open(QFile::WriteOnly | QFile::Truncate))
   {
      qFatal("Could not open header file for writing");
   }
   QTextStream out(&file);

   out << "Dumping app pinmux: \n\n";

   for(const auto &reference: app().getConstReferenceList())
   {
      out << reference.printable() << "\n";
   }

   file.close();
}


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