

#ifndef NDEBUG
//#define DEBUG_SCOPEDEPENDENTFUNCTIONS_H
#endif

#include <cassert>
#include <deque>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;

#include <rpn.h>
#include <rawinterpreter.h>
#include <singleton.h>




#include <scopedependentfunctions.h>



// Implementation
// ---------------------------------------------------------------
//



stateevalset::stateevalset(deque<rpnbase*>& ds, bool const evaluate)
{
  if (evaluate)
    eval(ds);
  else
    ds.push_front(this);
}

rpnbase* stateevalset::copy() const
{
  return new stateevalset(); 
}

void stateevalset::eval( deque<rpnbase*>& ds )
{
  if (inputstatescope::cscope->evaloverride)
    inputstatescope::cscope->evalpreserved = true;
  else
    inputstatescope::cscope->evalimmediate = true;

  dec();
}


stateevalunset::stateevalunset(deque<rpnbase*>& ds, bool const evaluate)
{
  if (evaluate)
    eval(ds);
  else
    ds.push_front(this);
}

rpnbase* stateevalunset::copy() const
{
  return new stateevalunset(); 
}

void stateevalunset::eval( deque<rpnbase*>& ds )
{
  if (inputstatescope::cscope->evaloverride)
    inputstatescope::cscope->evalpreserved = false;
  else
    inputstatescope::cscope->evalimmediate = false;

  dec();
}


stateevalquery::stateevalquery(deque<rpnbase*>& ds, bool const evaluate)
{
  if (evaluate)
    eval(ds);
  else
    ds.push_front(this);
}

rpnbase* stateevalquery::copy() const
{
  return new stateevalquery(); 
}

void stateevalquery::eval( deque<rpnbase*>& ds )
{
  int val;

  if (inputstatescope::cscope->evaloverride)
  {
    if (inputstatescope::cscope->evalpreserved)
      val = 1;
    else 
      val = 0;
  }
  else
  {
    if (inputstatescope::cscope->evalimmediate)
      val = 1;
    else 
      val = 0;
  }

  new rpninteger(ds,val);

  dec();
}

load::load(deque<rpnbase*>& ds, bool const evaluate)
{
  if (evaluate)
    eval(ds);
  else
    ds.push_front(this);
}

rpnbase* load::copy() const
{
  return new load();
}

void load::eval( deque<rpnbase*>& ds )
{
  if(ds.empty())
    return;

  ds[0]->accept(ds,*this);

  dec();
}



void load::visit(deque<rpnbase*>& ds,rpnstring& fname)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();

  ifstream input( fname.str.c_str() );
  if (!input) 
  {
    ds.push_front(x0);
    return;
  }

  SingletonPtr<inputstatescope>()->fd.reset();

  bool const preservedmode = SingletonPtr<inputstatescope>()->evalimmediate;

  //istream & is(input);
  SingletonPtr<inputstatescope>()->fd.process2_silent(input);


/* // Killed 21/11/04 because using streams explicitly.
  bool getinput=true;
  string s;
  while ( getinput )
  {
    if (!input)
      break;

    getline(input,s);
    SingletonPtr<inputstatescope>()->fd.process(getinput,s);
  }
*/


/*  // Preserved because it works. 21/12/03
  string s;
  bool getinput=true;

  vector<string> tokens;
  for ( ; getinput ; )
  {
    if (!input)
      break;

    getline(input,s);
      
    linesplit(tokens,s," ");
    if (!tokens.empty())
    {
      for (unsigned int i=0, imax=tokens.size(); i<imax; ++i)
      {
        if (tokens[i]=="quit")
        {
          getinput=false;
          break;
        }

        SingletonPtr<inputstatescope>()->eval(tokens[i]);

      }
    }
  }
*/

  SingletonPtr<inputstatescope>()->evalimmediate = preservedmode;
 
  // Load the user settings. 
  bin(); 

  x0->dec();
}

