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

class UserFunction : public Function
{
    Value Execute(int arity, Value* arg) override;

protected:
    Context* Address;

public:
    UserFunction(const char* nam, Context* s) :
        Function(nam) { Address = s; }

    Context* getAddress() const {return Address;}
    bool isUserFunction() const override {return true;}
    void accept(NodeVisitor *v) override {v->visit(this);}
};

class ObjectFunction : public UserFunction
{
    virtual Value Execute(int arity, Value* arg);

public:
    ObjectFunction(const char* nam, Context* s) :
        UserFunction(nam, s) {}
};

class ExternFunction : public UserFunction
{
    char* cmd;
    char* file;

    int compiled;
    int Compile(void);
    Value Execute(int arity, Value* arg) override;

public:
    void SetCommand(const char* c, const char* f);
    ExternFunction(const char* name, Context* s);
    ~ExternFunction();
    char* getCmd() const {return cmd;}
    char* getFile() const {return file;}
    void accept(NodeVisitor *v) override {v->visit(this);}
};

class OpPop : public Step
{
    void Print(void) override { printf("pop\n"); }
    Step* Execute(void) override ;
    void accept(NodeVisitor *v) override {v->visit(this);}
};


class OpStore : public Step
{
    Step* Execute(void) override;

protected:
    int Count;
//    char* Name;
    void Print(void) override { printf("store %s [%d]\n", Name() ? Name() : "(null)", Count); }

public:
    OpStore(const char* nam, int n = 0) : Step(nam)
    {
//        Name = strcache(nam);
        Count = n;
    }
//    ~OpStore() { strfree(Name()); }
    void accept(NodeVisitor *v) override {v->visit(this);}
    int getCount() const {return Count;}
};


class OpPush : public OpStore
{
    int type;

    Step* Execute(void) override;
    void Print(void) override { printf("push %s (%d)\n", Name() ? Name() : "(null)", type); }

public:
    OpPush(const char* nam, int t) :
        OpStore(nam) { type = t; }

    int getType() const {return type;}
    void accept(NodeVisitor *v) override {v->visit(this);}
};

class OpParam : public OpStore
{
    Step* Execute(void) override;

public:
    OpParam(const char* nam) :
        OpStore(nam) {}
    void accept(NodeVisitor *v) override {v->visit(this);}
    void Print(void) override { printf("param %s\n", Name() ? Name() : "(null)"); }
};

class OpCall : public OpStore
{
    int Arity;
    Step* Execute(void) override;
    void Print(void) override { printf("call %s %d\n", Name() ? Name() : "(null)", Arity); }

public:
    OpCall(const char* nam, int arity) :
        OpStore(nam) { Arity = arity; }
    void accept(NodeVisitor *v) override {v->visit(this);}
    int arity() const {return Arity;}
};

class OpGoto : public Step
{
    Step* Branch;
    void Print(void) override { printf("goto\n"); }

public:
    using Step::Step;
    OpGoto() { Branch = nullptr; }
    Step* Execute(void) override;
    void SetBranch(Step* b) { Branch = b; }
    int Ready(void) override { return Branch != nullptr; }
    void accept(NodeVisitor *v) override {v->visit(this);}
};

class OpTest : public OpGoto
{
    void Print(void) override { printf("test\n"); }
    Step* Execute(void) override;

public:
    using OpGoto::OpGoto;
    bool Pass(Value&);
    void accept(NodeVisitor *v) override {v->visit(this);}
};

class OpReturn : public Step
{
    void Print(void) override { printf("return\n"); }
    Step* Execute(void) override;
public:
    void accept(NodeVisitor *v) override  {v->visit(this);}
};


class OpConvert : public Step
{
    void Print(void) override { printf("convert %s \n", Name()); }
    Step* Execute(void) override {return Next();}

public:
    OpConvert(const char* nam, int n = 0) : Step(nam), numId_(n) {}
    void accept(NodeVisitor *v) override  {v->visit(this);}
    int numId() const {return numId_;}

protected:
    int numId_{-1};
};


class OpConvertFor : public OpConvert
{
    void Print(void) override { printf("convert for \n"); }

public:
    using OpConvert::OpConvert;
    void accept(NodeVisitor *v) override  {v->visit(this);}
};

class OpConvertLoop : public OpConvert
{
    void Print(void) override { printf("convert loop \n"); }

public:
    using OpConvert::OpConvert;
    void accept(NodeVisitor *v) override  {v->visit(this);}
};

class OpComment : public OpStore
{
    void Print(void) override { printf("comment\n"); }
public:
    using OpStore::OpStore;
    void accept(NodeVisitor *v) override  {v->visit(this);}
};

class OpConvertFnStart : public OpStore
{
    void Print(void) override { printf("function start\n"); }
public:
    using OpStore::OpStore;
    void accept(NodeVisitor *v) override  {v->visit(this);}
};
