#ifndef dpuserAST_H
#define dpuserAST_H

#include <cstdlib>
#include <string>
#include <map>
#include <vector>
#include <iostream>

#include "dpuserType.h"
#include "dpuser.yacchelper.h"

extern int looplock;
extern int indexBase;

// some globals to control user defined functions and procedures
extern bool returnPassed;
extern dpuserType returnedValue;
extern int nparams;

class ASTNode;

extern std::map<std::string, dpuserType> dpuser_vars;
extern std::map<std::string, ASTNode *>userprocedures;
extern std::map<std::string, std::vector<std::string> >userprocedure_arguments;
extern std::map<std::string, ASTNode *>userfunctions;
extern std::map<std::string, std::vector<std::string> >userfunction_arguments;

class ASTNode {
public:
   dpuserType      Value;
 
   ASTNode() { Value = 0; }
   virtual ~ASTNode() { }
   virtual dpuserType evaluate() = 0;
     virtual ASTNode *append(ASTNode *) { return this; }
     virtual ASTNode *append(std::string option) { return this; }
};

class numberNode : public ASTNode {
public:
	numberNode(long value) { Value = value; }
  numberNode(double value) { Value = value; }
  numberNode(double r, double i) { Value.fromRI(r, i); protect(Value.cvalue); Value.variable = true; }
  numberNode(const dpComplex &value) { Value = value; protect(Value.cvalue); Value.variable = true; }
  numberNode(const std::string &value, bool isString = true) { if (isString) { Value = value; protect(Value.svalue); Value.variable = true; } else { Value.ffvalue = new dpString(value); Value.type = typeFitsFile; protect(Value.ffvalue); Value.variable = true; } }
	dpuserType evaluate() { return Value; }
};

class unaryMinusNode : public ASTNode {
public:
  unaryMinusNode(ASTNode *left) : exp(left) { }
  virtual ~unaryMinusNode() { delete exp; }
	ASTNode *exp;
	dpuserType evaluate();
};

class variableNode : public ASTNode {
public:
  std::string id;
  variableNode(std::string value);
	dpuserType evaluate();
};

class writefitsNode : public ASTNode {
public:
    std::string fname;
    ASTNode *arg;
    writefitsNode(std::string f, ASTNode *a) : fname(f), arg(a) { }
    virtual ~writefitsNode() { delete arg; }
    dpuserType evaluate();
};

class operator_node : public ASTNode {
public:
    ASTNode *left;
    ASTNode *right;
    operator_node(ASTNode *L, ASTNode *R) : left(L), right(R) { }
    virtual ~operator_node() { delete left; delete right; }
};

class plusNode : public operator_node {
public:
  plusNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
	dpuserType evaluate();
};

class minusNode : public operator_node {
public:
  minusNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
	dpuserType evaluate();
};

class multiplyNode : public operator_node {
public:
  multiplyNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
	dpuserType evaluate();
};

class divideNode : public operator_node {
public:
  divideNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
	dpuserType evaluate();
};

class powerNode : public operator_node {
public:
  powerNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
	dpuserType evaluate();
};

class matrixmulNode : public operator_node {
public:
  matrixmulNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
    dpuserType evaluate();
};

class moduloNode : public operator_node {
public:
  moduloNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
    dpuserType evaluate();
};

class incrementNode : public ASTNode {
public:
	std::string id;
	incrementNode(std::string var) : id(var) { }
	dpuserType evaluate();
};

class decrementNode : public ASTNode {
public:
	std::string id;
	decrementNode(std::string var) : id(var) { }
	dpuserType evaluate();
};

class operatorEquals_node : public ASTNode {
public:
	std::string id;
	operatorEquals_node(std::string var, ASTNode *R) : id(var), right(R) { }
	virtual ~operatorEquals_node() { delete right; }
	ASTNode *right;
};

class plusEqualsNode : public operatorEquals_node {
public:
  plusEqualsNode(std::string var, ASTNode *R) : operatorEquals_node(var, R) { }
	dpuserType evaluate();
};

class minusEqualsNode : public operatorEquals_node {
public:
  minusEqualsNode(std::string var, ASTNode *R) : operatorEquals_node(var, R) { }
	dpuserType evaluate();
};

class multiplyEqualsNode : public operatorEquals_node {
public:
  multiplyEqualsNode(std::string var, ASTNode *R) : operatorEquals_node(var, R) { }
	dpuserType evaluate();
};

