/*
 * © 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 "ibmblk.h"
#include <ctype.h>
#include "mars.h"

#ifdef CHECK_ALIGN
#define COPY(a, b, c) mycopy((char*)(a), (char*)(b), c)
#else
#define COPY(a, b, c) memcpy(a, b, c)
#endif

#define FILL_CHR(x, y) fill_chr(x, y, sizeof(x))
#define FILL_INT(x, y) fill_int(x, y, sizeof(x))
#define GET_CHR(x) get_chr((char*)&x, sizeof(x))
#define GET_INT(x) get_int((char*)&x, sizeof(x))

static void mycopy(char* a, char* b, int c) {
    while (c--)
        *a++ = *b++;
}


static char block[MAX_NET_BLOCK];

static void upper_case(request* r, char* name) {
    const char* s = get_value(r, name, 0);
    char *p, *q;

    if (!s)
        return;

    q = p = NEW_STRING(s);
    while (*p) {
        if (islower(*p))
            *p = toupper(*p);
        p++;
    }
    set_value(r, name, q);
    FREE(q);
}

static void fill_chr(char* p, const char* q, int n) {
    char buf[256];
    sprintf(buf, "%*s", -n, q);
    COPY(p, buf, n);
}

static void fill_int(char* p, int m, int n) {
    char buf[256];
    sprintf(buf, "%0*d", n, m);
    COPY(p, buf, n);
}

static char* get_chr(char* p, int n) {
    static char buf[256];
    COPY(buf, p, n);
    buf[n] = 0;
    return buf;
}

static int get_int(char* p, int n) {
    return atoi(get_chr(p, n));
}

static int add_1_value(value* v, int offset, int shift) {
    _bvalue bvalue;
    int len = strlen(v->name + shift);
    int off = offset + OFFSET(_bvalue, value) + len;

    memset(&bvalue, '*', sizeof(bvalue));

    strncpy(bvalue.value, v->name + shift, len);

    FILL_INT(bvalue.vlen, len);
    FILL_INT(bvalue.ptype,
             *(v->name + shift) == '"' ? STR_TYPE : (is_number(v->name + shift) ? NUM_TYPE : CHR_TYPE));

    COPY(block + offset, &bvalue, OFFSET(_bvalue, value) + len);

    return off;
}

static int add_1_parameter(parameter* p, int offset) {
    _bparam bparam;
    value* v = p->default_values ? p->default_values : p->values;
    int cnt  = 0;
    int len  = strlen(p->name);
    int off  = offset + OFFSET(_bparam, param) + len;

    memset(&bparam, '*', sizeof(bparam));

    strncpy(bparam.param, p->name, len);

    FILL_INT(bparam.plen, len);

    while (v) {
        int shift = 0;
        if (strcmp(p->name, "DATE") == 0 && strlen(v->name) == 8)
            shift = 2;

        off = add_1_value(v, off, shift);
        cnt++;
        v = v->next;
    }


    FILL_INT(bparam.nvalues, cnt);
    COPY(block + offset, &bparam, OFFSET(_bparam, param) + len);
    return off;
}

static int add_1_request(request* r, int offset) {
    _subhdr subhdr;
    parameter* p = r->params;
    int cnt      = 0;
    int off      = offset + SUB_SIZE;


    memset(&subhdr, '*', sizeof(subhdr));

    FILL_INT(subhdr.shdrlen, OFFSET(_subhdr, nparms));
    FILL_CHR(subhdr.action, r->name);
    FILL_INT(subhdr.mode, 1);
    FILL_INT(subhdr.ccode, 0);
    FILL_INT(subhdr.escode, 0);

    while (p) {
        if (*(p->name) != '_') {
            off = add_1_parameter(p, off);
            cnt++;
        }
        p = p->next;
    }


    FILL_INT(subhdr.nparms, cnt);
    FILL_INT(subhdr.blklen, off - offset);
    COPY(block + offset, &subhdr, SUB_SIZE);
    return off;
}


char* buildblock(request* r, int actcls) {
    _prmhdr prmhdr;
    int cnt = 0;
    int off = PRM_SIZE;
    char buf[10];
    const char* s;
    long t;
    request* e = (request*)get_environ();

    memset(block, 0, sizeof(block));

    memset(&prmhdr, '*', sizeof(prmhdr));

    FILL_INT(prmhdr.hdrlen, PRM_SIZE);

    s = getenv("ARCH");
    s = s ? s : "unk";
    FILL_CHR(prmhdr.userid, s);
    FILL_CHR(prmhdr.compid, s);

    s = get_value(e, "user", 0);
    FILL_CHR(prmhdr.sendid, s ? s : "???");


    /* Expever should be uppercase */

    upper_case(r, "EXPVER");
    upper_case(r, "CFSPATH");
    upper_case(r, "PASSWORD");


    s = get_value(e, "account", 0);
    FILL_CHR(prmhdr.account, s ? s : "ECFSUN");

    gethostname(buf, sizeof(buf));
    FILL_CHR(prmhdr.recid, buf);

    if ((s = getenv("QSUB_REQNAME"))) {
        strncpy(buf, s, 9);
        buf[9] = 0;
    }

    FILL_CHR(prmhdr.userjob, buf);
    FILL_INT(prmhdr.pswitch, mars.debug ? 8 : 0);
    FILL_INT(prmhdr.hdrvers, 2);

    time(&t);
    strftime(buf, 10, "%y%m%d", gmtime(&t));
    FILL_CHR(prmhdr.subdate, buf);
    FILL_INT(prmhdr.recdate, getpid());

    strftime(buf, 10, "%H%M%S", gmtime(&t));
    FILL_CHR(prmhdr.subtime, buf);
    FILL_CHR(prmhdr.rectime, buf);
    FILL_INT(prmhdr.nihdr, 0);
    FILL_INT(prmhdr.actmod, 1);
    FILL_INT(prmhdr.suberr, 0);
    FILL_INT(prmhdr.status, 1);
    FILL_INT(prmhdr.actcls, actcls);

