#ifndef RPN_H
#define RPN_H

#include <iostream>
#include <deque>
#include <string>
#include <complex>

using namespace std;




class rpnfunction;

class rpnbase 
{
public:

  /* Reference counting for memory management. */
  unsigned int counter;
  virtual void inc() 
    { ++counter; }
  void dec() 
    { --counter; if (counter==0) delete this; }

  /* Constructors, destructors and deep copy. */
  rpnbase() : counter(1) {}
  virtual ~rpnbase()=0;
  virtual rpnbase* copy() const=0;

  virtual ostream& print(ostream& os) const 
    { return os << this->name(); }
  /* Associated symbol with the class. */
  virtual string const name() const 
    { return string("undefined"); }

  /* Evaluation on the stack. */
  virtual void eval( deque<rpnbase*>& ds )
    { ds.push_front(this); }
  
  /* Visitor patterns accept function forwards data type. */
  virtual void accept(deque<rpnbase*>& ds,rpnfunction& f) {}

  /* Real time type information. */
  virtual bool const isinteger() const  { return false; } 
  virtual bool const isreal() const     { return false; }
  virtual bool const iscomplex() const  { return false; }
  virtual bool const isstring() const   { return false; }
  virtual bool const isprogram() const  { return false; } 
  virtual bool const isvariable() const { return false; }
  virtual bool const isvector() const   { return false; }
  bool const isnumber() const 
    { return this->isreal() || this->isinteger(); }

};

ostream& operator << (ostream& os, rpnbase* const p); 

class rpnprogram;
class rpnstring;
class rpnreal;
class rpncomplex;
class rpninteger;
class rpnvar;
class rpnvector;


/* Derive functions */
class rpnfunction : public rpnbase
{
public:

  rpnfunction() {}
  ~rpnfunction() {}
  rpnbase* copy() const;

  virtual void visit(deque<rpnbase*>& ds,rpnprogram& p) {}
  virtual void visit(deque<rpnbase*>& ds,rpnstring& s) {}
  virtual void visit(deque<rpnbase*>& ds,rpnreal& n) {}
  virtual void visit(deque<rpnbase*>& ds,rpncomplex& n) {}
  virtual void visit(deque<rpnbase*>& ds,rpninteger& n) {}
  virtual void visit(deque<rpnbase*>& ds,rpnvar& n) {}
  virtual void visit(deque<rpnbase*>& ds,rpnvector& v) {}

  void eval( deque<rpnbase*>& ds ) 
  {
    if (!ds.empty())
      ds[0]->accept(ds,*this);

    dec();
  };

  string const name() const 
    { return string("rpnfunction"); }
};


class rpnreal : public rpnbase
{
public:

  long double num;
  typedef long double type;

  rpnreal() {}
  rpnreal(long double const n) : num(n) {} /* <TODO> Is this necessary? */
  rpnreal(deque<rpnbase*>& ds,long double const n); 
  ~rpnreal() {}
  rpnbase* copy() const;

  void accept(deque<rpnbase*>& ds,rpnfunction& f) 
    { f.visit(ds,*this); }

  ostream& print(ostream& os) const;
  string const name() const 
    { return string("rpnreal"); }

  bool const isreal() const 
    { return true; }
};


class rpncomplex: public rpnbase
{
public:

  complex< long double > num;

  rpncomplex() {}
  rpncomplex
  (
    deque<rpnbase*>& ds,
    long double const x, 
    long double const y
  ); 
  rpncomplex
  (
    deque<rpnbase*>& ds, 
    complex<long double> const & x
  ); 
  ~rpncomplex() {}
  rpnbase* copy() const;

  void accept(deque<rpnbase*>& ds,rpnfunction& f) 
    { f.visit(ds,*this); }

  ostream& print(ostream& os) const;
  string const name() const 
    { return string("rpncomplex"); }

  bool const iscomplex() const 
    { return true; }
};


class rpninteger: public rpnbase
{
public:

  long int num;
  typedef long int type;

  static unsigned int displaybase;

  rpninteger() {}
  rpninteger(deque<rpnbase*>& ds,long int const n);
  ~rpninteger() {}
  rpnbase* copy() const;

  void accept(deque<rpnbase*>& ds,rpnfunction& f) 
    { f.visit(ds,*this); }

  ostream& print(ostream& os) const;
  string const name() const 
    { return string("rpninteger"); }

  bool const isinteger() const 
    { return true; }
};


class rpnstring : public rpnbase
{
public:

  string str;

  rpnstring() {}
  rpnstring(deque<rpnbase*>& ds,string const & s);
  ~rpnstring() {}
  rpnbase* copy() const;

  void accept(deque<rpnbase*>& ds,rpnfunction& f) 
    { f.visit(ds,*this); }

  ostream& print(ostream& os) const;

  void eval( deque<rpnbase*>& ds );

  bool const isstring() const 
    { return true; }
};


