#include <cassert>
#include <sstream>
#include <map>
#include <string>
using namespace std;

#include <rpn.h>
#include <rpnfunc.h>
#include <mathfunc.h>
#include <singleton.h>

#include <rawinterpreter.h>


#ifndef NDEBUG
//#define DEBUG_INTERPRETER
#endif


fbuildbase* fbuilduser::copy() const
{
  x->inc();
  return new fbuilduser(x,nm);
}

void fbuilduser::make() const
{
  x->inc();
  if (inputstatescope::cscope->evalimmediate)
    x->eval(rpnprogramstackstate().ds());
  else
    rpnprogramstackstate().ds().push_front(x);
}

fbuilduser::fbuilduser(rpnbase* _x, string const & _nm)
  : x(_x), nm(_nm)
{
}

fbuilduser::~fbuilduser()
{
  x->dec();
}

string const fbuilduser::name() const
{
  return nm;
}
    

fdatainterp::fdatainterp()
{
  init();
}

void fdatainterp::init()
{
  addtodictionary( passtype<rpnprogset>() );
  addtodictionary( passtype<rpnpush>() );
  addtodictionary( passtype<rpnpop>() );
  addtodictionary( passtype<rpnpushn>() );
  addtodictionary( passtype<rpnpopn>() );
  addtodictionary( passtype<dssize2>() );
  addtodictionary( passtype<rpneval>() );
  addtodictionary( passtype<rpnclear>() );
  addtodictionary( passtype<rpnclearvar>() );
  addtodictionary( passtype<rpnclearboth>() );
  addtodictionary( passtype<rpndup>() );
  addtodictionary( passtype<rpndupn>() );
  addtodictionary( passtype<dssize>() );
  addtodictionary( passtype<rev>() );
  addtodictionary( passtype<rot>() );
  addtodictionary( passtype<rotn>() );
  addtodictionary( passtype<rpnswap>() );
  addtodictionary( passtype<rpnswap2>() );
  addtodictionary( passtype<drop>() );
  addtodictionary( passtype<dropi>() );
  addtodictionary( passtype<dropsymb>() );
  addtodictionary( passtype<dropn>() );
  addtodictionary( passtype<ifthen>() );
  addtodictionary( passtype<thenif>() );
  addtodictionary( passtype<ifthenelse>() );
  addtodictionary( passtype<thenelseif>() );
  addtodictionary( passtype<var>() );
  addtodictionary( passtype<vardel>() );
  addtodictionary( passtype<vareval>() );
  addtodictionary( passtype<varpushd>() );
  addtodictionary( passtype<varpopd>() );
  addtodictionary( passtype<depthd>() );
  addtodictionary( passtype<varls>() );
  addtodictionary( passtype<vartree>() );
  addtodictionary( passtype<varpwd>() );
  addtodictionary( passtype<pathtoggle>() );
  addtodictionary( passtype<varinc>() );
  addtodictionary( passtype<vardec>() );
  addtodictionary( passtype<varexists>() );
  addtodictionary( passtype<varreplace>() );
  addtodictionary( passtype<varrecall>() );
  addtodictionary( passtype<rpnnot>() );
  addtodictionary( passtype<rpnneg>() );
  addtodictionary( passtype<rpnadd>() );
  addtodictionary( passtype<rpnmultiply>() );
  addtodictionary( passtype<rpnsubtract>() );
  addtodictionary( passtype<rpndivide>() );
  addtodictionary( passtype<rpnlessthan>() );
  addtodictionary( passtype<rpnlessthanequal>() );
  addtodictionary( passtype<rpngreaterthan>() );
  addtodictionary( passtype<rpngreaterthanequal>() );
  addtodictionary( passtype<rpnequal>() );
  addtodictionary( passtype<rpnfor>() );
  addtodictionary( passtype<rpnforn>() );
  addtodictionary( passtype<prognew>() );
  addtodictionary( passtype<progdecompose>() );
  addtodictionary( passtype<progrev>() );
  addtodictionary( passtype<isrpnstring>() );
  addtodictionary( passtype<isrpninteger>() );
  addtodictionary( passtype<isrpnreal>() );
  addtodictionary( passtype<isrpncomplex>() );
  addtodictionary( passtype<isrpnprogram>() );
  addtodictionary( passtype<pathcd>() );
  addtodictionary( passtype<pathquery>() );
  addtodictionary( passtype<varmv>() );
  addtodictionary( passtype<rpncp>() );
  addtodictionary( passtype<rpninsert>() );
  addtodictionary( passtype<rpnerase>() );
  addtodictionary( passtype<rpnpointermake>() );
  addtodictionary( passtype<rpnvectormake>() );
  addtodictionary( passtype<rpnvectorpointermake>() );
  addtodictionary( passtype<pstream>() );
  addtodictionary( passtype<rpnintegerconvert>() );
  addtodictionary( passtype<rpnstringconvert>() );
  addtodictionary( passtype<rpnfactorial>() );
  addtodictionary( passtype<rpnmod>() );
  addtodictionary( passtype<rpngcd>() );
  addtodictionary( passtype<rpnascii>() );

  
  addtodictionary( passtype<rpnfcos>() );
  addtodictionary( passtype<rpnfsin>() );
  addtodictionary( passtype<rpnftan>() );
  addtodictionary( passtype<rpnflog>() );
  addtodictionary( passtype<rpnflog10>() );
  addtodictionary( passtype<rpnfexp>() );
  addtodictionary( passtype<rpnfsqrt>() );
  addtodictionary( passtype<rpnfcosh>() );
  addtodictionary( passtype<rpnfsinh>() );
  addtodictionary( passtype<rpnftanh>() );
  addtodictionary( passtype<rpnfacos>() );
  addtodictionary( passtype<rpnfasin>() );
  addtodictionary( passtype<rpnfatan>() );
  addtodictionary( passtype<rpnffloor>() );
  addtodictionary( passtype<rpnfceil>() );
  addtodictionary( passtype<rpnpow>() );
  addtodictionary( passtype<rpnfabs>() );
  addtodictionary( passtype<rpnfarg>() );
  addtodictionary( passtype<rpnfnorm>() );
  addtodictionary( passtype<rpnfimag>() );
  addtodictionary( passtype<rpnfconj>() );
  addtodictionary( passtype<rpnrealconvert>() );
  addtodictionary( passtype<rpnr_to_d>() );
  addtodictionary( passtype<rpnd_to_r>() );
  addtodictionary( passtype<rpnp_to_c>() );
  addtodictionary( passtype<rpnr_to_c>() );



  addtodictionary( passtype< rpnintegerhex >() );
  addtodictionary( passtype< rpnintegeroct >() );
  addtodictionary( passtype< rpnintegerdec >() );
  addtodictionary( passtype< rpnintegerbin >() );
  addtodictionary( passtype< rpnbitnot >() );
  addtodictionary( passtype< rpnbitxor>() );
  addtodictionary( passtype< rpnbitor >() );
  addtodictionary( passtype< rpnbitand>() );
  addtodictionary( passtype< rpnbitshiftleft>() );
  addtodictionary( passtype< rpnbitshiftright>() );


}

