//----------------------------------------------------------------------------
///
/// \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 "writerPlatform.h"
#include "formaterCpp.h"
#include "board.h"
#include "app.h"

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

//const char *CWriterPlatform::IMPLEMENTATION_FILE = "platform_generated.cpp";

CWriterPlatform::CWriterPlatform(CPinmux &pinmux, CConstructorPool &constructorPool, const QString basePath) /* explicit */
   :CWriter(pinmux, basePath)
   ,m_constructorPool(constructorPool)
{
   initFiles();
   return;
}

void CWriterPlatform::initFiles()
{
   if( !getBasePath().isEmpty() )
   {
      QFile header( getHeaderFileName() );
      QFile implementation( getImplementationFileName() );
      header.remove();
      implementation.remove();
   }

   return;
}

QString CWriterPlatform::getHeaderFileName()
{
   QString h( getBasePath() + "/platform_generated"
             + m_filePostfix.toLower() + ".hpp" );
   return(h);
}

QString CWriterPlatform::getImplementationFileName()
{
   QString i( getBasePath() + "/platform_generated"
             + m_filePostfix.toLower() + ".cpp" );
   return(i);
}

QString CWriterPlatform::getClassName( const CPeriphery &periphery ) const
{
   QString className;
   const CConstructor *constructor=m_constructorPool.getConstructor( CPeripheryConstPtr ( &periphery) );

   if( constructor && ( ! constructor->getClass().isEmpty() ) )
   {
      className=constructor->getClass();
   }
   else
   {
      QString _type=periphery.getPeripheryType().toLower();
      _type[0]=_type[0].toUpper();
      className=QString("C%1").arg(_type);
      int pos;
      while( ( pos = className.indexOf('_') ) !=-1)
      {
         className.remove(pos,1);
         className[pos]=className[pos].toUpper();
      }
   }

   return (className);
}

QString CWriterPlatform::getShortInstanceName( const CPeriphery &periphery ) const
{
   QString name;
   QString type { periphery.getPeripheryType().toLower() };
   CReferencePtr pReference=periphery.getReferenceLink();

   if( pReference && (!pReference->getName().isEmpty()))
   {
      name=pReference->getName();
   }
   else
   {
      name=periphery.getName();
   }
   type[0]=type[0].toUpper();

   return { QString("%1%2").arg(type).arg(name) };
}


QString CWriterPlatform::getInstanceName( const CPeriphery &periphery ) const
{
   QString name;
   QString start;
   QString type;
   int index;
   CReferencePtr pReference=periphery.getReferenceLink();

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

   type=periphery.getPeripheryType().toLower();
   while( ( index=type.indexOf('_') ) >= 0 )
   {
      type.remove(index, 1);
      type[index]=type[index].toUpper();
   }

   CPeripheryConstPtr parent;
   if(periphery.getReferenceLink())
   {
      parent=periphery.getReferenceLink()->getParent();
   }

   if( parent && pReference && ( ! pReference->doPruneName() ) )
   {
      start=getInstanceName( *parent );
      start+='_';
   }
   else
   {
      start="m_";
      if( pReference && ( pReference->getRelation()==ERelation::placeholder ) )
      {
         start+="p";
         type[0]=type[0].toUpper();
      }
   }

   if( name.length() )
   {
      name[0]=name[0].toUpper();
   }

   return( QString("%1%2%3").arg(start).arg(type).arg(name) );
}


QList<QString> CWriterPlatform::getPeripheralArguments(const CPeriphery &periphery) const
{
   QList<QString> argumentList;
#if 0
   const CConstructor *constructor=getConstructor(periphery);
   if(constructor==nullptr)
   {
      //qCritical("Constructors: %d", constructorList.count());
      qFatal("Could not find constructor for periphery of type '%s', name '%s', id %d"
             , qPrintable(periphery.getPeripheryType()), qPrintable(periphery.getName()) , periphery.getId() );
   }
#endif
   argumentList+=getConstructingArguments(periphery, CPeriphery::EBinding::direct );

   for(auto &argument:argumentList)
   {
      fillAttributes(periphery, argument);
   }

   return(argumentList);
}