void load::bin()
{
  rpnprogram* hm = rpnprogramstackstate().ps->back();
  if (hm->variables.empty())
    return;

  bool notfound=true;
  rpnprogram* bin;
  rpnvar* var;
  for (unsigned int i=0, imax=hm->variables.size(); i<imax; ++i)
  {
    var = hm->variables[i];
    if (var->varname=="bin")
    {
      if (! var->x->isprogram() )
        return;

      notfound=false;
      i=imax;
      bin = (rpnprogram*)(var->x);
    }
  }

  if (notfound)
    return;

  if (bin->variables.empty())
    return;

  /* Warning. This code is to do exactly what
     dictadd code does. So in essence I have
     the identical code functionality in two 
     different places of the source.
     This cost me about 4 hrs to track down.

     Someone could have done it much faster,
     but the point is that code functionality
     should always be unique. load shoud be
     transfering responsibility to dictadd.

     However dictadd builds the thing and so
     load and dictadd are coupled.

     Its the coupled relationship between
     load and dictadd that I failed to 
     understand. Its in the code only in that
     the classes are in the same file and this
     is just a hint.
    
  */

  for (unsigned int i=0, imax=bin->variables.size(); i<imax; ++i)
  {
    var = bin->variables[i];
    var->x->inc();
    SingletonPtr<inputstatescope>()->fd.add( new fbuilduser(var->x,var->varname) );
  }
}


rpnsave::rpnsave(deque<rpnbase*>& ds, bool const evaluate)
{
  if (evaluate)
    eval(ds);
  else
    ds.push_front(this);
}

rpnbase* rpnsave::copy() const
{
  return new rpnsave();
}

void rpnsave::eval( deque<rpnbase*>& ds )
{
  if(ds.empty())
    return;

  ds[0]->accept(ds,*this);

  dec();
}


//
//  Chelton  Oct 24th 2004 : readable file output
//
//  The code writevariables and writeprogram is hack and
//    slash coding. Its human readable : matching braces
//    appear at the start of a line. So by looking at the
//    output you can determine what the variables are. 
//
//  The code was originally not written to be readable and
//  just for file reading/writing but at some point editing 
//  a file by hand is useful.
//

void rpnsave::writevariables(ostream& os, rpnprogram* p)
{

  if (p->variables.empty())
    return;

  deque<rpnvar*>& var = p->variables;

  bool pwrite(false);

  for (unsigned int i=var.size(); i>0; )
  //for (unsigned int i=0, imax=var.size(); i<imax; ++i)
  {
    --i;

    if (var[i]->x->isprogram())
    {
      os << endl << "{ ";
      writeprogram(os, (rpnprogram*)(var[i]->x) );
      os << "} " << endl;
      pwrite=true;
    }
    else
      var[i]->x->print(os) << " ";

    os << var[i]->varname << " " << "@var ";

    if (pwrite)
      os << endl;

  }

}

void rpnsave::writeprogram(ostream& os, rpnprogram* p)
{

  bool pwrite(false);
  
  /* Write the data stack. */
  if (!p->v.empty())
  {
    os << "@-" << " ";
    for (unsigned int i=0, imax=p->v.size(); i<imax; ++i )
    //for (unsigned int i=p->v.size(); i>0; )
    {
      //--i;

      rpnbase* x = p->v[i];
      if (x->isprogram())
      {
        os << endl << "{ ";
        writeprogram( os,(rpnprogram*)x );
        os << "} ";
        pwrite=true;
      }
      else
      {
        x->print(os) << " ";
      }
    
    }

    os << "@rev" << " ";    
    if (pwrite)
      os << endl;

  }

  writevariables(os,p);
}

void rpnsave::visit(deque<rpnbase*>& ds,rpnstring& fname)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();

  ofstream os( fname.str.c_str(), ios::out|ios::trunc );
  if (!os) 
  {
    ds.push_front(x0);
    return;
  }

  bool const preservedmode = SingletonPtr<inputstatescope>()->evalimmediate;

  writeprogram(os,rpnprogramstackstate().ps->front());

  os << endl;

  os.clear();

  SingletonPtr<inputstatescope>()->evalimmediate = preservedmode;

  x0->dec();
}