/* <TODO> Restrict to access by rpnprogram, don't create on the stack. */

/* Rpn Variable class. - Used by rpnprogram. 
 */
class rpnvar : public rpnbase
{
public:

  string varname;
  rpnbase* x;  

  void inc();

  rpnvar(rpnbase* x_, string const & s);
  rpnvar(rpnprogram& prog, rpnbase* x_, string const & s);
  ~rpnvar();

  rpnbase* copy() const;
  void accept(deque<rpnbase*>& ds,rpnfunction& f) 
    { f.visit(ds,*this); }

  void eval( deque<rpnbase*>& ds )
    { rpnbase* w = x->copy(); w->eval(ds); }

  ostream& print(ostream& os) const;
  string const name() const 
    { return varname; }

  bool const isvariable() const 
    { return true; }
};


class rpnvector : public rpnbase
{
/* Counting from the stack bottom is 0 as the first element.
   Counting from the stack end is 1 as the first element.
   Then can convert between the two with the same formula:
     f(k) = n-k  eg n=6, k=1 then f(1)=5 which is 1: ,f( f(1) ) = 1.
*/
public:

  /* Counting from  the end of the stack. */
  int reverseindex;
  
  rpnvector(int index);

  rpnbase* copy() const;
  void accept(deque<rpnbase*>& ds,rpnfunction& f) 
    { f.visit(ds,*this); }

  ostream& print(ostream& os) const;
  string const name() const 
    { return "rpnvector"; }

  /* Convert between counting from the start of the stack 
     and the end of the stack. */
  int const indexcomplement(int const n) const;
  /* Return the index relative to the current stack. */
  //const int rpnvector::index() const;
  const int index() const;

  bool const isvector() const 
    { return true; }
};



class rpnprogram : public rpnbase 
{
  void vinc();
public:

  deque<rpnbase*> v;
  deque<rpnvar*> variables;

  // How the program is interpreted.
  //   bit 0 on has the program pushed onto the program stack.
  //   bit 1 on has a constant data stack, 
  //     off uses the top programs data stack.
  //   By default both bits are on.
  unsigned int state;

  void eval_11( deque<rpnbase*>& ds ); 
  void eval_10(); 
  void eval_01( deque<rpnbase*>& ds ); 
  void eval_00(); 

  void inc();

  bool const valid() const;

  rpnprogram();
  rpnprogram(deque<rpnbase*>& ds);
  rpnprogram
  (
    deque<rpnbase*>& ds,
    deque<rpnbase*>& ds2,
    bool const evaluate=true
  );
  ~rpnprogram();
  rpnbase* copy() const;

  void accept(deque<rpnbase*>& ds,rpnfunction& f) 
    { f.visit(ds,*this); }

  ostream& print(ostream& os) const;

  void eval( deque<rpnbase*>& ds ); 



  bool const isprogram() const 
    { return true; }
};


/* Access the programs state. Its a singleton class
 * which holds pointers to the systems state.
 * Also supports variable operations for var suggesting
 * that this class should be made into two classes. */ 

class rpnprogramstackstate
{
  static string findprogrampath;
  void findprogram
  (
    bool& found, 
    rpnprogram* p, 
    string const path, 
    rpnprogram* targ
  );
public:

  static rpnprogram rpnhome;

  /* The primary program pointer, is never empty. */
  static deque<rpnprogram*>* ps;

  /* Minor stack. */
  static deque<rpnbase*> ds2;

  rpnprogramstackstate() {}

  /* Client must call before and after use. */
  void init();

  /* Access the most recent program's stack. */
  deque<rpnbase*>& ds() 
    { return ps->front()->v; }
  /* Access the most recent programs's variables. */
  deque<rpnvar*>& vs() 
    { return ps->front()->variables; }

  ostream& print(ostream& os) const;

  /* Control scoping/visability of programs and consequently variables. */
  void push(rpnprogram* p)
    { ps->push_front(p); }
  void pop()
    { if(ps->size()>1) ps->pop_front(); }

  /* Add a variable to the most rescent stack program. */
  void add(rpnbase* x, string const & s);

  /* Find the program in the rpnhome tree. */
  void findprogram(bool& found, string& path, rpnprogram* targ);

  /* Searches for a named variable, in the stack order. */
  void find
  (
    bool & result,
    unsigned int & indexi,
    unsigned int & indexk,
    rpnvar* & x,
    string const & nm
  ) const;

  /* Support operations on variables. */
  void erase(string const &nm);
  void replace(rpnbase* x, string const & nm);
  void exists(bool& res, string const & nm) const;
  void evaluate(deque<rpnbase*>& ds, string const & nm) const;
  void recall(deque<rpnbase*>& ds, string const & nm) const; 
  void recallpointer(deque<rpnbase*>& ds, string const & nm) const;

};

ostream& operator << (ostream& os, rpnprogramstackstate const & ps);




#endif