const QString CWriterPlatform::getConsumingArguments( const CPeriphery &periphery, bool indirect ) const
{
   bool isFirst=true;
   QString arguments;
   for( auto consume: periphery.getConsumesListConst() )
   {
      QString argument;

      if(consume.getPeripheryLink()->getAttribute( "pseudo" ) == "yes" )
      {
         continue;
      }

      const CConstructor *constructor=m_constructorPool.getConstructor(consume.getPeripheryLink());
      if( !constructor )
      {
         qCritical("There is no constructor for name:%s/periphery-name:%s/type:%s"
                   , qPrintable( consume.getName() )
                   , qPrintable( consume.getPeripheryName() )
                   , qPrintable( consume.getPeripheryType() )
                   );
         qFatal( "Break." );
      }
      CPeriphery::EBinding binding=consume.getPeripheryLink()->getBinding();
            //consume.getRelation() == ERelation::shared ?
            //   CReference::EBinding::byReference : CReference::EBinding::direct;
      if( indirect != ( binding == CPeriphery::EBinding::byReference ) )
      {
         continue;
      }
      //argument = constructor->getConstructingArguments( *consume.getPeripheryLink(), binding );
      argument = getConstructingArguments( *consume.getPeripheryLink(), binding );
      if( !argument.isEmpty() )
      {
         if ( !isFirst )
         {
            arguments+=", ";
         }
         isFirst=false;
         arguments += argument;
      }
   }
   return(arguments);
}


//const QString getConsumingArguments( const CPeriphery &periphery, bool indirect ) const;

/** \brief Get the sonstructors arguments
 *
 *         Returns the string of arguments thats needed to be passed to the
 *         constructor.
 *         e.g.:
 *
 **/
const QString CWriterPlatform::getConstructingArguments(const CPeriphery &periphery, CPeriphery::EBinding binding ) const
{
   //QString peripheryBlock=getPeripheryBlock(periphery);
   //QString peripheryNumber=getPeripheryNumber(periphery);
   //CFormaterCpp formater(periphery, *this);
   QString arguments;
   bool isFirst=true;

   if( binding == CPeriphery::EBinding::byReference )
   {
      arguments=getInstanceName( periphery );
      if(arguments.isEmpty())
      {
         qFatal("That should not happen");
      }
   }
   else
   {
      const CConstructor *constructor=m_constructorPool.getConstructor(CPeripheryConstPtr(&periphery) );
      if(!constructor)
      {
         qFatal("Could not find constructor for periphery of type '%s', name '%s', id %d"
             , qPrintable(periphery.getPeripheryType()), qPrintable(periphery.getName()) , periphery.getId() );
      }

      // The "arguments" array in the JSON constructor description is an array
      // of entities; 'fixed', 'consumes'
      for(const QJsonValue &argument: constructor->getArguments())
      {
         QString type=argument.toObject()["type"].toString();
         QString value=argument.toObject()["value"].toString();

         arguments+=isFirst?"":", ";
         isFirst=false;

         if ( type == "fixed" )
         {
            arguments+=value;
         }
         else if ( type == "consumes" )
         {
              arguments+=getConsumingArguments( periphery, value == "indirect" );
         }
         else
         {
              qFatal( "Unknown constructor argument type: %s", qPrintable(type));
         }
      }

      QString peripheryName;
      if( ! periphery.getReferenceLink()->getName().isEmpty() )
      {
         peripheryName = periphery.getReferenceLink()->getName();
      }
      else
      {
         peripheryName = periphery.getName();
      }
      
      periphery.replaceLabels( arguments );
   }

   return(arguments);
}


QString CWriterPlatform::getPeripheralArgumentsString(const CPeriphery &periphery) const
{
   QList<QString> argumentList( getPeripheralArguments(periphery) );
   QString macro;

   for(auto argument: argumentList)
   {
      if(!argument.isEmpty())
      {
         if(macro.length())
            macro+=QString(", \\\n%1").arg(' ', MAKRO_IDENT);
         macro+=argument;
      }
   }

   if(macro.isEmpty())
      macro+="Yes";

   return( macro );
}