class divideEqualsNode : public operatorEquals_node {
public:
  divideEqualsNode(std::string var, ASTNode *R) : operatorEquals_node(var, R) { }
	dpuserType evaluate();
};

class greaterThanNode : public operator_node {
public:
  greaterThanNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
	dpuserType evaluate();
};

class lessThanNode : public operator_node {
public:
  lessThanNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
	dpuserType evaluate();
};

class greaterEqualNode : public operator_node {
public:
  greaterEqualNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
	dpuserType evaluate();
};

class lessEqualNode : public operator_node {
public:
  lessEqualNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
	dpuserType evaluate();
};

class equalNode : public operator_node {
public:
  equalNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
  virtual dpuserType evaluate();
};

class notEqualNode : public equalNode {
public:
  notEqualNode(ASTNode *L, ASTNode *R) : equalNode(L, R) { }
	dpuserType evaluate();
};

class andNode : public operator_node {
public:
  andNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
	dpuserType evaluate();
};

class orNode : public operator_node {
public:
  orNode(ASTNode *L, ASTNode *R) : operator_node(L, R) { }
	dpuserType evaluate();
};

class notNode : public ASTNode {
public:
	ASTNode *right;
  notNode(ASTNode *R) : right(R) { }
	virtual ~notNode() { delete right; }
	dpuserType evaluate();
};

class assignmentNode : public ASTNode {
public:
  std::string id;
	ASTNode *exp;
	assignmentNode(std::string name, ASTNode *node) : id(name), exp(node) { }
  virtual ~assignmentNode() { delete exp; }
  dpuserType evaluate();
};

class functionNode : public ASTNode {
public:
	std::string id;
        int builtin_id;
	std::vector<ASTNode *> args;
	std::vector<std::string> options;
        functionNode(std::string func);
	functionNode *append(ASTNode *arg) { args.push_back(arg); return this; }
	functionNode *append(std::string option) { options.push_back(option); return this; }
	virtual ~functionNode() { for (int i = 0; i < args.size(); i++) delete args.at(i); }
	dpuserType evaluate();
};

class procedureNode : public ASTNode {
public:
	std::string id;
        int builtin_id;
        std::vector<ASTNode *> args;
    std::vector<std::string> options;
    procedureNode(std::string proc);
//	procedureNode(ASTNode *);
	procedureNode *append(ASTNode *arg) { args.push_back(arg); return this; }
    procedureNode *append(std::string option) { options.push_back(option); return this; }
    virtual ~procedureNode() { for (int i = 0; i < args.size(); i++) delete args.at(i); }
	dpuserType evaluate();
};

class helpNode : public ASTNode {
public:
	std::string id;
	helpNode(std::string proc) : id(proc) { }
	dpuserType evaluate();
};

class statementNode : public ASTNode {
public:
	ASTNode *stmt = NULL;
	int id;
	statementNode(ASTNode *s) : id(0), stmt(s) { }
	statementNode(std::string proc, ASTNode *arg1);
	virtual ~statementNode() { if (stmt) delete stmt; }
	dpuserType evaluate();
};

class listNode : public ASTNode {
public:
	std::vector<ASTNode *> list;
	listNode() { list.clear(); }
	listNode *append(ASTNode *element) { list.push_back(element); return this; }
	virtual ~listNode() { for (int i = 0; i < list.size(); i++) delete list.at(i); }
    dpuserType evaluate();
};

class rangeNode : public ASTNode {
public:
    std::vector<ASTNode *> list;
    std::string type;
    rangeNode() { list.clear(); type.clear(); }
    rangeNode *append(ASTNode *element) { list.push_back(element); return this; }
    rangeNode *append(std::string option) { type.append(option); return this; }
    virtual ~rangeNode() { for (int i = 0; i < list.size(); i++) delete list.at(i); }
    dpuserType evaluate();
};

class createrangeNode : public ASTNode {
public:
    ASTNode *range;
    createrangeNode(ASTNode *newrange) : range(newrange) { }
    virtual ~createrangeNode() { delete range; }
    dpuserType evaluate();
};

class extractrangeNode : public ASTNode {
public:
    ASTNode *range;
    ASTNode *argument;
    extractrangeNode(ASTNode *arg, ASTNode *newrange) : argument(arg), range(newrange) { }
    virtual ~extractrangeNode() { delete argument; delete range; }
    dpuserType evaluate();
};

