
/*************************************************************************************
 *  Copyright (C) 2007 by Aleix Pol <aleixpol@kde.org>                               *
 *                                                                                   *
 *  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "algebrahighlighter.h"
#include <QGuiApplication>
#include <QStack>
#include <QPalette>

#include <analitza/expression.h>
#include <analitza/analyzer.h>
#include <analitza/variables.h>
#include <analitza/explexer.h>
#include <analitza/expressionparser.h>

AlgebraHighlighter::AlgebraHighlighter(QTextDocument *doc, const Analitza::Analyzer *na)
    : QSyntaxHighlighter(doc), m_correct(true), m_mode(Autodetect), m_pos(0), m_editingParameter(-1), m_editingBounds(-1), a(na)
{
    bold.setFontWeight(QFont::Bold);
}

QString removeTags(const QString& in)
{
    bool tag=false;
    QString out;
    for(int i=0; i<in.length(); i++){
        if(!tag && in[i]=='<')
            tag=true;
        else if(tag && in[i]=='>')
            tag=false;
        else if(!tag) {
            if(in.mid(i,4)==QLatin1String("&gt;")){
                out += '>';
                i+=3;
            } else
                out += in[i];
        }
    }
    return out;
}

struct FuncItem
{
    FuncItem() : parameter(-1), pos(-1), bounding(false) {}
    FuncItem(const QString& name, int p) : id(name), parameter(p), pos(-1), bounding(false) {}
    QString id;
    int parameter;
    int pos;
    bool bounding;
};

void AlgebraHighlighter::highlightBlock(const QString &text)
{
    m_correct=true;
    if(int(m_pos)>=text.length())
        m_pos=text.length();
    
    QPalette pal=qApp->palette();
    QColor number(pal.color(QPalette::Active, QPalette::Link));
    QColor variable(pal.color(QPalette::Active, QPalette::LinkVisited));
    QColor comment(pal.color(QPalette::Active, QPalette::Background));
    QColor id(150,0,50);
    QColor string(0xbb,0,0);
    QColor uncorrect(Qt::red);
    m_editingParameter=0;
    m_editingName.clear();
    
    if(Analitza::Expression::isMathML(text)) {
        QString lasttag;
        int inside=0;
        for(int i=0; i<text.length(); i++) {
            if(text[i]=='<') { //We enter in a tag
                lasttag.clear();
                int j=i+1, k=0;
                for(k=i+1; k<text.length() && text[k]!='>'; ++k){
                    lasttag.append(text[k]);
                    if(text[k]!=' ' && j==k-1)
                        j=k;
                }
                j++;
                
                setFormat(i, 1, bold);
                setFormat(j, 1, bold);
                if(lasttag.startsWith(QChar('/'))){
                    setFormat(i+1, j-i-1, QColor(100,0,0));
                    setFormat(i+1, 1, bold);
                    inside--;
                } else if(lasttag.endsWith(QChar('/'))) {
                    setFormat(i+1, j-i-1, id);
                    setFormat(j+1, 2, bold);
                } else if(j!=k) {
                    setFormat(i+1, j-i-1, QColor(150,0,0));
                    setFormat(j+1, k-j-1, QColor(150,100,0));
                    inside++;
                } else {
                    setFormat(i+1, j-i-1, QColor(150,0,0));
                    inside++;
                }
                i=k;
            }
            else if(lasttag==QLatin1String("cn"))
                setFormat(i, 1, number);
            else if(lasttag==QLatin1String("ci"))
                setFormat(i, 1, variable);
        }
        
        m_correct=(inside==0);
    } else {
        ExpLexer lex(text);
        QStack<ExpLexer::TOKEN> paren;
        QStack<FuncItem> parameter;
        
        while(lex.lex()!=ExpressionTable::EOF_SYMBOL && lex.error().isEmpty()) {
            QColor f;
            bool isBold=false;
            switch(lex.current.type){
                case ExpressionTable::tVal:
                    if(lex.current.val.mid(1,2)==QLatin1String("cn")) //if it is a number
                        f=number;
                    else { //if it is a variable
                        if(a && a->variables()->contains(removeTags(lex.current.val)))
                            f=number;
                        else
                            f=variable;
                    }
                    break;
                case ExpressionTable::tId:
                    f=id;
                    m_aName=lex.current.val;
                    break;
                case ExpressionTable::tString:
                    f=string;
                    break;
                case ExpressionTable::tComment:
                    f=comment;
                    break;
                case -1:
                    m_correct = false;
                    f=uncorrect;
                    break;
                case ExpressionTable::tLpr:
                case ExpressionTable::tLcb:
                    paren.push(lex.current);
                    if(m_pos>lex.current.pos)
                        parameter.push(FuncItem(m_aName, 0));
                    isBold=true;
                    setFormat(lex.current.pos, lex.current.len, bold);
                    break;
                case ExpressionTable::tRpr:
                case ExpressionTable::tRcb: {
                    isBold=true;
                    setFormat(lex.current.pos, lex.current.len, bold);
                    
                    bool corr=!paren.isEmpty();
                    if(corr) {
                        if(lex.current.type==ExpressionTable::tRpr && paren.top().type!=ExpressionTable::tLpr)
                            corr=false;
                        
                        if(m_pos==lex.current.pos || m_pos==paren.top().pos) {
                            QTextCharFormat bg=bold;
                            bg.setBackground(Qt::yellow);
                            
                            setFormat(lex.current.pos, lex.current.len, bg);
                            setFormat(paren.top().pos, paren.top().len, bg);
                        }
                        
                        paren.pop();
                        if(m_pos>lex.current.pos)
                            parameter.pop();
                    }
                    
                    if(!corr)
                        setFormat(lex.current.pos, lex.current.len, uncorrect); // error
                }    break;
                default:
                    if(m_pos>lex.current.pos && !parameter.isEmpty()
                            && lex.current.type==ExpressionTable::tComa) {
                        parameter.top().parameter++;
                        parameter.top().pos=lex.current.pos;
                    }
                    
                    
                    
                    if(m_pos>lex.current.pos && !parameter.isEmpty()
                            && lex.current.type==ExpressionTable::tColon) {
                        parameter.top().bounding=true;
                        parameter.top().pos=lex.current.pos;
                    }
                    
                    isBold=true;
                    setFormat(lex.current.pos, lex.current.len, bold);
                    break;
            }
            if(!isBold)
                setFormat(lex.current.pos, lex.current.len, f);
        }
        
        if(!lex.error().isEmpty())
        {
            setFormat(lex.current.pos, text.size()-lex.current.pos, uncorrect);
        }
        
        if(!parameter.isEmpty()) {
            m_editingName=parameter.top().id;
            m_editingParameter=parameter.top().parameter;
            m_editingBounds=parameter.top().bounding;
            
            if(m_editingParameter!=0 || m_editingBounds) {
                QTextCharFormat currentComa=bold;
                currentComa.setBackground(Qt::yellow);
                setFormat(parameter.top().pos, 1, currentComa);
            }
        }
    }
}

QString AlgebraHighlighter::editingName() const
{
    return m_editingName;
}

int AlgebraHighlighter::editingParameter() const
{
    return m_editingParameter;
}

bool AlgebraHighlighter::editingBounds() const
{
    return m_editingBounds;
}