void CWriterPlatform::createSortedTypesList()
{
   struct PPeripheryInfo
   {
      int count=0;
   };
   QHash <QString, PPeripheryInfo> typeCounter;

   // The construction of objects
   for(const QSharedPointer <CPeriphery> &periphery: board().listConst())
   {
      if( periphery->doInstantiate() && (periphery->getAttribute( "pseudo" ) != "yes" ) )
      {
         QString type=periphery->getPeripheryType();
         typeCounter[type].count++;
      }
   }

   typeList=typeCounter.keys();
}

int CWriterPlatform::writeFiles( ) /* virtual override */
{
   return(0);
}

int CWriterPlatform::writeHeader()
{
   QHash <QString, int> typeCounter;
   QHash <QString, int> includeHeaderCounter;
   QString line;
   QFile file( getHeaderFileName() );
   if (!file.open(QFile::WriteOnly | QFile::Truncate))
      qFatal("Could not open header file for writing; %s", qPrintable( getHeaderFileName() ) );
   QTextStream out(&file);

   for(const auto &periphery: board().listConst())
   {
      if( periphery->doInstantiate() && (periphery->getAttribute( "pseudo" ) != "yes" ) )
      {
         const CConstructor *constructor=getConstructor(&*periphery);
         QString includeHeader;
         QString frameWork;
         if(constructor)
         {
            includeHeader=constructor->getIncludeHeader();
            frameWork=constructor->getFrameWork();
         }
         else
         {
            frameWork="biwak";
         }

         if( includeHeader.isNull() )
         {
            QString type=periphery->getPeripheryType();
            typeCounter[frameWork + "/" + type]++;
         }

         if( ! includeHeader.isNull() ) // pReference->getInclude().isNull() )
         {
            includeHeaderCounter[includeHeader]++;
         }
      }
   }
   
   out << QString("#ifndef ARENA_PLATFORM_GENERATED_%1%2_HPP\n")
              .arg(app().getName().toUpper()).arg(m_filePostfix.toUpper());
   out << QString("#define ARENA_PLATFORM_GENERATED_%1%2_HPP\n")
              .arg(app().getName().toUpper()).arg(m_filePostfix.toUpper());
   writeCHeader( out );
   out << "\n\n";

   for( auto type: typeCounter.keys() )
   {
      out << QString("#include <%1.hpp>\n").arg(type.toLower());
   }
   for( auto includeHeader: includeHeaderCounter.keys() )
   {
      out << QString("#include %1\n").arg(includeHeader);
   }

   out << QString("#include <arena/platform_base.hpp>\n");

   out <<   "\n\n";

   for ( const QSharedPointer<CPeriphery> &periphery: board().listConst())
   {
      if(periphery->isUsed())
      {
         //CReference *pReference=periphery->getReferenceLink();
         if(periphery->getAttribute( "pseudo" ) == "yes" )
            continue;

         QString implementation=getDefines(*periphery);
         fillAttributes(*periphery, implementation);
         out << implementation;
      }
   }

   out <<   "\n\n";
   out << "extern \"C\"\n";
   out << "{\n";
   out << "   void arenaInitFunctions" << m_postfix << "();\n";
   out << "}\n";
   out << "\n\n";
   
   out <<   "class CPlatform" << m_postfix << "Pre\n"
            "{\n"
            "   public:\n"
            "      CPlatform" << m_postfix << "Pre()\n"
            "      {\n"
            "          arenaInitFunctions" << m_postfix << "();\n"
            "      }\n"
            "};\n\n";
   

   out <<   "class CPlatform" << m_postfix << ": public CPlatformBase"
                                            ", public CPlatform" << m_postfix << "Pre"
                                            "\n"
            "{\n"
            "   public:\n";

   for ( const QSharedPointer <CPeriphery> &periphery: board().listConst())
   {
      CReferencePtr pReference=periphery->getReferenceLink();

      if( periphery->getAttribute( "pseudo" ) == "yes" )
         continue;

      if( periphery->doInstantiate() )
      {
         out << "      " << formatPeripheryDefinition( *periphery, pReference.data(), periphery->getPeripheryType()) <<
                "\n";
      }
   }

   out << "\n";
   out << "      CPlatform" << m_postfix << "();\n";

   for ( const QSharedPointer<CPeriphery> &periphery: board().listConst())
   {
      if(periphery->isUsed())
      {
         if( periphery->instanciateByPointer() )
         {
            //CFormaterCpp formater( *periphery, pinmux().getConstructorPool() );
            //out << "      void instantiate" << formater.getShortInstanceName()
            //    <<         "();\n";

            out << "      void instantiate" << getShortInstanceName(*periphery )
                <<         "();\n";
         }
      }
   }


   out << "\n";
   out << "};\n";
   out << "\n\n";
   out << "//---fin-----------------------------------------------------------------------\n";
   out << QString("#endif // ? ! ARENA_PLATFORM_GENERATED_%1%2_HPP\n")
              .arg(app().getName().toUpper() ).arg(m_filePostfix.toUpper());
   file.close();

   return(0);
}

