/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#ifndef _H_MACRO_
#define _H_MACRO_

#include <inc_iostream.h>
#include <stdio.h>
#include <mars.h>

#include "value.h"
#include "node.h"

class Context;
class CObject;

class Step : public Node {
public:


	Context      *Owner()       { return (Context*)Node::Owner(); }
	Step         *Next()        { return (Step*)Node::Next(); }
	virtual Step *Execute(void) { return Next();              };
	virtual int   Ready(void)   { return 1;                   };

	int ref;
	int line;

//===============================================================
	void* operator new(size_t s)   { return fast_new(s,permanent_mem); }
	void  operator delete(void *p) { fast_delete(p,permanent_mem);     }

};

//======================================================================

class Function : public Node{

	int   cnt;
	vtype *types;

protected:
	const char  *info;


	Value&    Error(const char *,...);

public:
	Context      *Owner()               { return (Context*)Node::Owner(); }

	Function(const char *n, int = -1,...);
	~Function()                         { delete[] types; }

	int GetArity(void)               { return cnt; };
	vtype *GetTypes(void)            { return types; };
	void SetTypes(int n,vtype *t)    { delete[] types; types = t; cnt = n;};
	void AddType(vtype t);
	void DeprecatedMessage(bool isDeprecated, const char *oldName, const char *newName);

	virtual int       ValidArguments(int,Value*);
	virtual Value     Execute(int,Value*)        { return Value(0.0); };
	virtual const char*     Info(void)                 { return info; }
	virtual void      Print(void);
	Value ToString(void);

//===============================================================
	void* operator new(size_t s)   { return fast_new(s,permanent_mem); }
	void  operator delete(void *p) { fast_delete(p,permanent_mem);     }

};

class Variable : public Node {

	Value     value;
	int       export_;
	Variable *import_;

public:

	Variable(const char*,Value);
	Variable(const char*,Variable*);

	void   SetValue(Value& v,int arity,Value*);
	Value& GetValue(void);
	void   Dump(int);

	void   Export(void)       { export_ = 1;    }
	int    IsExported(void)	  { return export_; }
};

//====================
class Context : public Node{

	void      AddNode(List &l,Node *n)    { l.Append(n); n->SetOwner(this); }; // append to end of list
	void      InsertNode(List &l,Node *n) { l.Insert(n); n->SetOwner(this); }; // insert at head of list

static List       DictionaryStack;
static List       DictionaryCache;

	Function  *FindInDictionary(const char *name,int arity,Value *arg);

	List	  Methods;
	List	  Functions;
	List	  Fallbacks; // Functions to search last
	List	  Handlers;
	List	  Steps;
	List	  Locals;
	List	  Globals;
	List	  Contexts;

	int       inited;
	int       oo;
	CObject*  object;

	int       error;
	int       argn;
	int       argc;
	Value     *argv;
	static  int trace;
	static  int waitmode;   // 0=asynchronous, 1=synchronous module calls
	static  int sendlines;  // 1=send currently-executing line number to the macro editor
	static  int pause;      // number of seconds to wait between executing lines of code



public:
	Variable  *FindGlobal(const char *name);
	Variable  *FindVariable(const char *name);
	Function  *FindMethod(const char *name,int arity,Value *arg);
	Function  *FindMethod(const char *name,int arity,Value *arg,CObject*);
	Function  *FindFunction(const char *name,int arity,Value *arg);
	Function  *FindFallback(const char *name,int arity,Value *arg);
	Function  *FindLibrary(const char *name,int arity,Value *arg);
	Function  *FindHandler(const char *name,int arity,Value *arg);
	Value     *GetParameters(int count);
static void    EmptyStack();


public:

static int     Trace(void)   { return trace; }
static void    Trace(int n)  { trace = n; }
static int     WaitMode(void)   { return waitmode; }
static void    WaitMode(int n)  { waitmode = n; }
static int     SendLines(void)   { return sendlines; }
static void    SendLines(int n)  { sendlines = n; }
static int     PauseBetweenLines(void)   { return pause; }
static void    PauseBetweenLines(int n)  { pause = n; }

