/***************************** LICENSE START ***********************************

 Copyright 2017 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <string>
using std::string;


#include "LanguageHelper.h" 
#include "PythonHelper.h"
#include "Highlighter.h"



// -----------------------------------------------------------------------
// ------------------- Syntax highlighter code ---------------------------
//        this class is used by the PythonHelper object, defined below
// -----------------------------------------------------------------------


class PythonHighlighter : public Highlighter
{
public:
     PythonHighlighter(QTextDocument *parent = 0);
    ~PythonHighlighter(){}

     virtual void highlightBlock(const QString &text);

protected:
     QTextCharFormat keywordFormat;
     QTextCharFormat classFormat;
     QTextCharFormat singleLineCommentFormat;
     QTextCharFormat quotationFormat;
     QTextCharFormat functionFormat;
     QTextCharFormat numberFormat;
     QTextCharFormat builtinFunctionFormat;
     QTextCharFormat deprecatedFunctionFormat;
};


PythonHighlighter::PythonHighlighter(QTextDocument *parent)
     : Highlighter(parent)
{
    HighlightingRule rule;

    // Note that the order in which these highlighting rules are given
    // DOES matter! The rules seem to be applied in the order in which
    // they are given.

    //Functions - user defined
    functionFormat.setFontWeight(QFont::Bold);
    functionFormat.setForeground(QColor(236,47,33));
    rule.pattern = QRegExp("\\b[A-Za-z0-9_]+(?=\\()");
    rule.format = functionFormat;
    highlightingRules.append(rule);


    //Numerics
    //numberFormat.setForeground(QColor(193,63,65));
    numberFormat.setForeground(QColor(0,0,255));
    rule.pattern = QRegExp("\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f)?\\b");
    rule.format = numberFormat;
    highlightingRules.append(rule);



    //Built in functions - these are stored in a text file, so we read it in and parse it
    builtinFunctionFormat.setFontWeight(QFont::Bold);
    builtinFunctionFormat.setForeground(QColor(255,119,119));


    // Deprecated functions - these are stored in a text file, so we read it in and parse it
    // - the font will be a bit darker than that for 'normal' functions, and also underlined
    deprecatedFunctionFormat = builtinFunctionFormat;
    deprecatedFunctionFormat.setForeground(builtinFunctionFormat.foreground().color().darker(115));
    deprecatedFunctionFormat.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline	);
    deprecatedFunctionFormat.setToolTip(tr("Deprecated function"));  // the tool top seems not to work...


      //Keywords
    keywordFormat.setForeground(Qt::darkBlue);
    keywordFormat.setFontWeight(QFont::Bold);

    // the list of keywords was generated from Python as follows:
    // import keyword
    // print(keyword.kwlist)
    // import builtins
    // print(dir(builtins))


    QStringList keywords;

    keywords << "and" << "as" << "assert" << "break" << "class" << "continue" << "def" << "del" << "elif" << "else"
             << "except" << "exec" << "finally" << "for" << "from" << "global" << "if" << "import" << "in" << "is"
             << "lambda" << "not" << "or" << "pass" << "print" << "raise" << "return" << "try" << "while" << "with"
             << "yield"
             << "ArithmeticError" << "AssertionError" << "AttributeError" << "BaseException" << "BufferError"
             << "BytesWarning" << "DeprecationWarning" << "EOFError" << "Ellipsis" << "EnvironmentError"
             << "Exception" << "False" << "FloatingPointError" << "FutureWarning" << "GeneratorExit" << "IOError"
             << "ImportError" << "ImportWarning" << "IndentationError" << "IndexError" << "KeyError"
             << "KeyboardInterrupt" << "LookupError" << "MemoryError" << "NameError" << "None" << "NotImplemented"
             << "NotImplementedError" << "OSError" << "OverflowError" << "PendingDeprecationWarning"
             << "ReferenceError" << "RuntimeError" << "RuntimeWarning" << "StandardError" << "StopIteration"
             << "SyntaxError" << "SyntaxWarning" << "SystemError" << "SystemExit" << "TabError" << "True"
             << "TypeError" << "UnboundLocalError" << "UnicodeDecodeError" << "UnicodeEncodeError"
             << "UnicodeError" << "UnicodeTranslateError" << "UnicodeWarning" << "UserWarning"
             << "ValueError" << "Warning" << "ZeroDivisionError" << "__builtins__" << "__doc__"
             << "__file__" << "__future_module__" << "__name__" << "__package__" << "__path__" << "abs"
             << "absolute_import" << "all" << "any" << "apply" << "ascii" << "basestring" << "bin" << "bool"
             << "buffer" << "bytearray" << "bytes" << "callable" << "chr" << "classmethod" << "cmp" << "coerce"
             << "compile" << "complex" << "copyright" << "credits" << "delattr" << "dict" << "dir" << "divmod"
             << "enumerate" << "eval" << "execfile" << "exit" << "file" << "filter" << "float" << "format"
             << "frozenset" << "getattr" << "globals" << "hasattr" << "hash" << "help" << "hex" << "id"
             << "input" << "int" << "intern" << "isinstance" << "issubclass" << "iter" << "len" << "license"
             << "list" << "locals" << "long" << "map" << "max" << "memoryview" << "min" << "next" << "object"
             << "oct" << "open" << "ord" << "pow" << "print" << "property" << "quit" << "range" << "raw_input"
             << "reduce" << "reload" << "repr" << "reversed" << "round" << "set" << "setattr" << "slice"
             << "sorted" << "staticmethod" << "str" << "sum" << "super" << "sys" << "tuple" << "type"
             << "unichr" << "unicode" << "vars" << "xrange" << "zip";

    foreach (QString word, keywords)
    {
        QString pattern = "\\b" + word + "\\b";
        rule.pattern = QRegExp(pattern);
        rule.format = keywordFormat;
        highlightingRules.append(rule);
    }


    // strings
    quotationFormat.setForeground(Qt::darkRed);
    rule.pattern = QRegExp("(\"[^\"]*\")|(\'[^\']*\')");
    rule.format = quotationFormat;
    highlightingRules.append(rule);


    //Comments
    singleLineCommentFormat.setFontItalic(true);
    singleLineCommentFormat.setForeground(Qt::darkGreen);
    rule.pattern = QRegExp("#[^\n]*");
    rule.format = singleLineCommentFormat;
    highlightingRules.append(rule);

}



void PythonHighlighter::highlightBlock(const QString &text)
{
    foreach (HighlightingRule rule, highlightingRules)
    {
        QRegExp expression(rule.pattern);
        int index = text.indexOf(expression);
        while (index >= 0)
        {
            int length = expression.matchedLength();
            setFormat(index, length, rule.format);
            index = text.indexOf(expression, index + length);
        }
    }
}




// -----------------------------------------------------------------------
// -------------------------- PythonHelper code ---------------------------
// -----------------------------------------------------------------------


// ---------------------------------------------------------------------------
// PythonHelper::PythonHelper
// constructor
// ---------------------------------------------------------------------------

PythonHelper::PythonHelper()
{
    languageName_ = "Python";
    className_    = "PYTHON";
    serviceName_  = "(x=`dirname \"%s\"`;cd \"$x\" ; python3 \"%s\")";
}


// ---------------------------------------------------------------------------
// PythonHelper::~PythonHelper
// destructor
// ---------------------------------------------------------------------------

PythonHelper::~PythonHelper()
{
}


// ---------------------------------------------------------------------------
// PythonHelper::~createHighlighter
// creates a new syntax highlighter object
// ---------------------------------------------------------------------------

Highlighter *PythonHelper::createHighlighter(QTextDocument *parent)
{
    return new PythonHighlighter(parent);
}


// ---------------------------------------------------------------------------
// PythonHelper::isHeaderLine
// decides whether the given line is a Python script header line
// ---------------------------------------------------------------------------

bool PythonHelper::isHeaderLine(const QString &text)
{
    return (text.contains("#") && 
            text.contains("python"));
}

// ---------------------------------------------------------------------------
// PythonHelper::autoHeaderText
// returns the line(s) of text to add to an empty file when opened in editor
// ---------------------------------------------------------------------------

QString PythonHelper::autoHeaderText()
{
    return "import metview as mv\n\n";
}