bool checkOrder();

int CWriterPlatform::writeImplementation( )
{
   QMap <QString, QString> arenaInitFunctions;

   QString fileName=getImplementationFileName();
   QString line;
   QFile file(fileName);
   if (!file.open(QFile::WriteOnly | QFile::Truncate))
      qFatal("Could not open header file for writing");
   QTextStream out(&file);

   qWarning("Create Implementation: ");

   writeCHeader( out );
   out << "\n\n";
   
   out << QString("#include \"platform_generated" )
                  << m_filePostfix << ".hpp\"\n";

#if 1
   for( auto type: typeList )
   {
      //out << QString("#include <biwak/%1.hpp>\n").arg(type.toLower());
      //if(m_pBoard->getProperties().contains("MCU_PLATFORM"))
      if(board().getProperties().contains("platformHeaders"))
      {
         QStringList platformHeaders=board().getProperties()["platformHeaders"].toString().split(",");
         if( platformHeaders.contains(type) )
         {
            out << QString("#include <biwak/%1/%2.hpp>\n")
                .arg(board().getProperties()["MCU_PLATFORM"].toString().toLower())
                .arg(type.toLower());
         }
      }
   }

   QStringList platformHeaders=board().getProperties()["platformIncludes"].toString().split(",");
   for(const QString &include: platformHeaders)
   {
      if( !include.isEmpty() )
      {
         out << QString("#include %1\n").arg(include);
      }
   }

#endif

   out <<   "\n\n";
   for ( const QSharedPointer<CPeriphery> &periphery: board().listConst())
   {
      if(periphery->isUsed())
      {
         //CReference *pReference=periphery->getReferenceLink();
         if(periphery->getAttribute( "pseudo" ) == "yes" )
            continue;

         if( periphery->doInstantiateImplementation() )
         {
            QString implementation=getImplementation(*periphery);
            fillAttributes(*periphery, implementation);
            out << implementation;
         }
      }
   }

   out <<   "\n\n";
   out <<   "CPlatform" << m_postfix << "::CPlatform" << m_postfix << "()\n";

   bool isFirst=true;

   for ( const QSharedPointer<CPeriphery> &periphery: board().listConst())
   {
      if(periphery->isUsed())
      {
         CReferencePtr pReference=periphery->getReferenceLink();

         if(periphery->getAttribute( "pseudo" ) == "yes" )
            continue;

         if( periphery->instanciateByPointer() )
         {
            continue;
         }

         if( periphery->doInstantiate() /*&& ( periphery.getType()==type ) */ )
         {
            int counter=0;
            out << "      " << ( isFirst ? ":" : "," )
                << formatPeripheryConstructor(*periphery, pReference.data(), /*type*/ periphery->getPeripheryType(), counter) << "\n";
            isFirst=false;
            counter++;
         }

         //const CConstructor *constructor=
               //m_constructorPool.getConstructor( CPeripheryConstPtr( periphery ) );
         const CConstructor *constructor=m_constructorPool.getConstructor( CPeripheryConstPtr ( &(*periphery) ) );
         /*
         if(!constructor)
         if(periphery->getAttributes().contains("initFunction"))
         {
            m_arenaInitFunctions[ periphery->getAttributes()["initFunction"] ] = "y";
         }
         */
         fillMap( arenaInitFunctions, constructor->getInitFunctions()
                 , *periphery, *constructor);
         fillMap( arenaInitFunctions, periphery->getInitFunctions()
                 , *periphery, *constructor);
      }
   }
   out << "\n";

   out <<   "{\n";
   out <<   "   \n";
   out <<   "}\n";
   out <<   "\n";

   // Instanziate-methods for placeholders
   for ( const QSharedPointer<CPeriphery> &periphery: board().listConst())
   {
      if(periphery->isUsed())
      {
         if( periphery->instanciateByPointer() )
         {
            const CConstructor *constructor=m_constructorPool.getConstructor( CPeripheryConstPtr ( &(*periphery) ) );
            QStringList initFunctions=constructor->getInitFunctions();
            initFunctions+=periphery->getInitFunctions();
            QMap <QString, QString> map;
            fillMap( map, initFunctions, *periphery, *constructor);

            out << "\n\n";
            out << "void CPlatform" << m_postfix << "::instantiate" << getShortInstanceName( *periphery )
                <<         "()\n"
                << "{\n";
            
            printPreInitFunctions(out);
            out << "\n";
            
            for( QString key : map.keys() )
            {
               out << "   " << key << "\n";
            }
            
            printPostInitFunctions(out);
            out << "\n";

            out << "   " << getInstanceName( *periphery ) << " = new "
                         << getClassName( *periphery ) << "("
                         << getPeripheralArgumentsString( *periphery )
                         << ");\n"
                << "}\n";
         }
      }
   }

   out << "\n\n";
   out << "void arenaInitFunctions" << m_postfix << "()\n";
   out << "{\n";
   
   printPreInitFunctions(out);

   for( QString key : arenaInitFunctions.keys() )
   {
      out << "   " << key << "\n";
   }
   
   printPostInitFunctions(out);
   out << "}\n";

   out << "\n\n";
   out << "//---fin-----------------------------------------------------------------------\n";
   file.close();

   return(0);
}


