/***************************************************************************
 *   copyright       : (C) 2009-2022 by Pascal Brachet                     *
 *   https://www.xm1math.net/algobox/                                       *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   @license GPL-2.0+ <https://spdx.org/licenses/GPL-2.0+.html>           *
 ***************************************************************************/


#include <QtGui>
#include <QRegularExpression>

#include "algohighlighter.h"
#include "blockdata.h"

AlgoHighlighter::AlgoHighlighter(QTextDocument *parent)
    : QSyntaxHighlighter(parent)
{
ColorStandard = QColor("#000000");
ColorComment = QColor("#606060");
ColorBloc=QColor("#800000");
ColorCommande=QColor("#0000CC");
ColorSi=QColor("#800080");
ColorTantQue=QColor("#DD6F06");
ColorPour=QColor("#BB8800");
ColorFonction=QColor("#9A4D00");
FonctionWords=QString("\\bFONCTION\\b,\\bFONCTIONS_UTILISEES\\b,\\bDEBUT_FONCTION\\b,\\bFIN_FONCTION\\b,\\bVARIABLES_FONCTION\\b").split(",");
BlocWords= QString("\\bVARIABLES\\b,\\bDEBUT_ALGORITHME\\b,\\bFIN_ALGORITHME\\b").split(",");
CommandeWords= QString("\\bEST_DU_TYPE\\b,\\bPAUSE\\b,\\bLIRE\\b,\\bAFFICHERCALCUL\\b,\\bAFFICHER\\b,\\bPREND_LA_VALEUR\\b,\\bTRACER_POINT\\b,\\bTRACER_POINT_Rouge\\b,\\bTRACER_POINT_Vert\\b,\\bTRACER_POINT_Bleu\\b,\\bTRACER_POINT_Blanc\\b,\\bTRACER_SEGMENT\\b,\\bTRACER_SEGMENT_Rouge\\b,\\bTRACER_SEGMENT_Vert\\b,\\bTRACER_SEGMENT_Bleu\\b,\\bTRACER_SEGMENT_Blanc\\b,\\bRENVOYER\\b,\\bAPPELER_FONCTION\\b").split(",");
SiWords=QString("\\bSI\\b,\\bALORS\\b,\\bSINON\\b,\\bDEBUT_SI\\b,\\bFIN_SI\\b,\\bDEBUT_SINON\\b,\\bFIN_SINON\\b").split(",");
TantQueWords=QString("\\bTANT_QUE\\b,\\bDEBUT_TANT_QUE\\b,\\bFIN_TANT_QUE\\b,\\bFAIRE\\b").split(",");
PourWords=QString("\\bPOUR\\b,\\bALLANT_DE\\b,\\bDEBUT_POUR\\b,\\bFIN_POUR\\b").split(",");
}

AlgoHighlighter::~AlgoHighlighter(){
}

