#pragma once

/************************************************************************
    MeOS - Orienteering Software
    Copyright (C) 2009-2017 Melin Software HB

    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 3 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, see <http://www.gnu.org/licenses/>.

    Melin Software HB - software@melin.nu - www.melin.nu
    Eksoppsvägen 16, SE-75646 UPPSALA, Sweden

************************************************************************/

#include <map>
#include <string>
#include <vector>

using namespace std;

class Parser;


class ParseNode {
public:
  virtual bool isVariable() const {return false;}
  virtual int evaluate(const Parser &parser) const = 0;
  virtual void assign(const Parser &parser, int value) const;
  virtual void assignVector(const Parser &parser, const vector<int> &value) const;
 
  virtual ~ParseNode() = 0;
};


class Parser {
  enum Operator {
    OpNone,
    OpPlus, // 1
    OpMinus, // 1
    OpTimes, // 2
    OpDivide, // 2
    OpMod, // 2
    OpMax,
    OpMin,

    OpEquals,
    OpNotEquals,
    OpLess,
    OpMore,
    OpLessEquals,
    OpMoreEquals,

    OpAnd,
    OpOr,

    OpNot,

    OpAssign,

    OpLeftP,
    OpRightP,

    OpInc,
    OpDec,
    OpIncPost,
    OpDecPost,
    OpIncPre,
    OpDecPre,

    OpReturn,
    OpSize,
    OpSizeBase,
    OpSizeSub,
    OpBreak,
    OpSortArray,
  };


  static const int levelMax = 3;
  static int getLevel(Operator op);
  static void eatWhite(const string &expr, size_t &pos);

  ParseNode *parseFunction(const string &name, const string &expr, size_t &pos);


  static Operator parseOperator(const string &expr, size_t &pos, int level);
  static Operator parseOperatorAux(const string &expr, size_t &pos);

  struct Symbol {
    string desc;
    bool isVector;
    bool isMatrix;
    vector< vector<int> > value;
  };

  map<string, Symbol> symb;
  mutable map<string, vector<int> > var;

  mutable int breakMode;
  mutable bool returnMode;
  mutable bool ignoreValue;

  ParseNode *parseStatement(const string &expr, bool primary);
  ParseNode *parseStatement(ParseNode *left, const string &expr, size_t &pos, int level, bool changeSign);

  class StatementNode : public ParseNode {
    ParseNode *node;
    StatementNode *next;

    StatementNode(const StatementNode &); //Do not use
    StatementNode &operator=(const StatementNode &); //Do not use

  public:
    int evaluate(const Parser &parser) const;

    StatementNode();
    virtual ~StatementNode();
    friend class Parser;
  };

  class ValueNode : public ParseNode {
    string value;
    ValueNode(const ValueNode &); //Do not use
    ValueNode &operator=(const ValueNode &); //Do not use

  public:
    int evaluate(const Parser &parser) const;
    bool isVariable() const;
    void assign(const Parser &parser, int value) const;
    void assignVector(const Parser &parser, const vector<int> &value) const;
 
    ValueNode();
    virtual ~ValueNode();
    friend class Parser;
  };

  class ArrayValueNode : public ParseNode {
    string expr;
    ParseNode *index;
    ParseNode *index2;    
    ArrayValueNode(const ArrayValueNode &); //Do not use
    ArrayValueNode &operator=(const ArrayValueNode &); //Do not use

  public:
    int evaluate(const Parser &parser) const;
    void assign(const Parser &parser, int value) const;
    void assignVector(const Parser &parser, const vector<int> &value) const;
 
    bool isVariable() const;
    ArrayValueNode();
    virtual ~ArrayValueNode();
    friend class Parser;
  };



  class UnaryOperatorNode : public ParseNode {
    Operator op;
    ParseNode *right;
    UnaryOperatorNode(const UnaryOperatorNode &); //Do not use
    UnaryOperatorNode &operator=(const UnaryOperatorNode &); //Do not use