void CWriterPlatform::printPreInitFunctions(QTextStream& out) const
{
   QJsonArray arr=board().getProperties()["preInitFunctions"].toJsonArray();
   for( QJsonValueRef pre : arr )
   {
      out << "   " << pre.toString() << "\n";
   }
}


void CWriterPlatform::printPostInitFunctions(QTextStream& out) const
{
   QJsonArray arr=board().getProperties()["postInitFunctions"].toJsonArray();
   for( QJsonValueRef post : arr )
   {
      out << "   " << post.toString() << "\n";
   }
}


QString CWriterPlatform::macroLine(const QString &name, const QString &value)
{
   QString line=QString("#define %1%2").arg(name).arg(" ", -40);
   line.truncate(MAKRO_IDENT);
   line+=value;
   line+="\n";
   return(line);
}


QString CWriterPlatform::formatPeripheryDefinition(const CPeriphery &periphery
                                  , const CReference *pReference
                                  , const QString &type)
{
   QString line;

   line+=QString("%1 %2%3;")
                  .arg( getClassName( periphery ) )
                  .arg( periphery.instanciateByPointer()?"*":"" )
                  .arg( getInstanceName( periphery ) );
   return(line);
}

QString CWriterPlatform::formatPeripheryConstructor(const CPeriphery &periphery
                                  , const CReference *pReference
                                  , const QString &type, __attribute__ ((unused)) int counter)
{
   QString line;
   QString pname;
   QString arguments;

   if(! periphery.instanciateByPointer() )
   {
      arguments=getPeripheralArgumentsString( periphery );
         if(arguments=="Yes")
            arguments.clear();
   }
   else
   {
      arguments="nullptr";
   }
   line+=QString("%1(%2) /* order:%3, ID:%4 */")
         .arg( getInstanceName(periphery) ).arg(arguments)
         .arg(periphery.getOrder()).arg(periphery.getId());

   line = replaceSubLabels( line, periphery, nullptr );

   return(line);
}

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

   macro+=QString("%1").arg(pin);

   for(const auto &consumes: periphery.getConsumesListConst())
   {
      if(!macro.isEmpty())
         macro+=QString(", \\\n%1").arg(' ', MAKRO_IDENT);

      macro+=QString("%1").arg(getWiring(consumes));
   }

   if(macro.isEmpty())
      macro+="None";
   return( macro );
}

