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

#include "macro.h"

CObject::CObject(const char* nam, Context* c) :
    Content(tobject),
    name(strcache(nam)),
    context(c),
    members(context->GetGlobals()),
    defaults(context->GetGlobals())
{
}

CObject::CObject(const CObject& other) :
    Content(tobject),
    name(strcache(other.name)),
    context(other.context),
    members(other.members),
    defaults(other.defaults)
{
}

CObject::~CObject()
{
    strfree(name);
}

// void CObject::ToRequest(request* &)
// {
// }

void CObject::Print(void)
{
    std::cout << "<" << name << ">";
    members.Print();
    std::cout << "";
}

Content* CObject::Clone(void)
{
    return new CObject(*this);
}

Value CObject::super()
{
    static char* s = strcache(".super");
    return context->FindGlobal(s)->GetValue();
}

void CObject::SetContext(CObject* instance)
{
    // Restore context
    CList* c;
    members.GetValue(c);
    context->SetGlobals(c);
    context->SetObject(instance);

    // Set context for super class
    super().GetValue(c);
    for (int i = 0; i < c->Count(); i++) {
        CObject* o;
        (*c)[i].GetValue(o);
        o->SetContext(instance);
    }
}

void CObject::GetContext()
{
    CList* c;
    // Save context
    members = Value(context->GetGlobals());

    // Restort defaults
    defaults.GetValue(c);
    context->SetGlobals(c);
    context->SetObject(nullptr);

    // Set context for super class
    super().GetValue(c);
    for (int i = 0; i < c->Count(); i++) {
        CObject* o;
        (*c)[i].GetValue(o);
        o->GetContext();
    }
}

Value CObject::Method(const char* name, int arity, Value* arg)
{
    SetContext(this);

    for (int i = 0; i < arity; i++)
        context->Push(arg[i]);

    context->CallFunction(name, arity, 1);

    Value v = context->Pop();

    GetContext();

    return v;
}

void CObject::GetInheritance(Context** x, int& n)
{
    CList* c;

    x[n++] = context;

    super().GetValue(c);
    for (int i = 0; i < c->Count(); i++) {
        CObject* o;
        (*c)[i].GetValue(o);
        o->GetInheritance(x, n);
    }
}

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

class MemberCallFunction : public Function
{
public:
    MemberCallFunction(const char* n) :
        Function(n) {}
    virtual Value Execute(int arity, Value* arg);
    virtual int ValidArguments(int arity, Value* arg);
};

int MemberCallFunction::ValidArguments(int arity, Value* arg)
{
    if (arity < 2)
        return false;
    if (arg[arity - 2].GetType() != tobject)
        return false;
    if (arg[arity - 1].GetType() != tstring)
        return false;

    return true;
}

Value MemberCallFunction::Execute(int arity, Value* arg)
{
    CObject* o;
    arg[arity - 2].GetValue(o);
    const char* name;
    arg[arity - 1].GetValue(name);
    return o->Method(name, arity - 2, arg);
}

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


static void install(Context* c)
{
    c->AddFunction(new MemberCallFunction("method"));

    // Binary op
    // int i;
    // for(i=0;BinOps[i].symb;i++)
    // c->AddFunction(new CdfCdfBinOp(BinOps[i].symb,BinOps[i].proc ));

    // for(i=0;BinOps[i].symb;i++)
    //  c->AddFunction(new NumCdfBinOp(BinOps[i].symb,BinOps[i].proc ));

    // Mult op as Binary op
    // for(i=0;MulOps[i].symb;i++)
    //  c->AddFunction(new CdfCdfBinOp(MulOps[i].symb,MulOps[i].proc ));
    // for(i=0;MulOps[i].symb;i++)
    //  c->AddFunction(new NumCdfBinOp(MulOps[i].symb,MulOps[i].proc ));
}

static Linkage linkage(install);