class operatorRangeEqualsNode : public ASTNode {
public:
    std::string id;
    ASTNode *range;
    ASTNode *right;
    long indices[10];
    dpuserType frange;
    operatorRangeEqualsNode(std::string var, ASTNode *newrange) : id(var), range(newrange), right(NULL) { }
    operatorRangeEqualsNode(std::string var, ASTNode *newrange, ASTNode *R) : id(var), range(newrange), right(R) { }
    virtual ~operatorRangeEqualsNode() { delete range; if (right != NULL) delete right; }
    virtual dpuserType evaluate();
    long extractIndices();
};

class operatorRangePlusPlusNode : public operatorRangeEqualsNode {
public:
    operatorRangePlusPlusNode(std::string var, ASTNode *newrange) : operatorRangeEqualsNode(var, newrange) { }
    //virtual ~operatorRangePlusPlusNode() { delete range; }
    dpuserType evaluate();
};

class operatorRangeMinusMinusNode : public operatorRangeEqualsNode {
public:
    operatorRangeMinusMinusNode(std::string var, ASTNode *newrange) : operatorRangeEqualsNode(var, newrange, NULL) { }
    //virtual ~operatorRangeMinusMinusNode() { delete range; }
    dpuserType evaluate();
};

class operatorRangePlusEqualNode : public operatorRangeEqualsNode {
public:
    operatorRangePlusEqualNode(std::string var, ASTNode *newrange, ASTNode *R) : operatorRangeEqualsNode(var, newrange, R) { }
    //virtual ~operatorRangePlusEqualNode() { delete range; delete right; }
    dpuserType evaluate();
};

class operatorRangeMinusEqualNode : public operatorRangeEqualsNode {
public:
    operatorRangeMinusEqualNode(std::string var, ASTNode *newrange, ASTNode *R) : operatorRangeEqualsNode(var, newrange, R) { }
    //virtual ~operatorRangeMinusEqualNode() { delete range; delete right; }
    dpuserType evaluate();
};

class operatorRangeMultiplyEqualNode : public operatorRangeEqualsNode {
public:
    operatorRangeMultiplyEqualNode(std::string var, ASTNode *newrange, ASTNode *R) : operatorRangeEqualsNode(var, newrange, R) { }
    //virtual ~operatorRangeMultiplyEqualNode() { delete range; delete right; }
    dpuserType evaluate();
};

class operatorRangeDivideEqualNode : public operatorRangeEqualsNode {
public:
    operatorRangeDivideEqualNode(std::string var, ASTNode *newrange, ASTNode *R) : operatorRangeEqualsNode(var, newrange, R) { }
    //virtual ~operatorRangeDivideEqualNode() { delete range; delete right; }
    dpuserType evaluate();
};

class forloopNode : public ASTNode {
public:
	ASTNode *start, *check = NULL, *commands, *change = NULL, *limit = NULL;
	forloopNode(ASTNode *s, ASTNode *c, ASTNode *e, ASTNode *co) : start(s), check(c), change(e), commands(co) { }
	forloopNode(ASTNode *s, ASTNode *c, ASTNode *co) : start(s), limit(c), commands(co) { }
	virtual ~forloopNode() { delete start; delete check; delete change; delete commands; }
	dpuserType evaluate();
};

class whileNode : public ASTNode {
public:
    ASTNode *check, *commands;
    whileNode(ASTNode *c, ASTNode *co) : check(c), commands(co) { }
    virtual ~whileNode() { delete check; delete commands; }
    dpuserType evaluate();
};

class ifNode : public ASTNode {
public:
	ASTNode *expression, *commands, *elsecommands = NULL;
	ifNode(ASTNode *e, ASTNode *c) : expression(e), commands(c) { }
	ifNode(ASTNode *e, ASTNode *c, ASTNode *ec) : expression(e), commands(c), elsecommands(ec) { }
	virtual ~ifNode() { delete expression; delete commands; delete elsecommands; }
	dpuserType evaluate();
};

class userprocedureNode : public ASTNode {
public:
	ASTNode *body;
	userprocedureNode(ASTNode *b) : body(b) { }
	virtual ~userprocedureNode() { delete body; }
    dpuserType evaluate() { return body->evaluate(); }
};

class whereNode : public ASTNode {
public:
    ASTNode *comparison;
    std::string id;
    whereNode(ASTNode *c) : comparison(c), id("") { }
    whereNode(ASTNode *c, std::string var) : comparison(c), id(var) { }
    virtual ~whereNode() { delete comparison; }
    dpuserType evaluate();
};

#endif /* dpuserAST_H */