#ifdef ALL_REQS
    while (r) {
        off = add_1_request(r, off);
        cnt++;
        r = r->next;
    }
#else

    off = add_1_request(r, off);
    cnt++;

#endif


    FILL_INT(prmhdr.noshdr, cnt);
    FILL_INT(prmhdr.msglen, off);
    COPY(block, &prmhdr, PRM_SIZE);

    if (mars.debug) {
        request* u = procces_reply(block);
        print_one_request(u);
        free_all_requests(u);
    }

    return block;
}

static request* procces_subhdr(int* offset) {
    _subhdr subhdr;
    char buf[256];
    int n;
    int i;
    int off = *offset;
    request* r1;
    parameter *p1, *p2;
    value *v1, *v2;

    COPY(&subhdr, block + (*offset), sizeof(subhdr));

    *offset += GET_INT(subhdr.shdrlen);

    strcpy(buf, GET_CHR(subhdr.action));
    i = strlen(buf);
    while (i >= 0 && (buf[i] == 0 || buf[i] == ' '))
        buf[i--] = 0;

    r1 = new_request(strcache(buf), NULL);

    n = GET_INT(subhdr.nparms);

    off += SUB_SIZE;

    for (i = 0; i < n; i++) {
        int len, j, cnt;
        _bparam bparam;

        COPY(&bparam, block + off, sizeof(bparam));

        len = GET_INT(bparam.plen);
        cnt = GET_INT(bparam.nvalues);

        strncpy(buf, bparam.param, len);
        buf[len] = 0;

        off += len + OFFSET(_bparam, param);


        p2 = new_parameter(strcache(buf), NULL);
        if (r1->params == NULL)
            r1->params = p2;
        else
            p1->next = p2;
        p1 = p2;


        for (j = 0; j < cnt; j++) {
            _bvalue bvalue;
            COPY(&bvalue, block + off, sizeof(bvalue));

            len = GET_INT(bvalue.vlen);

            strncpy(buf, bvalue.value, len);
            buf[len] = 0;

            v2 = new_value(strcache(buf));
            if (p2->values == NULL)
                p2->values = v2;
            else
                v1->next = v2;
            v1 = v2;

            off += len + OFFSET(_bvalue, value);
        }
    }
    return r1;
}

int actcls(char* block) {
    _prmhdr prmhdr;
    COPY(&prmhdr, block, sizeof(prmhdr));
    return GET_INT(prmhdr.actcls);
}

void geterrors(char* block, int req, int* e1, int* e2, int* e3) {
    _prmhdr prmhdr;
    _subhdr subhdr;
    int n, i;
    int off = PRM_SIZE;

    COPY(&prmhdr, block, sizeof(prmhdr));

    n   = GET_INT(prmhdr.noshdr);
    *e1 = GET_INT(prmhdr.suberr);

    for (i = 0; i < n; i++) {
        COPY(&subhdr, block + off, sizeof(subhdr));
        *e2 = GET_INT(subhdr.ccode);
        *e3 = GET_INT(subhdr.escode);
        off += GET_INT(subhdr.shdrlen);
        if (i == req)
            break;
    }
}

request* procces_reply(char* block) {
    _prmhdr prmhdr;
    request* r1 = NULL;
    request* r2 = NULL;
    request* r3 = NULL;
    int off     = PRM_SIZE;

    int i;
    int n;

    COPY(&prmhdr, block, sizeof(prmhdr));

    n = GET_INT(prmhdr.noshdr);

    for (i = 0; i < n; i++) {

        r3 = procces_subhdr(&off);

        if (r1 == NULL)
            r1 = r3;
        else
            r2->next = r3;
        r2 = r3;
    }
    return r1;
}
