//-----------------------------------------------------------------------------
///
/// \brief  Ansi escape code sequence to html converter
///
///         Used to show terminal output in QTextEdit for example.
///         Text can per parsed incremental, no complete lines as input are
///         required.
///
///         Only ansi color sequence is supported yet.
///
/// \date   20150326
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//-----------------------------------------------------------------------------


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


#include <lepto/ansiparser.h>
#include <lepto/log.h>


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


const char*ansiColorStrings[10]
{
   [0]="#000000",  // Black
   [1]="#AA0000",  // Red
   [2]="#00AA00",  // Green
   [3]="#AA5500",  // Yellow
   [4]="#0000AA",  // Blue
   [5]="#AA00FF",  // Magenta
   [6]="#00AAAA",  // Cyan
   [7]="#AAAAAA",  // White
   [8]="#000000",
   [9]="#000000",
};


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


/// \brief  le constructuer
///
///         Nothing to do here
CAnsiParser::CAnsiParser()
   //:input(_input)
   :spanPending(false)
   ,parPending(false)

{
   error=false;
   m_addBreak=false;
   eParserState=eParserStateNone;
   // Actually we are at the end. But there is nothing yet so no
   m_atEndOfLine=false;
   pos=0;
}


/// \brief  clears output strng
///
///         has to called whenever ouput was used
void CAnsiParser::clearOutput()
{
   output.clear();
}


/// \brief  Returns true if last char parsed was newline
///
///         this means a html block is complete and the output can be used.
bool CAnsiParser::atEndOfLine()
{
   return(m_atEndOfLine);
}

#if 1
/// \brief  parse ansi escape sequence
///
///         output is cleared before parsing.
void CAnsiParser::parse(const QString &_input)
{
   input=&_input;

   for(pos=0; ( pos<input->length() ) && (!error) ; pos++)
   {
      //int value=(int)((*input)[pos].unicode());
      int value=(int)((*input)[pos].unicode());
      //printf("Value: 0x%X\n", value);
      m_atEndOfLine=false;

      switch(eParserState)
      {
         case eParserStateNone:
         {
            switch(value)
            {
               case 27: // Escape
                  eParserState=eParserStateEscaped;
                  break;
               case '\n':
               {
                  closePending();
                  if(m_addBreak)
                     output+="<br />";
                  m_atEndOfLine=true;
                  lineEndReached();
                  break;
               }
               case '\r':
                  break;
               default:
                  output+=(*input)[pos];
                  break;
            }

            break;
         }

         case eParserStateEscaped:
         {
            switch( value )
            {
               case '[':
                  eParserState=eParserStateCsi;
                  break;
               case '#':
                  eParserState=eParserStateTelegram;
                  break;
               default:
                  output+=QChar( 27 );
                  output+=QChar( value );
                  //lHint << "Unknown escape command '" << value << "'";
                  error=true;
                  break;
            }
            break;
         }

         case eParserStateCsi:
            parseCsi();
            break;
         case eParserStateTelegram:
            parseTelegram();
            break;

         default:
            throw("Unknown error State");
            break;
      }

   }

   if(error)
   {
      eParserState=eParserStateNone;
      resetParam();
      error=0;
   };

   return;
}
#endif

/// \brief   Close a pending "<span>"-html-block
void CAnsiParser::closePending()
{
   if(spanPending)
   {
      output+="</span>";
      spanPending=false;
   }

   return;
}


bool CAnsiParser::isPending()
{
   return( (eParserState!=eParserStateNone) );
}

#if 1
/// \brief  Parse an CSI-Sequence
void CAnsiParser::parseCsi()
{
   if( ! ((*input)[pos]).isLetter() )
   {
         param+=(*input)[pos];
         //printf("Param: %d\n", (*input)[pos].unicode());
   }
   else
   {
      switch(((*input)[pos]).unicode())
      {
         case 'm':
            parseColor();
            resetParam();
            eParserState=eParserStateNone;
            break;
         default:
            lInfo( "Unknown ansi sequence: %d", ((*input)[pos]).unicode() );
            error=true;
            break;
      }
   }

   return;
}


/// \brief  Parse an base64-coded telegram
void CAnsiParser::parseTelegram()
{
   if( (*input)[pos] == ';' )
   {
      // emit
      otherCommand( ((*input)[pos]).unicode(), param);
      resetParam();
      eParserState=eParserStateNone;
   }
   else
   {
      param+=(*input)[pos];
   }

   return;
}


/// \brief  Parse an CSI-Sequence
void CAnsiParser::parseBiwak()
{
   if( !((*input)[pos]).isLetter() )
   {
         param+=(*input)[pos];
   }
   else
   {
      switch( ((*input)[pos]).unicode() )
      {
         case 'c':
            //serial2can.parseCan( getOutput() );
            // emit
            otherCommand( ((*input)[pos]).unicode(), param);
            resetParam();
            eParserState=eParserStateNone;
            break;
         default:
            lInfo( "Unknown biwak sequence: %d", ((*input)[pos]).unicode() );
            error=true;
            break;
      }
   }

   return;
}


/// \brief  Parse an CSI-Sequence
void CAnsiParser::resetParam()
{
   param.clear();
}


/// \brief  Parse color sequence
void CAnsiParser::parseColor()
{
   closePending();

   output+="<span style=\" color:";
   int digit=param[1].digitValue();
   if(digit!=-1)
      output+=ansiColorStrings[digit];

   //printf("Color: %d\n", digit);

   output+=";\">";

   param.clear();
   spanPending=true;
}
#endif

/// \brief  Get reference to output string
const QString &CAnsiParser::getOutput()
{
   return(output);
}


#ifdef TCB


ansiParserOtherCommandFunc_t ansiParserOtherCommandFunc=nullptr;
ansiParserlineEndReachedFunc_t ansiParserlineEndReachedFunc=nullptr;


void CAnsiParser::lineEndReached()
{
   if(ansiParserlineEndReachedFunc)
      ansiParserlineEndReachedFunc();

   return;
}


int CAnsiParser::otherCommand(char command, QString &param)
{
   if(ansiParserOtherCommandFunc)
      return ansiParserOtherCommandFunc(command, param);

   return(-1);
}


void CAnsiParser::setLineEndReachedFunc(ansiParserlineEndReachedFunc_t _ansiParserlineEndReachedFunc)
{
   ansiParserlineEndReachedFunc=_ansiParserlineEndReachedFunc;

   return;
}


void CAnsiParser::setOtherCommandFunc(ansiParserOtherCommandFunc_t _ansiParserOtherCommandFunc)
{
   ansiParserOtherCommandFunc=_ansiParserOtherCommandFunc;

   return;
}

#endif


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