dictadd::dictadd(deque<rpnbase*>& ds, bool const evaluate)
{
  if (evaluate)
    eval(ds);
  else
    ds.push_front(this);
}

rpnbase* dictadd::copy() const
{
  return new dictadd();
}

void dictadd::eval( deque<rpnbase*>& ds )
{
  if(ds.empty())
    return;

  ds[0]->accept(ds,*this);

  dec();
}

void dictadd::visit(deque<rpnbase*>& ds,rpnstring& fname)
{
  if (ds.size()<2)
    return;

  rpnbase* x0 = ds.front();
  ds.pop_front();
  rpnbase* x1 = ds.front();
  ds.pop_front();
  
  rpnprogram* hm = rpnprogramstackstate().ps->back();
  rpnprogram* bin;
  bool notfound=true;
  if (!hm->variables.empty())
  {
    rpnvar* var;
    for (unsigned int i=0, imax=hm->variables.size(); i<imax; ++i)
    {
      var = hm->variables[i];
      if (var->varname=="bin")
      {
        notfound=false;
        i=imax;
        bin = (rpnprogram*)(var->x);

        if (! var->x->isprogram() ) /* Restore the stack. */
        {
          ds.push_front(x1);
          ds.push_front(x0);

          return;
        }

      }
    }
  }
  if (notfound)
  {
    bin = new rpnprogram();
    new rpnvar(*hm,bin,"bin");
  }

  /* x1 is now owned by the rpn variable. */
  new rpnvar(*bin,x1,fname.str);

  /* fbuild user has a pointer to x1. */
  x1->inc();
  SingletonPtr<inputstatescope>()->fd.add( new fbuilduser(x1,fname.str) );

  x0->dec();
}


rpninterp::rpninterp(deque<rpnbase*>& ds, bool const evaluate)
{
  if (evaluate)
    eval(ds);
  else
    ds.push_front(this);
}

rpnbase* rpninterp::copy() const
{
  return new rpninterp();
}

void rpninterp::eval( deque<rpnbase*>& ds )
{
  if(ds.empty())
    return;

  ds[0]->accept(ds,*this);

  dec();
}

void rpninterp::visit(deque<rpnbase*>& ds,rpnstring& s)
{
  stringstream ss(s.str.c_str());
  SingletonPtr<inputstatescope>()->fd.process2_silent(ss);
}


/*
read::read(deque<rpnbase*>& ds, bool const evaluate)
{
  if (evaluate)
    eval(ds);
  else
    ds.push_front(this);
}

rpnbase* read::copy() const
{
  return new read();
}

void read::eval( deque<rpnbase*>& ds )
{
  if(ds.empty())
    return;

  ds[0]->accept(ds,*this);

  dec();
}

void read::visit(deque<rpnbase*>& ds,rpnstring& fname)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();

  ifstream input( fname.str.c_str() );
  if (!input) 
  {
    ds.push_front(x0);
    return;
  }

  bool getinput=true;
  string s;
  while ( getinput )
  {
    if (!input)
      break;

    getline(input,s);
    SingletonPtr<inputstatescope>()->fd.process(getinput,s);
  }

  x0->dec();
}
*/


initscopedependentfunctions::initscopedependentfunctions()
{
#ifdef DEBUG_SCOPEDEPENDENTFUNCTIONS_H
cout << "initscopedependentfunctions()" << endl;
#endif

  addtothedictionary< dictadd >();
  addtothedictionary< stateevalset >();
  addtothedictionary< stateevalunset >();
  addtothedictionary< stateevalquery >();
  addtothedictionary< load >();
  addtothedictionary< rpnsave >();
  addtothedictionary< rpninterp >();
//  addtothedictionary< read >();
}