void fdatainterp::dictionarydelete()
{
  if (!dict.empty())
  {
    for( map<string,fbuildbase*>::iterator i = dict.begin();
      i!=dict.end(); ++i )
      delete i->second; 
  
    dict.clear();
  }
}

void fdatainterp::reset()
{
  dictionarydelete();

  // Build the dictionary.
  for (unsigned int i=0, n=dicttable.size(); i<n; ++i)
    add( dicttable[i]->copy() );
}

fdatainterp::~fdatainterp()
{
  dictionarydelete();
}

rpnreal::type const fdatainterp::real(string const & word) const
{
  stringstream ss(word);
  rpnreal::type var;
  ss >> var;

  return var;
}

rpninteger::type const fdatainterp::integer(string const & word) const
{
  stringstream ss(word);
  rpninteger::type var;
  ss >> var;

  return var;
}

void fdatainterp::eval(string const & word)
{
// 1. Determine if word is a number else
// 2. Determine if word is in the dictionary else
// 3. assume word is a string.
  char ch = word[0];

  if ( (ch=='-') && (word.size()>1))
    ch = word[1];

  if ( (ch=='.') && (word.size()>1))
    ch = word[1];

  if ( isdigit(ch) )
  {
    // Is the number real or integer. 
    bool integertype=true;
    for (unsigned int i=0, imax=word.size(); i<imax; ++i)
    {
      if( word[i]=='.')
      {
        integertype=false;
        i=imax;
      }
    }

    // Construct the number.
    if (integertype)
      new rpninteger( rpnprogramstackstate().ds(), integer(word) );
    else
      new rpnreal( rpnprogramstackstate().ds(), real(word) ); 
   
    return;
  }

  // Not a number 

#ifdef DEBUG_INTERPRETER
cout << "Input: Not a number." << endl;
#endif

  // The user can force a string onto the stack.
/*
  if (ch=='"')
  {
    if (word.size()==1)
      return;

    string w(word.begin()+1,word.end());
    new rpnstring( rpnprogramstackstate().ds(), w );

    return;
  }
*/

  // Search the dictionary. 

  // The # symbol indicates the item should not be evaluated. 
  // The @ symbol indicates the item should be evaluated.     
  if ((ch=='#')||(ch=='@'))
  {
    if (word.size()<=2)
    {
      if (word.size()==1)
        return; // Ignore lone # or @ character.

      // Setting the mode was so important that I took it away
      // from the standard function system. 
      if (word=="@+")
      {  // Set immediate evaluation mode. 
        inputstatescope::cscope->evalimmediate = true;
        return;
      }

      if (word=="@-")
      { // Unset immediate evaluation mode.
        inputstatescope::cscope->evalimmediate = false;
        return;
      }
    }

    bool tmpeval = true;
    if (ch=='#')
      tmpeval = false;

    string word2(word.begin()+1,word.end());
      
    map<string,fbuildbase*>::iterator i = dict.find(word2);
    if (i!=dict.end())
    {
      inputstatescope::cscope->evaloverride = true;
      inputstatescope::cscope->evalpreserved = 
        inputstatescope::cscope->evalimmediate;
      inputstatescope::cscope->evalimmediate = tmpeval;
 
      i->second->make();

      inputstatescope::cscope->evalimmediate = 
          inputstatescope::cscope->evalpreserved;
      inputstatescope::cscope->evaloverride = false;

      return;
    }

  }
  else
  {

#ifdef DEBUG_INTERPRETER
cout << "Input: Not a evaluation directive, word=" << 
  word  << "." << endl;
#endif

    map<string,fbuildbase*>::iterator i = dict.find(word);
    if (i!=dict.end())
    {
      i->second->make();
  
      return;
    }
  }

  // Assume a string

#ifdef DEBUG_INTERPRETER
cout << "Input: string*" << word << "*" << endl;
#endif

  new rpnstring( rpnprogramstackstate().ds(), word );
};


