/*
 * © Copyright 1996-2012 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include "mars.h"

static mempool varmem = {
    1, /* 1 page */
    1, /* clear */
};

#define STACK_SIZE 10
#define FASTNEW(type) (type*)fast_new(sizeof(type), &varmem)
#define FASTDEL(x) fast_delete(x, &varmem)

static variable* vars = NULL;

static int top = 0;
static variable stack[STACK_SIZE + 1];

err push_scalar(double d) {
    if (top == STACK_SIZE) {
        marslog(LOG_EROR, "Stack is full");
        return -2;
    }
    stack[top].scalar = true;
    stack[top].val    = d;
    stack[top].name   = NULL;
    top++;
    return NOERR;
}

err push_named_scalar(char* name, double d) {
    if (top == STACK_SIZE) {
        marslog(LOG_EROR, "Stack is full");
        return -2;
    }
    stack[top].scalar = true;
    stack[top].val    = d;
    stack[top].name   = name;
    top++;
    return NOERR;
}

err push_fieldset(fieldset* f) {
    if (top == STACK_SIZE) {
        marslog(LOG_EROR, "Stack is full");
        return -2;
    }
    stack[top].scalar = false;
    stack[top].fs     = f;
    stack[top].name   = NULL;
    top++;
    return NOERR;
}

err push_named_fieldset(char* name, fieldset* f) {
    if (top == STACK_SIZE) {
        marslog(LOG_EROR, "Stack is full");
        return -2;
    }
    stack[top].scalar = false;
    stack[top].fs     = f;
    stack[top].name   = name;
    top++;
    return NOERR;
}

err push(variable* v) {
    abort();
    return 0;
}

variable* pop() {
    if (top == 0) {
        marslog(LOG_EROR, "Stack is empty");
        return NULL;
    }
    return &stack[--top];
}

variable* stack_top() {
    return &stack[top - 1];
}

variable* find_variable(const char* name) {
    variable* w = vars;
    while (w) {
        if (w->name == name)
            return w;
        w = w->next;
    }
    return NULL;
}

err name_variable(variable* v, char* name) {
    abort();
    return 0;
}

static void free_variables(int code, void* data) {
    while (vars)
        free_variable(vars);
}

variable* new_variable(const char* name, fieldset* f, double d) {
    variable* w = find_variable(name);
    variable* v = FASTNEW(variable);

    if (vars == 0)
        install_exit_proc(free_variables, 0);

    v->name = name ? strcache(name) : NULL;

    if (f) {
        v->fs     = f;
        v->scalar = false;
        f->refcnt++;
        if (save_fieldset(f)) {
            FASTDEL(v);
            return NULL;
        }
    }
    else {
        v->scalar = true;
        v->fs     = NULL;
        v->val    = d;
    }

    if (w)
        free_variable(w);

    v->next = vars;
    vars    = v;
    return v;
}

void free_variable(variable* v) {
    variable* w = vars;
    variable* z = NULL;
    while (w) {
        variable* n = w->next;

        if (w == v) {
            if (z)
                z->next = v->next;
            else
                vars = v->next;
            if (v->name)
                strfree(v->name);
            if (v->fs)
                free_fieldset(v->fs);
            FASTDEL(v);
        }
        z = w;
        w = n;
    }
}