void AlgoHighlighter::highlightBlock(const QString &text)
{
int i = 0;
int state = previousBlockState();
if (state<0) state=0;
QChar last, next ,ch,tmp;
QString buffer;
const int StateStandard = 0;
const int StateComment = 1;
const int StateString = 2;

BlockData *blockData = new BlockData;
int leftPos = text.indexOf( '(' );
while ( leftPos != -1 ) 
  {
  ParenthesisInfo *info = new ParenthesisInfo;
  info->character = '(';
  info->position = leftPos;

  blockData->insertPar( info );
  leftPos = text.indexOf( '(', leftPos+1 );
  }

int rightPos = text.indexOf(')');
while ( rightPos != -1 ) 
  {
  ParenthesisInfo *info = new ParenthesisInfo;
  info->character = ')';
  info->position = rightPos;

  blockData->insertPar( info );
  rightPos = text.indexOf( ')', rightPos+1 );
  }
setCurrentBlockUserData(blockData);
blockData->code.clear(); 

for (int j=0; j < text.length(); j++) blockData->code.append(0);
while (i < text.length())
    {
    ch = text.at( i );
    buffer += ch;
    if ( i < text.length()-1 ) next = text.at( i+1 );

    switch (state) 
    {
	case StateStandard: 
	{
	tmp=text.at( i );
	if (tmp=='/') 
	    {
		if (next=='/')
			{
			setFormat( i, 1,ColorComment );
			blockData->code[i]=1;
			state=StateComment;
			i++;
			if ( i < text.length())
				{
				setFormat( i, 1,ColorComment);
				blockData->code[i]=1;
				}
			}
		else
		{
		setFormat( i, 1,ColorStandard );
		blockData->code[i]=0;
		state=StateStandard;
		}
	    } 
	else if (tmp=='"') 
	    {
	    blockData->code[i]=1;
	    state=StateString;
	    } 
	else if (tmp== '(' )
	    {
	    blockData->code[i]=1;
	    setFormat( i, 1,ColorStandard);
	    state=StateStandard;
	    } 
	else if (tmp== ')' )
	    {
	    blockData->code[i]=1;
	    setFormat( i, 1,ColorStandard);
	    state=StateStandard;
	    } 
	else if (isWordSeparator(tmp))
	    {
	    blockData->code[i]=1;
	    setFormat( i, 1,ColorStandard);
	    } 
	else
	    {
	    setFormat( i, 1,ColorStandard);
	    state=StateStandard;
	    }
	buffer = QString();
	} break;
	case StateString: 
	{
	tmp=text.at( i );
	if (tmp== '"') 
	    {
	    blockData->code[i]=1;
	    state=StateStandard;
	    }
	else
	    {
	    blockData->code[i]=1;
	    state=StateString;
	    }
	buffer = QString();
	} break;
	case StateComment: {
	setFormat( i, 1,ColorComment);
	blockData->code[i]=1;
	state=StateComment;
	buffer = QString();
	} break;
    }
    last = ch;
    i++;
    }
setCurrentBlockState(StateStandard) ;

QTextCharFormat fonctionFormat;
fonctionFormat.setFontWeight(QFont::Bold);
fonctionFormat.setForeground(ColorFonction);
QTextCharFormat blocFormat;
blocFormat.setFontWeight(QFont::Bold);
blocFormat.setForeground(ColorBloc);
QTextCharFormat siFormat;
siFormat.setFontWeight(QFont::Bold);
siFormat.setForeground(ColorSi);
QTextCharFormat tantqueFormat;
tantqueFormat.setFontWeight(QFont::Bold);
tantqueFormat.setForeground(ColorTantQue);
QTextCharFormat pourFormat;
pourFormat.setFontWeight(QFont::Bold);
pourFormat.setForeground(ColorPour);
if (text.isEmpty()) return;
i=0;
while (i < text.length())
	{
	buffer = QString();
	ch = text.at( i );
	while ((blockData->code[i]!=1) && (!isSpace(ch)))
	      {
	      buffer += ch;
	      i++;
	      if (i < text.length()) ch = text.at( i );
	      else break;
	      }
	if ( buffer.length() > 0 )
		{
		for ( QStringList::Iterator it = FonctionWords.begin(); it != FonctionWords.end(); ++it ) 
			{
			QRegularExpression expression(( *it ),QRegularExpression::CaseInsensitiveOption /* | QRegularExpression::InvertedGreedinessOption */);
			int index = expression.match(buffer).capturedStart();
			while (index >= 0) 
			  {
			  int length = expression.match(buffer).capturedLength();
			  setFormat(i-length, length,fonctionFormat);
			  index = expression.match(buffer,index+length).capturedStart();
			  }
			}
		for ( QStringList::Iterator it = BlocWords.begin(); it != BlocWords.end(); ++it ) 
			{
			QRegularExpression expression(( *it ),QRegularExpression::CaseInsensitiveOption /* | QRegularExpression::InvertedGreedinessOption */);
			int index = expression.match(buffer).capturedStart();
			while (index >= 0) 
			  {
			  int length = expression.match(buffer).capturedLength();
			  setFormat(i-length, length,blocFormat);
			  index = expression.match(buffer,index+length).capturedStart();
			  }
			}
		for ( QStringList::Iterator it = CommandeWords.begin(); it != CommandeWords.end(); ++it ) 
			{
			QRegularExpression expression(( *it ),QRegularExpression::CaseInsensitiveOption /* | QRegularExpression::InvertedGreedinessOption */);
			int index = expression.match(buffer).capturedStart();
			while (index >= 0) 
			  {
			  int length = expression.match(buffer).capturedLength();
			  setFormat(i-length, length,ColorCommande);
			  index = expression.match(buffer,index+length).capturedStart();
			  }
			}
		for ( QStringList::Iterator it = SiWords.begin(); it != SiWords.end(); ++it ) 
			{
			QRegularExpression expression(( *it ),QRegularExpression::CaseInsensitiveOption /* | QRegularExpression::InvertedGreedinessOption */);
			int index = expression.match(buffer).capturedStart();
			while (index >= 0) 
			  {
			  int length = expression.match(buffer).capturedLength();
			  setFormat(i-length, length,siFormat);
			  index = expression.match(buffer,index+length).capturedStart();
			  }
			}
		for ( QStringList::Iterator it = TantQueWords.begin(); it != TantQueWords.end(); ++it ) 
			{
			QRegularExpression expression(( *it ),QRegularExpression::CaseInsensitiveOption /* | QRegularExpression::InvertedGreedinessOption */);
			int index = expression.match(buffer).capturedStart();
			while (index >= 0) 
			  {
			  int length = expression.match(buffer).capturedLength();
			  setFormat(i-length, length,tantqueFormat);
			  index = expression.match(buffer,index+length).capturedStart();
			  }
			}
		for ( QStringList::Iterator it = PourWords.begin(); it != PourWords.end(); ++it ) 
			{
			QRegularExpression expression(( *it ),QRegularExpression::CaseInsensitiveOption /* | QRegularExpression::InvertedGreedinessOption */);
			int index = expression.match(buffer).capturedStart();
			while (index >= 0) 
			  {
			  int length = expression.match(buffer).capturedLength();
			  setFormat(i-length, length,pourFormat);
			  index = expression.match(buffer,index+length).capturedStart();
			  }
			}
		}
	i++;
	}
}

bool AlgoHighlighter::isWordSeparator(QChar c) const
{
    switch (c.toLatin1()) {
    case '.':
    case ',':
    case '?':
    case '!':
    case ':':
    case ';':
    case '-':
    case '<':
    case '>':
    case '[':
    case ']':
    case '(':
    case ')':
    case '{':
    case '}':
    case '=':
    case '/':
    case '+':
    case '%':
    case '&':
    case '^':
    case '*':
    case '\'':
    case '"':
    case '~':
        return true;
    default:
        return false;
    }
}

bool AlgoHighlighter::isSpace(QChar c) const
{
    return c == QLatin1Char(' ')
        || c == QChar::Nbsp
        || c == QChar::LineSeparator
        || c == QLatin1Char('\t')
        ;
}