	Context   *FindContext(const char *name);
	static const char* FallBackHandler(const char *name);
	int        HasHandler(const char *nam,int arity=0,Value *arg = NULL)
				{ return FindHandler(nam,arity,arg) != NULL; };

	int        Argc(void)  { return argc; };
	Value      *Argv(void) { return argv; };

	List       *Dictionaries(void)  { return &DictionaryStack; };
    Function   *Macro;             // If the context is a user function

	void       PushDictionary(const char *name);
	void       PopDictionary(void);
	void       Dump(int);
	Value      ImportVariable(const char *name);
	Value      ExportVariable(const char *name);

static  const char    *UniqueName(void);
static  Context *Current;          // Context beeing compiled
static  Step    *Instruction;      // current step
static  int     CurrentLine(void) { return Instruction?Instruction->line:0;}
static  const char    *InitGlobals;
static  boolean  stop;

static  Value   Metview(const char *);  // Execute a metview icon

	    Context(const char *name,int = 0);
	    ~Context();


	    Context    *Owner()   { return (Context*)Node::Owner(); }


virtual Value   Run(const char *handler = NULL,int argc = 0,Value *argv = NULL);

	    void      Store(const char*,Value,int);
	    Value&    Fetch(const char*);

	    void 	  Push(Value);
	    Value     Pop(void);

virtual void      RuntimeError(const char*,int);
	    Value&    Error(const char *,...);

static	void      Stop(void)      { stop = true; }
static  boolean   Stopped(void)   { return stop; }

	    int       GetError(void)  { return error; }
	    void      SetError(int e) { error = e;    }

		Function *WhichFunction(const char*,int,Value*,int = 0);
	    void      CallFunction(const char*,int,int = 0);
	    void      CallHandler(const char*,int);

	    void      AddExtern(const char*name,const char*cmd);
	    void      AddFunction(Function *f)   { AddNode(oo?Methods:Functions,f); }
	    void      AddFunctionToFront(Function *f)   { InsertNode(oo?Methods:Functions,f); }
	    void      AddFallback(Function *f)   { AddNode(Fallbacks,f); }
	    void      AddHandler(Function *f)    { AddNode(Handlers,f); }

	    void      AddLocal(Variable *v)      { AddNode(Locals,v); }
	    void      AddGlobal(Variable *v)     { AddNode(Globals,v); }

virtual void      NewStep(Step *)            {};
	    void      AddStep(Step *s)           { AddNode(Steps,s); NewStep(s); }
	    void      AddContext(Context *c)     { AddNode(Contexts,c); }

		void      AddParameter(const char*);

	    Value	  NextParameter(void);

		Function *FirstFunction(void) { return (Function*)Functions.Head(); };
virtual	Step *FirstStep(void)         { return (Step*)Steps.Head(); };

//===============================================================
	void* operator new(size_t s)   { return fast_new(s,permanent_mem); }
	void  operator delete(void *p) { fast_delete(p,permanent_mem);     }

//===============================================================

		CList*   GetGlobals();
		void     SetGlobals(CList*);

		CObject* GetObject()           ;
		void     SetObject(CObject* o) { object = o; }


};

class Linkage {

	typedef void (*cproc)(Context*);

	Linkage *next;
	cproc 	proc;

	static Linkage *Links;
	void Run(Context *c) { proc(c); if(next) next->Run(c);};

public :


	Linkage(cproc p)     { proc=p; next=Links; Links=this;};
	Linkage  *Next(void) { return next; };

static	void Install(Context *c)   { Links->Run(c); }
};

//======================================================================

class PlotterFunction : public Function {

	static char* plotter;
        static boolean setbyuser;
	virtual int ValidArguments(int arity,Value *arg);
	virtual Value Execute(int arity,Value *arg);

public:
	PlotterFunction(const char *n);
	static char* Plotter();
	static void SetPlotter( const char* p );
	static void Init();
};

#endif
