/*****
* process.h
* Andy Hammerlindl 2006/08/19
*
* Handles processing blocks of code (including files, strings, and the
* interactive prompt, for listing and parse-only modes as well as actually
* running it.
*****/

#pragma once

#include "common.h"
#include "stm.h"
#include "stack.h"
#include "pipestream.h"
#include "callable.h"
#include "pen.h"
#include "dec.h"
#include "transform.h"
#include "asyparser.h"

#ifdef HAVE_LIBTIRPC
#include "xstream.h"
#endif

// Process the code respecting the parseonly and listvariables flags of
// settings.
void processCode(absyntax::block *code);
void processFile(const string& filename, bool purge=false);
void processPrompt();

// Run the code in its own environment.
void runCode(absyntax::block *code);
void runString(const string& s, bool interactiveWrite=false);
void runFile(const string& filename);
void runPrompt();

// Run the code in a given run-time environment.
typedef vm::interactiveStack istack;
void runCodeEmbedded(absyntax::block *code, trans::coenv &e, istack &s);
void runStringEmbedded(const string& str, trans::coenv &e, istack &s);
void runPromptEmbedded(trans::coenv &e, istack &s);

// Basic listing.
void doUnrestrictedList();

template<class T>
class terminator {
public:
 typedef mem::vector<T *> Pointer;
 Pointer pointer;

 // Return first available index
 size_t available() {
   size_t index=0;
   for(auto p=pointer.begin(); p != pointer.end(); ++p) {
     if(*p == NULL) {return index;}
     ++index;
   }
   pointer.push_back(NULL);
   return index;
 }

 size_t add(T *p) {
   size_t index=available();
   pointer[index]=p;
   return index;
 }

 void remove(size_t index) {
   pointer[index]=NULL;
 }

 ~terminator() {
   for(auto p=pointer.begin(); p != pointer.end(); ++p) {
     if(*p != NULL) {
       (*p)->~T();
       (*p)=NULL;
     }
   }
 }
};

class texstream : public iopipestream {
public:
 ~texstream();
};

typedef std::pair<size_t,size_t> linecolumn;
typedef mem::map<const linecolumn,string> xkey_t;
typedef mem::deque<camp::transform> xtransform_t;
typedef mem::map<const string,xtransform_t> xmap_t;
typedef mem::map<const size_t,types::signature *> sigMap_t;

struct processDataStruct {
 texstream tex; // Bi-directional pipe to latex (to find label bbox)
 mem::list<string> TeXpipepreamble;
 mem::list<string> TeXpreamble;
 vm::callable *atExitFunction;
 vm::callable *atUpdateFunction;
 vm::callable *atBreakpointFunction;
 camp::pen defaultpen;
 camp::pen currentpen;

 sigMap_t sigMap;

 // For xasy:
 string fileName;
 position topPos;
 string KEY;
 xkey_t xkey;
 xmap_t xmap;
 unsigned int xmapCount;

 terminator<std::ofstream> ofile;
 terminator<std::fstream> ifile;
#ifdef HAVE_LIBTIRPC
 terminator<xdr::ixstream> ixfile;
 terminator<xdr::oxstream> oxfile;
#endif

 processDataStruct() : xmapCount(0) {
   atExitFunction=NULL;
   atUpdateFunction=NULL;
   atBreakpointFunction=NULL;
   defaultpen=camp::pen::initialpen();
   currentpen=camp::pen();
 }

};

enum transMode {
 TRANS_INTERACTIVE,
 TRANS_NORMAL
};

// Abstract base class for the core object being run in line-at-a-time mode, it
// may be a block of code, file, or interactive prompt.
struct icore {
 virtual ~icore() {}
 virtual void doParse() = 0;
 virtual void doList() = 0;

public:
 virtual void preRun(trans::coenv &e, istack &s);
 virtual void run(trans::coenv &e, istack &s, transMode tm=TRANS_NORMAL) = 0;
 virtual void postRun(trans::coenv &, istack &s);
 virtual void doRun(bool purge=false, transMode tm=TRANS_NORMAL);
 virtual void process(bool purge=false);
};

// Abstract base class for one-time processing of an abstract syntax tree.
class itree : public icore {
 string name;
 absyntax::block *cachedTree;
public:
 itree(string name="<unnamed>");
 virtual absyntax::block *buildTree() = 0;

 // Build the tree, possibly throwing a handled_error if it cannot be built.
 virtual absyntax::block *getTree();
 virtual string getName();

 void doParse();
 void doList();
 void run(trans::coenv &e, istack &s, transMode tm=TRANS_NORMAL);
 void doExec(transMode tm=TRANS_NORMAL);
};

class ifile : public itree {
 string filename;
 string outname;
 string outname_save;

public:
 ifile(const string& filename);
 absyntax::block *buildTree();

 void preRun(trans::coenv& e, istack& s);
 void postRun(trans::coenv &e, istack& s);
 void process(bool purge=false);
};

class icode : public itree {
 absyntax::block *tree;

public:
 icode(absyntax::block *tree, string name="<unnamed>")
         : itree(name), tree(tree) {}

 absyntax::block *buildTree() {
   return tree;
 }
};

class istring : public itree {
 string str;

public:
 istring(const string& str, string name="<eval>")
         : itree(name), str(str) {}

 absyntax::block *buildTree() {
   return parser::parseString(str, getName());
 }
};

processDataStruct &processData();