QString CWriterPlatform::getImplementation(const CPeriphery &periphery) const
{
   //QString implementation=periphery.getAttributes()["implementation"];

   const CConstructor *constructor=m_constructorPool.getConstructor( CPeripheryConstPtr ( &periphery) );
   if(!constructor)
   {
      qFatal("No constructor for '%s', '%s/%s'"
             , qPrintable( periphery.getName() )
             , qPrintable( periphery.getPeripheryName() )
             , qPrintable( periphery.getPeripheryType() )
             );
   }
   //QString implementation=constructor->getImplementation();
   QString implementation=constructor->getAttribute("implementation");
   if(! implementation.isEmpty() )
   {
      implementation+="\n";
   }
   //for( autopinmux().getBoard()->getOptions();

   implementation = replaceSubLabels( implementation, periphery, constructor);
   return(implementation);
}


QString CWriterPlatform::getDefines(const CPeriphery &periphery) const
{
   const CConstructor *constructor=m_constructorPool.getConstructor( CPeripheryConstPtr ( &periphery) );
   if(!constructor)
   {
      qFatal("No constructor for '%s', '%s/%s'"
             , qPrintable( periphery.getName() )
             , qPrintable( periphery.getPeripheryName() )
             , qPrintable( periphery.getPeripheryType() )
             );
   }
   //QString implementation=constructor->getImplementation();
   QString implementation=constructor->getAttribute("defines");
   if(! implementation.isEmpty() )
   {
      //implementation+="\n";
   }

   implementation = replaceSubLabels( implementation, periphery, constructor);
   return(implementation);
}


const QString &CWriterPlatform::fillAttributes(const CPeriphery &periphery, QString &arguments) const
{
   int startIndex, stopIndex;
   const char *startString="<attribute:";

   while( ( startIndex=arguments.indexOf( startString ) ) >= 0)
   {
      int consumed=-1;
      stopIndex=arguments.indexOf(">", startIndex );
      if(stopIndex<0)
      {
         qFatal("Wrong attribute syntax");
      }
      QString tag=arguments.mid(startIndex, stopIndex - startIndex + 1);
      QString part=arguments.mid(startIndex+1, stopIndex - startIndex - 1);
      QStringList list=part.split(":");
      QString attribute;
      QString value;

      if( list.count() <= 1 )
      {
         qFatal("Syntax error");
      }
      else if( list.count() == 2 )
      {
         attribute=list[1];
      }
      if( list.count() > 2 )
      {
         consumed=list[1].toInt();
         attribute=list[2];
      };

      // If overloading is enabled for given attribute, try to get it from reference

      if( consumed >= 0 )
      {
         if( periphery.getConsumesListConst().size() <= consumed )
         {
            periphery.dump();
            qFatal( "Periphery does not have consumed-reference '%d'", consumed );
         }
         CPeripheryPtr peripheryPtr=periphery.getConsumesListConst()[consumed].getPeripheryLink();
         value=peripheryPtr->getAttribute( attribute );
         if( value.isEmpty() )
         {
            peripheryPtr->dump();
            qFatal( "Periphery does not provide attribute '%s'", qPrintable(attribute) );
         }
      }

      if( value.isEmpty() && board().isOverloadingAttribute(attribute) )
      {
         if ( ! periphery.getReferenceLink()->getAttribute(attribute).isEmpty() )
         {
           value=periphery.getReferenceLink()->getAttribute( attribute );
         }
      }

      if ( value.isEmpty() && ( ! periphery.getAttribute(attribute).isEmpty() ) )
      {
         value=periphery.getAttribute( attribute );
      }

      if ( value.isEmpty() )
      {
         periphery.dump();
         qWarning("Arguments: %s", qPrintable(arguments));
         qFatal("%s: error: Nothing provides attribute '%s', periphery '%s'",
                qPrintable( pinmux().getBoard()->getMainFile() ),
                qPrintable(attribute),
                qPrintable(periphery.getPeripheryName()));
      }

      value=value.trimmed();
      value.replace("\n", ", ");
      arguments.replace(tag, value);
   }

   QString peripheryName;
   if( ! periphery.getReferenceLink()->getName().isEmpty() )
   {
      peripheryName = periphery.getReferenceLink()->getName();
   }
   else
   {
      peripheryName = periphery.getName();
   }
   
   periphery.replaceLabels(arguments);

   return(arguments);
}