class noaction
{
public:

  void operator()() {}
};



inputstatescope* inputstatescope::cscope = 0;
inputstate*      inputstatescope::cstate = 0;

inputstatescope::inputstatescope()
{
  assert(cscope==0); // This is a singleton class. 

  inner = new innerinput();
  outer = new outerinput(); 
  error = false;
  evalimmediate = true;
  evaloverride=false;

  cscope = this;

  setinner();
}

inputstatescope::~inputstatescope()
{
  delete inner;
  delete outer;
  cstate=0;
}

void inputstatescope::setinner()
{
  cstate = inner;
}

void inputstatescope::setouter()
{
  cstate = outer;
}

void inputstatescope::eval(string const & word)
{
  cstate->eval(word);
}
  

void innerinput::eval(string const & word)
{

  if (word=="{")
  {
    rpnprogram* p = new rpnprogram();
    rpnprogramstackstate().ds().push_front(p);   
    rpnprogramstackstate().push( p );

    // Commented out to treat all programs equally.    
    // inputstatescope::cscope->evalimmediate = false; 
    inputstatescope::cscope->setouter();
    return;
  }    

  inputstatescope::cscope->fd.eval(word);
}


void outerinput::eval(string const & word)
{
  if (word=="}")
  {
    if(rpnprogramstackstate().ps->size() == 2)
    {
      rpnprogramstackstate().pop();
      // Expecting a program on the data stack. 

      // Programs are not evaluated when placed on the stack.

      inputstatescope::cscope->setinner();
      return;
    }

    // Assume rpnprogramstackstate().ps->size() > 2.
    rpnprogramstackstate().pop();
    return;
  }

  if (word=="{")
  {
    rpnprogram* p = new rpnprogram();
    rpnprogramstackstate().ds().push_front(p);   
    rpnprogramstackstate().push( p );

    return;
  } 

  inputstatescope::cscope->fd.eval(word);
}


//
//  I am experimenting with streams.  In theory
//  reading a file and getting user input are both
//  istreams so should use the same code, but they
//  behave differently.
//
//  The code is really similar. This version is
//  getting from a file. The end of file needs to
//  be checked so that an exit can occure. The keyboard
//  read needs no such test.
//
//  quit will not work, it leaves the local stream only.
void fdatainterp::process2_silent( istream & is )
{
  char c;
  char const quote('"');
  char const space(' ');
  char const ret('\n');

  string s;

  string const quit("quit");

  while ((c = is.peek()))
  {
//cout << "c=" << c << endl;
    s.clear();

    if (c==quote)
    {
      is.get(c);
      is.get(c);
      while (c!=quote)
      {
        s += c;
        is.get(c);
        if (!is)
          return;
      }

      new rpnstring( rpnprogramstackstate().ds(), s );
    }
    else
    if (c=='(')
    {
      complex< long double > x;
      is >> x;
     
      new rpncomplex( rpnprogramstackstate().ds(), x );

    }
    else
    {
      is.get(c);
      if ((c!=space)&&(c!=ret))
      {
        s += c;
        is.get(c);
//cout << "c=" << c << endl;
        while ((c!=space)&&(c!=ret))
        {
          s += c;
          is.get(c);
//cout << "c=" << c << endl;
          if (!is)
            return;
        }
      }

      if (!s.empty())
        SingletonPtr<inputstatescope>()->eval(s);

    }

    if (!is)
      return;

    if (s==quit)
      break;
  }
}






