/***************************************************************************
 *   Copyright (C) 2005 by Thierry CHARLES   *
 *   thierry@les-charles.net   *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "ini_enlighter.h"
#include "components/codeeditor/tcodeeditor.h"
#include "lib/lib_logging.h"

/**
 * définis tout au style par défaut
 * ne pas appeler directement
 * @param iLine ligne a colorer
 * @param iPreviousLineEndState état dans lequel se trouvait l'analyseur lors de la sortie de cette fonction pour la ligne précédente. La première ligne (0) est appellée avec 0.
 * @param info informations de mise en valeur a compléter.
 * @return état de l'analyseur à utiliser pour la ligne suivante.
 */
int IniEnlighter::enlightLine(int iLine, int iPreviousLineEndState, TLineEnlightenmentInformation & info)
{
    uint i = 0;
    uint iWordStart = 0;
    uint iWordEnd = 0;
    uint iState = iPreviousLineEndState;
    wxChar c = 0;
    wxString sWord;
    const wxString & sText = this->getEditor()->getDocument()->getLine(iLine);


#define STATE_DEFAULT               0
#define STATE_WORD                  1
#define STATE_NUMBER_INT            2
#define STATE_NUMBER_FLOAT          3
#define STATE_COMMENTARY_TOEOL      4
#define STATE_TEXTSTRING            5
#define STATE_TEXTSTRING_BACKED     6
#define STATE_CHAR                  7
#define STATE_CHAR_BACKED           8

    info.clearStyleInformations();

    while (i < sText.length())
    {
        c = sText[i];
        switch (iState)
        {
            case STATE_DEFAULT:
                if (isalpha(c) || (c == '_'))
                {
                    iState = STATE_WORD;
                    sWord = c;
                }
                else if (isdigit(c))
                    iState = STATE_NUMBER_INT;
                else if (c == ';')
                    iState = STATE_COMMENTARY_TOEOL;
                else if (c == '\"')
                    iState = STATE_TEXTSTRING;
                else if (c == '\'')
                    iState = STATE_CHAR;

                if(iState != STATE_DEFAULT)
                    iWordStart = i;

                break;

            case STATE_WORD:
                if ((c == '/') || (c == '\"') || (c == '\'') || (!(isalnum(c) || (c == '_'))))
                {
                    iWordEnd = i;

                    if (this->isKeyword(sWord))
                    {
                        iWordStart--;
                        if (sText[iWordStart] != '#')
                            iWordStart++;

                        info.setStyle(STYLE_KEYWORD, iWordStart, iWordEnd);
                    }
                    else
                    {
                        info.setStyle(STYLE_IDENT, iWordStart, iWordEnd);
                    }

                    if (c == ';')
                        iState = STATE_COMMENTARY_TOEOL;
                    else if (c == '\"')
                        iState = STATE_TEXTSTRING;
                    else if (c == '\'')
                        iState = STATE_CHAR;
                    else
                        iState = STATE_DEFAULT;
                    iWordStart = i;
                }
                sWord += c;
                break;

            case STATE_NUMBER_INT:
                if (c == '.')
                {
                    iState = STATE_NUMBER_FLOAT;
                }
                else if ((c == '/') || (c == '\"') || (c == '\'') || (!isdigit(c)))
                {
                    iWordEnd = i;
                    info.setStyle(STYLE_NUMBER, iWordStart, iWordEnd);

                    if (c == ';')
                        iState = STATE_COMMENTARY_TOEOL;
                    else if (c == '\"')
                        iState = STATE_TEXTSTRING;
                    else if (c == '\'')
                        iState = STATE_CHAR;
                    else
                        iState = STATE_DEFAULT;
                    iWordStart = i;
                }
                break;

            case STATE_NUMBER_FLOAT:
                if ((c == '/') || (c == '\"') || (c == '\'') || (!isdigit(c)))
                {
                    iWordEnd = i;
                    info.setStyle(STYLE_NUMBER, iWordStart, iWordEnd);

                    if (c == ';')
                        iState = STATE_COMMENTARY_TOEOL;
                    else if (c == '\"')
                        iState = STATE_TEXTSTRING;
                    else if (c == '\'')
                        iState = STATE_CHAR;
                    else
                        iState = STATE_DEFAULT;
                    iWordStart = i;
                }
                break;

            case STATE_COMMENTARY_TOEOL:
                i = sText.length();
                info.setStyle(STYLE_COMMENT, iWordStart);
                iState = STATE_DEFAULT;
                break;

            case STATE_TEXTSTRING:
            // optimisation du passage du texte
                while ((i < sText.length()) && (sText[i] != '\"') && (sText[i] != '\\'))
                    i++;
                if (i == sText.length())
                {
                    iWordEnd = i+1;
                    info.setStyle(STYLE_STRING, iWordStart, iWordEnd);
                }
                else if (sText[i] == '\"')
                {
                    iWordEnd = i+1;
                    info.setStyle(STYLE_STRING, iWordStart, iWordEnd);
                    iState = STATE_DEFAULT;
                }
                else if (sText[i] == '\\')
                    iState = STATE_TEXTSTRING_BACKED;
                break;

            case STATE_TEXTSTRING_BACKED:
                iState = STATE_TEXTSTRING;
                break;

            case STATE_CHAR:
            // optimisation du passage du texte
                while ((i < sText.length()) && (sText[i] != '\'') && (sText[i] != '\\'))
                    i++;
                if (i == sText.length())
                {
                    iWordEnd = i+1;
                    info.setStyle(STYLE_CHAR, iWordStart, iWordEnd);
                }
                else if (sText[i] == '\'')
                {
                    iWordEnd = i+1;
                    info.setStyle(STYLE_CHAR, iWordStart, iWordEnd);
                    iState = STATE_DEFAULT;
                }
                else if (sText[i] == '\\')
                    iState = STATE_CHAR_BACKED;
                break;

            case STATE_CHAR_BACKED:
                iState = STATE_CHAR;
                break;

            default:
                iState = STATE_DEFAULT;
                i--;
                break;
        }
        i++;
    }

    //fermeture des colorations
    switch (iState)
    {
        case STATE_WORD:
            if (this->isKeyword(sWord))
            {
                iWordStart--;
                if (sText[iWordStart] != '#')
                    iWordStart++;

                info.setStyle(STYLE_KEYWORD, iWordStart);
            }
            else
                info.setStyle(STYLE_IDENT, iWordStart);
            iState = STATE_DEFAULT;
            break;
        case STATE_NUMBER_INT:
        case STATE_NUMBER_FLOAT:
            info.setStyle(STYLE_NUMBER, iWordStart);
            iState = STATE_DEFAULT;
            break;
        case STATE_COMMENTARY_TOEOL:
            info.setStyle(STYLE_COMMENT, iWordStart);
            iState = STATE_DEFAULT;
            break;
        case STATE_TEXTSTRING:
        case STATE_TEXTSTRING_BACKED:
            info.setStyle(STYLE_STRING, iWordStart);
            break;
        case STATE_CHAR:
        case STATE_CHAR_BACKED:
            info.setStyle(STYLE_CHAR, iWordStart);
            break;
    }


    return iState;


#undef STATE_DEFAULT
#undef STATE_WORD
#undef STATE_NUMBER_INT
#undef STATE_NUMBER_FLOAT
#undef STATE_COMMENTARY_TOEOL
#undef STATE_TEXTSTRING
#undef STATE_TEXTSTRING_BACKED
#undef STATE_CHAR
#undef STATE_CHAR_BACKED
}

bool IniEnlighter::isKeyword(const wxString & sWord) const
{
    if(this->keywords == NULL) return false;

    TStringList::const_iterator it = this->keywords->find(sWord);

    return (it != this->keywords->end());
}