const CReference* CWriterPlatform::getSubPeriphery(const CPeriphery &startPeriphery, const QString indexText) const
{
   QStringList idList=indexText.split(".");
   const CReference *r = nullptr;
   const CPeriphery *p = &startPeriphery;

   for(const QString &id:idList)
   {
      int index=id.toInt();
      if( index >= p->getConsumesListConst().count() )
      {
         //qCritical() << "Constructor: " << text;
         qCritical() << "Periphery: " << p->getName()
                     << "/" << p->getPeripheryName();
         qFatal("Framework: Referenced index too high; consumed periphery does not exist.");
      }
      r = &p->getConsumesListConst()[ index ];
      p = r->getPeripheryLink().data();
   }
   return(r);
}

QString CWriterPlatform::replaceSubLabels(QString text, const CPeriphery &periphery
                                     , const CConstructor *constructor) const
{
   int index;
   QStringList repList{"option", "alternateFunction", "pin", "periphery",
                       "name", "peripheryName", "referenceName"};
   for(const auto &rep:repList)
   {
      QString pre=QString("<%1:").arg(rep);
      while( (index=text.indexOf( pre )) >= 0 )
      {
         int start=index+pre.length();
         int end=text.indexOf(">", start);
         QString functionName=text.mid( start, end-start );
         const CReference *r = getSubPeriphery(periphery, functionName);
         const CPeriphery *p = r->getPeripheryLink().data();

         QString target;
         if(rep=="alternateFunction")
         {
            target=p->getConstAlternateFunctions()[r->getAlternateFunction()];
            if( target.isEmpty() )
            {
               target=r->getParent()->getAttribute( "alternateFunction" );
            }
            if( target.isEmpty() )
            {
               qCritical("%s: error: Alternate function empty for %s; neither "
                         "in the periphery GPIO nor in the referencing attributes. "
                         "Refernece: %s"
                           , qPrintable( pinmux().getBoard()->getMainFile() )
                           , qPrintable( p->getPeripheryName() )
                           , qPrintable( r->getName() ) );
               qCritical("   Alternate function: %s", qPrintable( r->getAlternateFunction() ) );
               qCritical("   Reference: %s", qPrintable( r->getName() ) );
               qFatal("   Reference parent: %s", qPrintable( r->getParent()->getPeripheryName() ) );
            }
         }
         else
         if(rep=="pin")
         {
            target=p->getPeripheryNumber();
         }
         else
         if(rep=="periphery")
         {
            target=p->getPeripheryBlock();
         }
         else
         if(rep=="option")
         {
            if ( pinmux().getBoard()->getOptions().contains(functionName) )
            {
		if(constructor)
		{
            	    target=constructor->getAttribute( QString("implementation_") + functionName );
		}
		else
		{
            	    target=QString("NOT_IMPLEMENTED_BY_PINMUXER");
		}
            }
            else
            {
               target.clear();
            }
         }
         else
         if(rep=="peripheryName")
         {
            target=p->getPeripheryName();
         }
         else
         if(rep=="referenceName")
         {
            target=r->getName();
         }
         else
         if(rep=="name")
         {
            target=getInstanceName(*p);
         }
         else
         {
            qFatal("unknown replace string");
         }
         text.replace(QString("<%1:%2>").arg(rep).arg(functionName),
                                target
                                );
      }
   }
   return(text);
}

void CWriterPlatform::fillMap(QMap<QString, QString> &map, const QStringList &initFunctions,
         const CPeriphery &periphery, const CConstructor &constructor)
{
   for(QString initFunction:initFunctions)
   {
      initFunction = fillAttributes(periphery, initFunction );
      initFunction = replaceSubLabels(initFunction, periphery, &constructor);
      
      map[ initFunction ] = "y";
   }
   
}


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