/***************************** 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 *************************************/

#pragma once

#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, const char*, const char*);
    Value ToString(void);

    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);

    //===============================================================
    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, bool& nameFound, 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
    static int baseIndex;             // index of first element: 1 for Macro, 0 for Python
    static const char* baseLanguage;  // which language we are running from (macro or python)


public:
    Variable* FindGlobal(const char* name);
    Variable* FindVariable(const char* name);
    Function* FindMethod(const char* name, bool& nameFound, int arity, Value* arg);
    Function* FindMethod(const char* name, bool& nameFound, int arity, Value* arg, CObject*);
    Function* FindFunction(const char* name, bool& nameFound, int arity, Value* arg);
    Function* FindFallback(const char* name, bool& nameFound, int arity, Value* arg);
    Function* FindLibrary(const char* name, bool& nameFound, 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; }
    static int BaseIndex(void) { return baseIndex; }
    static void BaseIndex(int n) { baseIndex = n; }
    static const char* BaseLanguage(void) { return baseLanguage; }
    static void BaseLanguage(const char* s) { baseLanguage = s; }
    static bool IsMacro() { return !strcmp(BaseLanguage(), "macro"); }
    static bool IsPython() { return !strcmp(BaseLanguage(), "python"); }


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

    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 void SetInstruction(Step* s) { Instruction = s; }
    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 = nullptr, int argc = 0, Value* argv = nullptr);

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

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

    virtual void RuntimeError(const char*, int);
    void PrintErrorMessage(const char*);
    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; }
    void SetError(const char*);

    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();
};
