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

/********************************************************/
/*                  Definition 190                      */
/********************************************************/
#ifdef FORTRAN_UPPERCASE
#define d_def_x_ D_DEF_X
#endif

#ifdef FORTRAN_NO_UNDERSCORE
#define d_def_x_ d_def_x
#endif

#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif

extern fortint d_def_x_(fortint* n, fortint* output, unsigned char* input);

#if defined(__cplusplus) || defined(c_plusplus)
}
#endif


static int decode_local_def(fortint number, fortint length, unsigned char* buffer, request* r, gribsec1* orig_s1) {
    int e = 0;
    griblocsec1 s1;
    gribsec1 sec1;

    CLEAR(s1);
    e = d_def_x_(&number, (fortint*)&s1, buffer);
    marslog(LOG_DBUG, "Calling decoder for definition %d, returns %d", number, e);
    if (e > length) {
        marslog(LOG_EROR, "Decoder D_DEF_%d returns %d (> %d from GRIB header)", number, e, length);
        return -7;
    }

    if (s1.ecmwf_local_id != number) {
        marslog(LOG_EROR, "Mistmatch in Multi-local definition");
        marslog(LOG_EROR, "Main GRIB header has local def. %d", number);
        marslog(LOG_EROR, "Local definition is %d", s1.ecmwf_local_id);
        return -7;
    }

    /* Fill the request with values from this local definition
       Copy the original GRIB section 1, then replace the contents
       with the just read local section 1
    */
    CLEAR(sec1);
    memcpy((unsigned char*)&sec1, (unsigned char*)orig_s1, sizeof(gribsec1));
    memcpy((unsigned char*)&(sec1.local), (unsigned char*)&s1, sizeof(s1));
    if ((e = localdefinition(&sec1, r)) != NOERR) {
        marslog(LOG_WARN, "Error %d when inspecting local definition %d", e, number);
        return e;
    }

    return e;
}

int decode_multi_local_definition_190(gribsec1* s1, request* r, char* grib) {
    int i                   = 0;
    int e                   = 0;
    int offset              = 0;
    unsigned char* startdef = (unsigned char*)&(s1->local.u.multi_definition_190.number) + sizeof(fortint) + (s1->local.u.multi_definition_190.number * sizeof(multilocaldef_190));

    for (i = 0; i < s1->local.u.multi_definition_190.number; ++i) {
        fortint defnumber = s1->local.u.multi_definition_190.def[i].number;
        fortint deflength = s1->local.u.multi_definition_190.def[i].length;

        unsigned char* localdef = (unsigned char*)startdef;
        localdef += offset;

        e = decode_local_def(defnumber, deflength, localdef, r, s1);
        if (e)
            return e;

        offset += deflength;
    }

    set_mars_class(&s1->local, r);
    set_mars_type(&s1->local, r);
    set_mars_stream(s1, &s1->local, r);
    set_mars_expver(&s1->local, r);

    return e;
}

/********************************************************/
/*                  Definition 192                      */
/********************************************************/
int decode_multi_local_definition(gribsec1* s1, request* r, char* grib) {
    int i      = 0;
    int e      = 0;
    int offset = 0;
    fortint* p = (fortint*)&(s1->local.u.multi_definition.localdef);

    marslog(LOG_DBUG, "Found %d defitions within def 192", s1->local.u.multi_definition.number);
    for (i = 0; i < s1->local.u.multi_definition.number; ++i) {
        fortint deflength = p[0];
        gribsec1 sec1;

        CLEAR(sec1);
        memcpy((unsigned char*)&sec1, (unsigned char*)s1, sizeof(gribsec1));
        memcpy((unsigned char*)&(sec1.local), (unsigned char*)&p[1], deflength * sizeof(fortint));

        marslog(LOG_DBUG, "Processing definition %d => ECMWF local definition %" D " [%" D "]", i, sec1.local.ecmwf_local_id, p[1]);

        if ((e = localdefinition(&sec1, r)) != NOERR) {
            marslog(LOG_WARN, "Error %d when inspecting local definition %" D "", e, sec1.local.ecmwf_local_id);
            return e;
        }

        p += deflength + 1;
    }

    set_mars_class(&s1->local, r);
    set_mars_type(&s1->local, r);
    set_mars_stream(s1, &s1->local, r);
    set_mars_expver(&s1->local, r);

    return e;
}