  public:
    int evaluate(const Parser &parser) const;

    UnaryOperatorNode();
    virtual ~UnaryOperatorNode();
    friend class Parser;
  };

  class BinaryOperatorNode : public ParseNode {
    Operator op;
    ParseNode *left;
    ParseNode *right;
    BinaryOperatorNode(const BinaryOperatorNode &); //Do not use
    BinaryOperatorNode &operator=(const BinaryOperatorNode &); //Do not use

  public:
    int evaluate(const Parser &parser) const;

    BinaryOperatorNode();
    virtual ~BinaryOperatorNode();
    friend class Parser;
  };

  class IfNode : public ParseNode {
    ParseNode *condition;
    ParseNode *iftrue;
    ParseNode *iffalse;
    IfNode(const IfNode &); //Do not use
    IfNode &operator=(const IfNode &); //Do not use

  public:
    int evaluate(const Parser &parser) const;

    IfNode();
    virtual ~IfNode();
    friend class Parser;
  };

  class WhileNode : public ParseNode {
    ParseNode *condition;
    ParseNode *body;
    WhileNode(const WhileNode &); //Do not use
    WhileNode &operator=(const WhileNode &); //Do not use

  public:
    int evaluate(const Parser &parser) const;

    WhileNode();
    virtual ~WhileNode();
    friend class Parser;
  };


  class ForNode : public ParseNode {
    ParseNode *start;
    ParseNode *condition;
    ParseNode *update;

    ParseNode *body;
    ForNode(const ForNode &); //Do not use
    ForNode &operator=(const ForNode &); //Do not use

  public:
    int evaluate(const Parser &parser) const;

    ForNode();
    virtual ~ForNode();
    friend class Parser;
  };


  int evaluate(const string &input) const;
  int evaluate(const string &input, int index, int index2) const;
  int evaluateSize(const string &input, int index) const;
  void sortArray(const string &input) const;

  
  void storeVariable(const string &input, const vector<int> &value) const;
  void storeVariable(const string &input, int value) const;
  void storeVariable(const string &input, int index, int value) const;

  UnaryOperatorNode *getUnary();
  BinaryOperatorNode *getBinary();
  ValueNode *getValue();
  ArrayValueNode *getArrayValue();
  StatementNode *getStatement();
  IfNode *getif ();
  WhileNode *getWhile();
  ForNode *getFor();

  ParseNode *parseif (const string &expr, size_t &pos);
  ParseNode *parseReturn(const string &expr, size_t &pos);
  ParseNode *parseValue(const string &word, const string &expr, size_t &pos);
  ParseNode *parseWhile(const string &expr, size_t &pos);
  ParseNode *parseFor(const string &expr, size_t &pos);

  string parseMethod(const string &expr, size_t &pos);

  vector<ParseNode *> nodes;
  bool isMatrix(const string &symbol) const;
  bool isVector(const string &symbol) const;

  const vector<int> &getVector(const string &symbol, int index) const;
public:
  ParseNode *parse(const string &expr);
  static void test();

  Parser();
  ~Parser();
  void clear();
  void addSymbol(const char *name, const string &value);
  void addSymbol(const char *name, int value);
  void addSymbol(const char *name, const vector<string> &value);
  void addSymbol(const char *name, const vector<int> &value);
  void addSymbol(const char *name, vector< vector<int> > &value);
  void removeSymbol(const char *name);
  void declareSymbol(const char *name, const string &desc, bool isVector, bool isMatrix = false);
  void clearSymbols();
  void clearVariables() const;

  void takeVariable(const char*name, vector<int> &val) const;

  void getSymbols(vector< pair<wstring, size_t> > &symb) const;

  void getSymbolInfo(int ix, wstring &name, wstring &desc) const;

  void dumpVariables(gdioutput &gdi, int c1, int c2) const;
  void dumpSymbols(gdioutput &gdi, int c1, int c2) const;
};
