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

#include "macro.h"

class ShellFunction : public Function
{
public:
    ShellFunction(const char* n) :
        Function(n)  //,1,tstring)
    {
        info = "Execute a shell command";
    }

    virtual Value Execute(int arity, Value* arg);
};

Value ShellFunction::Execute(int arity, Value* arg)
{
    int ret_code;
    const char* p;
    char buf[2048], shell_cmd[600];
    buf[0] = shell_cmd[0] = 0;

    for (int i = 0; i < arity; i++) {
        arg[i].GetValue(p);
        strcat(shell_cmd, p);
    }

    strcat(shell_cmd, " 2>&1");

    FILE* f = popen(shell_cmd, "r");
    if (!f)
        return Error("Cannot execute shell command: %s", shell_cmd);

    while (fgets(buf, sizeof(buf), f))
        std::cout << buf;

    std::cout << std::flush;

    if ((ret_code = pclose(f)) != 0) {
        marslog(LOG_INFO, "Warning : Shell command '%s' returned %d",
                shell_cmd, ret_code);
    }

    return Value(ret_code);
}


class GetenvFunction : public Function
{
public:
    GetenvFunction(const char* n) :
        Function(n, 1, tstring)
    {
        info = "Return the value of a shell variable";
    }

    virtual Value Execute(int arity, Value* arg);
};

Value GetenvFunction::Execute(int, Value* arg)
{
    const char* p;
    arg[0].GetValue(p);
    p = getenv(p);
    p = p ? p : "";

    if (isdate(p))
        return Value(Date(p));
    else if (is_number(p))
        return Value(atof(p));
    else
        return Value(p);
}


class Getenv2Function : public Function
{
public:
    Getenv2Function(const char* n) :
        Function(n, 2, tstring, tnumber)
    {
        info = "Return the value of a shell variable";
    }

    virtual Value Execute(int arity, Value* arg);
};

Value Getenv2Function::Execute(int, Value* arg)
{
    const char* p;
    arg[0].GetValue(p);
    p = getenv(p);
    p = p ? p : "";

    int convert;
    arg[1].GetValue(convert);

    if (!convert)
        return Value(p);


    if (isdate(p))
        return Value(Date(p));
    else if (is_number(p))
        return Value(atof(p));
    else
        return Value(p);
}


class PutenvFunction : public Function
{
public:
    PutenvFunction(const char* n) :
        Function(n, 2, tstring, tstring)
    {
        info = "Sets the value of an environment variable.";
    }
    virtual Value Execute(int arity, Value* arg);
};

Value PutenvFunction::Execute(int, Value* arg)
{
    const char *p = nullptr, *q = nullptr;

    arg[0].GetValue(p);
    arg[1].GetValue(q);
    if (!p && !q)
        return Value((char*)nullptr);

    char* buf = new char[strlen(p) + strlen(q) + 2];  // memory leak!!!
    sprintf(buf, "%s=%s", p, q);
    putenv(buf);
    return Value(buf);
}


class SleepFunction : public Function
{
public:
    SleepFunction(const char* n) :
        Function(n, 1, tnumber)
    {
        info = "Sleep (number of seconds)";
    }

    virtual Value Execute(int arity, Value* arg);
};

Value SleepFunction::Execute(int, Value* arg)
{
    double d;
    arg[0].GetValue(d);
    return Value(sleep((unsigned int)d));
}


class NiceFunction : public Function
{
public:
    NiceFunction(const char* n) :
        Function(n, 1, tnumber)
    {
        info = "Nice (nice value)";
    }

    virtual Value Execute(int arity, Value* arg);
};

Value NiceFunction::Execute(int, Value* arg)
{
    int d;
    arg[0].GetValue(d);
    return Value(nice(d));
}


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

Value PauseFunction::Execute(int, Value*)
{
    return Value(pause());
}


class TempFileFunction : public Function
{
public:
    TempFileFunction(const char* n) :
        Function(n, 0)
    {
        info = "Return a unique filename for a temporary file.";
    }

    virtual Value Execute(int arity, Value* arg);
};

Value TempFileFunction::Execute(int, Value*)
{
    char nam[1024];  //-- std::string cannot be used: mkstemp requires char array

    strcpy(nam, getenv("METVIEW_TMPDIR"));
    strcat(nam, "/macrotmp_XXXXXX");

    int ret = mkstemp(nam);  //-- filename created + file opened

    if (ret != -1)
        close(ret);  //-- success, close created file
    else
        marslog(LOG_EROR, "tmpfile: unable to create a unique name!");

    return Value(nam);
}


static void install(Context* c)
{
    c->AddFunction(new ShellFunction("shell"));
    c->AddFunction(new SleepFunction("sleep"));
    c->AddFunction(new NiceFunction("nice"));
    c->AddFunction(new PauseFunction("pause"));
    c->AddFunction(new GetenvFunction("getenv"));
    c->AddFunction(new Getenv2Function("getenv"));
    c->AddFunction(new PutenvFunction("putenv"));
    c->AddFunction(new TempFileFunction("tmpfile"));
}

static Linkage linkage(install);
