#include <stdio.h>
#include <string.h>
#include "cmor.h"
#include <udunits2.h>
#include <stdlib.h>
#include "cdmsint.h"
#include <math.h>

int cuErrOpts = CU_VERBOSE;

/************************************************************************/
/*                         cmor_calendar_c2i()                          */
/************************************************************************/
int cmor_calendar_c2i(char *calendar, cdCalenType * ical)
{

    cmor_add_traceback("cmor_calendar_c2i");

    cmor_is_setup();

    if (strcmp(calendar, "gregorian") == 0)
        *ical = cdMixed;
    else if (strcmp(calendar, "standard") == 0)
        *ical = cdMixed;
    else if (strcmp(calendar, "proleptic_gregorian") == 0)
        *ical = cdStandard;
    else if (strcmp(calendar, "noleap") == 0)
        *ical = cdNoLeap;
    else if (strcmp(calendar, "365_day") == 0)
        *ical = cdNoLeap;
    else if (strcmp(calendar, "360_day") == 0)
        *ical = cd360;
    else if (strcmp(calendar, "julian") == 0)
        *ical = cdJulian;
    else if (strcmp(calendar, "none") == 0)
        *ical = cdClim;
    else {
        cmor_pop_traceback();
        return (1);
    }
    cmor_pop_traceback();
    return (0);
}

/************************************************************************/
/*                  cmor_convert_interval_to_seconds()                  */
/************************************************************************/
double cmor_convert_interval_to_seconds(double interv, char *inunits)
{

/* -------------------------------------------------------------------- */
/*      this converts times values from some units                      */
/*      to some others                                                  */
/* -------------------------------------------------------------------- */
    int i, oui, n;
    char msg[CMOR_MAX_STRING];
    char ctmp[CMOR_MAX_STRING];
    char sshort[6];
    extern ut_system *ut_read;
    ut_unit *user_units = NULL, *cmor_units = NULL;
    cv_converter *ut_cmor_converter = NULL;
    double tmp;

    cmor_add_traceback("cmor_convert_interval_to_seconds");

    strcpy(msg, "seconds");

    ut_trim(msg, UT_ASCII);
    cmor_units = ut_parse(ut_read, msg, UT_ASCII);

    sshort[5] = '\0';

/* -------------------------------------------------------------------- */
/*      first we need to figure out the out units                       */
/*      step 1 look for the since keyword                               */
/* -------------------------------------------------------------------- */
    n = strlen(inunits);
    oui = -1;
    for (i = 0; i < n; i++) {
        strncpy(sshort, &inunits[i], 5);
        if (strcmp(sshort, "since") == 0) {
            oui = i;
            break;
        }
    }
    if (oui == -1) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Time units conversion, output units must\n! "
                 "contain the 'since' word, you defined: %s", inunits);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }

    strncpy(msg, inunits, oui - 1);
    msg[oui - 1] = '\0';

/* -------------------------------------------------------------------- */
/*      ok at this point we need to convert                             */
/*      this in some base units: seconds                                */
/* -------------------------------------------------------------------- */
    ut_trim(msg, UT_ASCII);

    user_units = ut_parse(ut_read, msg, UT_ASCII);
    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING,
                 "In udunits parsing user units: %s", msg);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }
    if (ut_are_convertible(cmor_units, user_units) == 0) {
        snprintf(ctmp, CMOR_MAX_STRING,
                 "axis interval units (%s) are incompatible with seconds", msg);
        ut_free(user_units);
        ut_free(cmor_units);
        cmor_handle_error(ctmp, CMOR_CRITICAL);
        cmor_pop_traceback();
        return (1);
    }
    ut_cmor_converter = ut_get_converter(user_units, cmor_units);
    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING, "In udunits getting converter");
        cmor_handle_error(msg, CMOR_CRITICAL);
    }
    tmp = cv_convert_double(ut_cmor_converter, interv);
    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING, "In udunits converting");
        cmor_handle_error(msg, CMOR_CRITICAL);
    }
    cv_free(ut_cmor_converter);
    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING, "Udunits: Error freeing converter");
        cmor_handle_error(msg, CMOR_CRITICAL);
    }
    ut_free(user_units);
    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING, "Udunits: Error freeing units");
        cmor_handle_error(msg, CMOR_CRITICAL);
    }
    ut_free(cmor_units);
    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING, "Udunits: Error freeing units");
        cmor_handle_error(msg, CMOR_CRITICAL);
    }

    cmor_pop_traceback();
    return (tmp);
}

/************************************************************************/
/*                      cmor_convert_time_units()                       */
/************************************************************************/
int cmor_convert_time_units(char *inunits, char *outunits, char *loutunits)
{
/* -------------------------------------------------------------------- */
/*      this converts times values from some units to some others       */
/* -------------------------------------------------------------------- */
    int i, oui, iui, n;
    char msg[CMOR_MAX_STRING];
    char sshort[6];
    int hasqm;

    cmor_add_traceback("cmor_convert_time_units");
    cmor_is_setup();

    sshort[5] = '\0';
    /* first we need to figure out the out units */
    /* step 1 look for the since keyword */
    n = strlen(outunits);
    oui = -1;
    for (i = 0; i < n; i++) {
        strncpy(sshort, &outunits[i], 5);
        if (strcmp(sshort, "since") == 0) {
            oui = i;
            break;
        }
    }
    if (oui == -1) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Time units conversion, output units must contain\n! "
                 "the 'since' word");
        cmor_handle_error(msg, CMOR_CRITICAL);
    }

    /* ok now check if output units have a "?" */
    hasqm = -1;
    for (i = oui + 5; i < n; i++)
        if (outunits[i] == '?') {
            hasqm = i;
            break;
        }

    /* here we check for the since in user units */
    n = strlen(inunits);
    iui = -1;
    for (i = 0; i < n; i++) {
        strncpy(sshort, &inunits[i], 5);
        if (strcmp(sshort, "since") == 0) {
            iui = i;
            break;
        }
    }
    if (iui == -1) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Time units conversion, input units must contain the\n! "
                 "'since' word");
        cmor_handle_error(msg, CMOR_CRITICAL);
    }
    /* now construct the output units in case of a ? */
    if (hasqm != -1) {          /* there is a ? */
        strncpy(loutunits, outunits, oui);
        loutunits[oui] = '\0';
        strcpy(msg, &inunits[iui]);
        strncat(loutunits, msg, CMOR_MAX_STRING - strlen(loutunits));
    } else {                    /* fully defined output units */
        strncpy(loutunits, outunits, CMOR_MAX_STRING);
    }
    cmor_pop_traceback();
    return (0);
}

/************************************************************************/
/*                      cmor_convert_time_values()                      */
/************************************************************************/
int cmor_convert_time_values(void *values_in, char type, int nvalues,
                             double *values_out, char *inunits,
                             char *outunits, char *calin, char *calout)
{
/* -------------------------------------------------------------------- */
/*      this converts times values from some units to some others       */
/* -------------------------------------------------------------------- */
    int i;
    char msg[CMOR_MAX_STRING];
    char loutunits[CMOR_MAX_STRING];
    double dtmp;
    cdCalenType icali, icalo;
    cdCompTime comptime;

    cmor_add_traceback("cmor_convert_time_values");
    cmor_is_setup();

    if (cmor_calendar_c2i(calin, &icali) != 0) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Cannot convert times for calendar: %s", calin);
        cmor_handle_error(msg, CMOR_NORMAL);
        cmor_pop_traceback();
        return (1);
    }
    if (cmor_calendar_c2i(calout, &icalo) != 0) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Cannot convert times for calendar: %s", calout);
        cmor_handle_error(msg, CMOR_NORMAL);
        cmor_pop_traceback();
        return (1);
    }

    i = cmor_convert_time_units(inunits, outunits, &loutunits[0]);
    for (i = 0; i < nvalues; i++) {
        if (type == 'd')
            dtmp = (double)((double *)values_in)[i];
        else if (type == 'f')
            dtmp = (double)((float *)values_in)[i];
        else if (type == 'l')
            dtmp = (double)((long *)values_in)[i];
        else if (type == 'i')
            dtmp = (double)((int *)values_in)[i];
        else {
            snprintf(msg, CMOR_MAX_STRING,
                     "cannot convert time value from '%c' type", type);
            cmor_handle_error(msg, CMOR_CRITICAL);
        }
/* -------------------------------------------------------------------- */
/*      ok makes a comptime out of input                                */
/* -------------------------------------------------------------------- */
        cdRel2Comp(icali, &inunits[0], dtmp, &comptime);

/* -------------------------------------------------------------------- */
/*      ok now converts that back to a rel units with outunits          */
/* -------------------------------------------------------------------- */
        cdComp2Rel(icalo, comptime, loutunits, &dtmp);
        values_out[i] = dtmp;
    }
    cmor_pop_traceback();
    return (0);
}

/************************************************************************/
/*                      cmor_set_axis_attribute()                       */
/************************************************************************/
int cmor_set_axis_attribute(int id, char *attribute_name, char type,
                            void *value)
{
    extern cmor_axis_t cmor_axes[];
    char msg[CMOR_MAX_STRING];
    int i, index;

    cmor_add_traceback("cmor_set_axis_attribute");
    cmor_is_setup();

    index = -1;
    cmor_trim_string(attribute_name, msg);

    for (i = 0; i < cmor_axes[id].nattributes; i++) {
        if (strcmp(cmor_axes[id].attributes[i], msg) == 0) {
            index = i;
            break;
        }                       /* we found it */
    }

    if (index == -1) {
        index = cmor_axes[id].nattributes;
        cmor_axes[id].nattributes += 1;
    }
/* --------------------------------------------------------------------- */
/*      store the name                                                   */
/* --------------------------------------------------------------------- */

    strncpy(cmor_axes[id].attributes[index], msg, CMOR_MAX_STRING);

    cmor_axes[id].attributes_type[index] = type;
    if (type == 'c') {
        if (strlen(value) > 0)
            strncpytrim(cmor_axes[id].attributes_values_char[index],
                        value, CMOR_MAX_STRING);
    } else if (type == 'f')
        cmor_axes[id].attributes_values_num[index] = (double)*(float *)value;
    else if (type == 'i')
        cmor_axes[id].attributes_values_num[index] = (double)*(int *)value;
    else if (type == 'd')
        cmor_axes[id].attributes_values_num[index] = (double)*(double *)value;
    else if (type == 'l')
        cmor_axes[id].attributes_values_num[index] = (double)*(long *)value;
    else {
        snprintf(msg, CMOR_MAX_STRING,
                 "unknown type %c allowed types are c,i,l,f,d, for "
                 "attribute %s of axis %s (table: %s)",
                 type,
                 attribute_name,
                 cmor_axes[id].id,
                 cmor_tables[cmor_axes[id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_NORMAL);
        cmor_pop_traceback();
        return (1);
    }
    cmor_pop_traceback();
    return (0);
}

/************************************************************************/
/*                      cmor_get_axis_attribute()                       */
/************************************************************************/
int cmor_get_axis_attribute(int id, char *attribute_name, char type,
                            void *value)
{
    extern cmor_axis_t cmor_axes[];
    char msg[CMOR_MAX_STRING];
    int i, index;

    cmor_add_traceback("cmor_get_axis_attribute");
    cmor_is_setup();
    index = -1;
    for (i = 0; i < cmor_axes[id].nattributes; i++) {
        if (strcmp(cmor_axes[id].attributes[i], attribute_name) == 0) {
            index = i;
            break;
        }                       /* we found it */
    }
    if (index == -1) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Attribute %s could not be found for axis %i (%s, table: %s)",
                 attribute_name, id, cmor_axes[id].id,
                 cmor_tables[cmor_axes[id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_NORMAL);
        cmor_pop_traceback();
        return (1);
    }
    type = cmor_axes[id].attributes_type[i];
    if (type == 'c')
        strcpy(value, cmor_axes[id].attributes_values_char[index]);
    else if (type == 'f')
        value = (float *)&cmor_axes[id].attributes_values_num[index];
    else if (type == 'i')
        value = (int *)&cmor_axes[id].attributes_values_num[index];
    else if (type == 'd')
        value = (double *)&cmor_axes[id].attributes_values_num[index];
    else if (type == 'l')
        value = (long *)&cmor_axes[id].attributes_values_num[index];
    cmor_pop_traceback();
    return (0);
}

/************************************************************************/
/*                      cmor_has_axis_attribute()                       */
/************************************************************************/
int cmor_has_axis_attribute(int id, char *attribute_name)
{
    extern cmor_axis_t cmor_axes[];
    int i, index;

    cmor_add_traceback("cmor_has_axis_attribute");
    cmor_is_setup();
    index = -1;
    for (i = 0; i < cmor_axes[id].nattributes; i++) {
        if (strcmp(cmor_axes[id].attributes[i], attribute_name) == 0) {
            index = i;
            break;              /* we found it */
        }
    }
    if (index == -1) {
        cmor_pop_traceback();
        return (1);
    }
    cmor_pop_traceback();
    return (0);
}

/************************************************************************/
/*                  cmor_check_values_inside_bounds()                   */
/************************************************************************/
int cmor_check_values_inside_bounds(double *values, double *bounds, int length,
                                    char *name)
{
    int i;
    char msg[CMOR_MAX_STRING];

    cmor_add_traceback("cmor_check_values_inside_bounds");
    for (i = 0; i < length; i++) {

        if (((bounds[2 * i] < values[i]) && (bounds[2 * i + 1] < values[i]))
            || ((bounds[2 * i] > values[i])
                && (bounds[2 * i + 1] > values[i]))) {
            snprintf(msg, CMOR_MAX_STRING,
                     "axis %s has values not within bounds at indice:\n! "
                     "%i: %lf not within: %lf, %lf", name, i, values[i],
                     bounds[2 * i], bounds[2 * i + 1]);
            cmor_handle_error(msg, CMOR_CRITICAL);
        }
    }
    cmor_pop_traceback();
    return (0);
}

/************************************************************************/
/*                          cmor_isLongitude()                          */
/************************************************************************/
int cmor_isLongitude(cmor_axis_def_t * refaxis)
{
    if ((refaxis->axis == 'X') && (strncmp(refaxis->units, "degree", 6) == 0)
        && (strcmp(refaxis->units, "degrees") != 0))
        return (1);
    else
        return (0);
}

/************************************************************************/
/*                        cmor_check_monotonic()                        */
/************************************************************************/
int cmor_check_monotonic(double *values, int length, char *name, int isbounds,
                         int axis_id)
{
    int i, treatlon = 0, j = 0;
    char msg[CMOR_MAX_STRING];
    cmor_axis_def_t *refaxis;
    int mono;
    int nloop;
    double *values2, tmp;

    cmor_add_traceback("cmor_check_monotonic");

    refaxis =
      &cmor_tables[cmor_axes[axis_id].ref_table_id].
      axes[cmor_axes[axis_id].ref_axis_id];
    treatlon = cmor_isLongitude(refaxis);
/* -------------------------------------------------------------------- */
/*  ensure that values are monotonic                                    */
/* -------------------------------------------------------------------- */
    if (isbounds == 1) {
        for (i = 0; i < length / 2 - 2; i++) {
            if (((values[2 * i] - values[2 * i + 2])
                 * (values[2 * i + 2] - values[2 * i + 4])) <= 0.) {
                if (cmor_isLongitude(refaxis) == 1) {
                    treatlon = 1;
                } else {
                    snprintf(msg, CMOR_MAX_STRING,
                             "axis %s (table: %s) has non monotonic\n! "
                             "bounds values : %lf, %lf, %lf", name,
                             cmor_tables[cmor_axes[axis_id].
                                         ref_table_id].szTable_id,
                             values[2 * i], values[2 * i + 2],
                             values[2 * i + 4]);
                    if (refaxis->climatology == 0) {
                        cmor_handle_error(msg, CMOR_CRITICAL);
                    } else {
                        cmor_handle_error(msg, CMOR_WARNING);
                    }
                }
            }
        }

/* -------------------------------------------------------------------- */
/*      ok we are dealing with a longitude                              */
/*      need to figure out the offset....                               */
/* -------------------------------------------------------------------- */
        if ((refaxis->valid_max != 1.e20) && (treatlon)) {
/* -------------------------------------------------------------------- */
/*      The VERY first thing is to make sure we are modulo 360          */
/* -------------------------------------------------------------------- */
            values2 = (double *)malloc(sizeof(double) * length);
            for (i = 0; i < length; i++) {
                values2[i] = fmod(values[i], 360.);
            }
/* -------------------------------------------------------------------- */
/*      Now keep looping until we do not have up and downs              */
/* -------------------------------------------------------------------- */
            mono = -1;
            nloop = 0;
            while (mono == -1) {
                mono = 1;
                tmp = 0;
                for (i = 0; i < length - 4; i++) {
                    tmp = (values2[i] - values2[i + 2])
                      * (values2[i + 2] - values2[i + 4]);
                    if (tmp <= 0)
                        break;
                }
                if (tmp < 0) {  /* ok we flip floppped */
                    tmp = values2[0];
                    for (i = 0; i < length - 1; i++) {
                        values2[i] = values2[i + 1];
                    }
                    values2[i] = tmp;
                    mono = -1;
                    nloop += 1;
                    if (nloop == length) {
                        sprintf(msg,
                                "longitude axis bounds are not monotonic,\n! "
                                "axis %s (table: %s)",
                                cmor_axes[axis_id].id,
                                cmor_tables[cmor_axes[axis_id].
                                            ref_table_id].szTable_id);
                        cmor_handle_error(msg, CMOR_CRITICAL);
                    }
                }
            }
            if (length > 2) {
                if (values2[0] < values2[2]) {

/* -------------------------------------------------------------------- */
/*      First keep adding 360 until each value is                       */
/*      greater than the previous                                       */
/* -------------------------------------------------------------------- */
                    for (i = 0; i < length - 2; i++) {
                        while (values[i + 2] < values[i]) {
                            values[i + 2] += 360.;
                        }
                    }
                } else {

/* -------------------------------------------------------------------- */
/*      first keep adding 360 until each value is                       */
/*      greater than the previous                                       */
/* -------------------------------------------------------------------- */
                    for (i = 0; i < length - 2; i++) {
                        while (values[i + 2] > values[i]) {
                            values[i + 2] -= 360.;
                        }
                    }
                }
            }
            free(values2);

/* -------------------------------------------------------------------- */
/*      stored_direction                                                */
/* -------------------------------------------------------------------- */
            if ((length > 1)
                && (((refaxis->stored_direction == 'i')
                     && (values[length - 1] < values[0]))
                    || ((refaxis->stored_direction == 'd')
                        && (values[0] < values[length - 1])))) {
/* -------------------------------------------------------------------- */
/*      need to flip that axis                                          */
/* -------------------------------------------------------------------- */
                if (cmor_axes[axis_id].revert == 1) {
                    snprintf(msg,
                             CMOR_MAX_STRING,
                             "bounds of axis %s (table: %s) need to be\n! "
                             "flipped but axis values did not need to.\n! "
                             "This is inconsistent", name,
                             cmor_tables[cmor_axes[axis_id].
                                         ref_table_id].szTable_id);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }

                for (i = 0; i < length / 2; i++) {
                    tmp = values[i];
                    values[i] = values[length - 1 - i];
                    values[length - 1 - i] = tmp;
                }
            }

/* -------------------------------------------------------------------- */
/*      ok make sure we have data spanning only 1 modulo                */
/* -------------------------------------------------------------------- */
            if (fabs(values[length - 1] - values[0]) > 360.) {
                snprintf(msg, CMOR_MAX_STRING,
                         "axis %s has bounds values spanning\n! "
                         "more 360 degrees %lf, %lf", name, values[0],
                         values[length - 1]);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }
/* -------------------------------------------------------------------- */
/*      ok now check the monotonic again                                */
/* -------------------------------------------------------------------- */
            for (i = 0; i < length / 2 - 2; i++) {
                if (((values[2 * i] - values[2 * i + 2])
                     * (values[2 * i + 2] - values[2 * i + 4])) <= 0.) {
                    snprintf(msg, CMOR_MAX_STRING,
                             "axis %s (table: %s), has really non monotonic\n! "
                             "bounds values : %lf, %lf, %lf", name,
                             cmor_tables[cmor_axes[axis_id].
                                         ref_table_id].szTable_id, values[i],
                             values[i + 2], values[i + 4]);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }
            }
            /* First of all need to check if bounds needs to be flipped */
            j = 1;
            for (i = 0; i < length - 2; i += 2) {
                if ((values[i] < values[i + 1])
                    && (values[i] > values[i + 2])) {
                    sprintf(msg,
                            "Axis: '%s' (table: %s), your bounds direction seems\n! "
                            "to be decreasing, but within cell %i they are stored\n! "
                            "increasingly: you have [%lf, %lf], but the next set\n! "
                            "is: [%lf, %lf]", name,
                            cmor_tables[cmor_axes[axis_id].
                                        ref_table_id].szTable_id, i, values[i],
                            values[i + 1], values[i + 2], values[i + 3]);
                    cmor_handle_error(msg, CMOR_WARNING);
                    j++;
                }
                if ((values[i] > values[i + 1])
                    && (values[i] < values[i + 2])) {
                    sprintf(msg,
                            "Axis: '%s' (table: %s), your bounds direction seems\n! "
                            "to be increasing, but within cell %i they are stored\n! "
                            "decreasingly: you have [%lf, %lf], but the next set\n! "
                            "is: [%lf, %lf]", name,
                            cmor_tables[cmor_axes[axis_id].
                                        ref_table_id].szTable_id, i, values[i],
                            values[i + 1], values[i + 2], values[i + 3]);
                    cmor_handle_error(msg, CMOR_WARNING);
                    j++;
                }
            }
            if (j == length / 2) {
                for (i = 0; i < length; i += 2) {
                    tmp = values[i];
                    values[i] = values[i + 1];
                    values[i + 1] = tmp;
                }
            } else if (j != 1) {
                sprintf(msg,
                        "Some but not all of your longitude bounds need to be\n! "
                        "flipped, see warnings ot see which ones, axis: %s \n! "
                        "(table: %s)", cmor_axes[axis_id].id,
                        cmor_tables[cmor_axes[axis_id].
                                    ref_table_id].szTable_id);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }
        }

/* -------------------------------------------------------------------- */
/*      do not do the following in case of climatological stuff....     */
/* -------------------------------------------------------------------- */
        if (refaxis->climatology == 0) {
            for (i = 0; i < length - 2; i++) {
                //printf("i=%d: value[i]%f, [i+1]%f [i+2]%f\n",i,
                //        values[i],values[i+1],values[i+2]);
                //printf("bool:%d",( values[i] < values[i + 1] ) &&
                //      ( values[i + 2] < values[i + 1] ));
                //printf("bool:%d",(( values[i] > values[i + 1] ) &&
                //        ( values[i + 2] > values[i + 1] )));

/* -------------------------------------------------------------------- */
/*      also check that bounds do not overlap                           */
/* -------------------------------------------------------------------- */
                if (((values[i] < values[i + 1])
                     && (values[i + 2] < values[i + 1]))
                    || ((values[i] > values[i + 1])
                        && (values[i + 2] > values[i + 1]))) {
                    snprintf(msg, CMOR_MAX_STRING,
                             "axis %s (table: %s) has overlapping bounds values:\n! "
                             "%lf, %lf, %lf at index: %i", name,
                             cmor_tables[cmor_axes[axis_id].
                                         ref_table_id].szTable_id, values[i],
                             values[i + 1], values[i + 2], i);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }
            }
            for (i = 0; i < length - 2; i = i + 2) {
                //printf("i=%d: [i+1]%f [i+2]%f\n",i,values[i+1],values[i+2]);
                if (values[i + 1] != values[i + 2]) {
                    snprintf(msg, CMOR_MAX_STRING,
                             "axis %s (table: %s) has bounds values that leave gaps\n! "
                             "(index %i): %lf, %lf, %lf", name,
                             cmor_tables[cmor_axes[axis_id].
                                         ref_table_id].szTable_id, i, values[i],
                             values[i + 1], values[i + 2]);
                    cmor_handle_error(msg, CMOR_WARNING);
                }
            }
        }
    } else {
        for (i = 0; i < length - 2; i++) {
            if (((values[i] - values[i + 1]) * (values[i + 1] - values[i + 2]))
                <= 0.) {
                if (cmor_isLongitude(refaxis) == 1) {
                    treatlon = 1;
                    break;
                } else {
                    snprintf(msg, CMOR_MAX_STRING,
                             "axis %s (table: %s) has non monotonic values:\n! "
                             "%lf, %lf and  %lf", name,
                             cmor_tables[cmor_axes[axis_id].
                                         ref_table_id].szTable_id, values[i],
                             values[i + 1], values[i + 2]);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }
            }
        }
/* -------------------------------------------------------------------- */
/*       ok we are dealing with a longitude                             */
/*       need to figure out the offset....                              */
/* -------------------------------------------------------------------- */
        if ((refaxis->valid_max != 1.e20) && (treatlon)) {

/* -------------------------------------------------------------------- */
/*       The VERY first thing is to make sure we are modulo 360         */
/* -------------------------------------------------------------------- */
            values2 = (double *)malloc(sizeof(double) * length);
            for (i = 0; i < length; i++) {
                values2[i] = fmod(values[i], 360.);
            }

/* -------------------------------------------------------------------- */
/*      Now keep looping until we do not have up and downs              */
/* -------------------------------------------------------------------- */
            mono = -1;
            nloop = 0;
            while (mono == -1) {
                mono = 1;
                tmp = 0;
                for (i = 0; i < length - 2; i++) {
                    tmp = (values2[i] - values2[i + 1])
                      * (values2[i + 1] - values2[i + 2]);
                    if (tmp <= 0)
                        break;
                }
/* -------------------------------------------------------------------- */
/*      ok we flip floppped                                             */
/* -------------------------------------------------------------------- */
                if (tmp < 0) {
                    tmp = values2[0];
                    for (i = 0; i < length - 1; i++) {
                        values2[i] = values2[i + 1];
                    }
                    values2[i] = tmp;
                    mono = -1;
                    nloop += 1;
                    if (nloop == length) {
                        sprintf(msg,
                                "longitude axis is not monotonic (axis: %s, "
                                "table: %s)", cmor_axes[axis_id].id,
                                cmor_tables[cmor_axes[axis_id].
                                            ref_table_id].szTable_id);
                        cmor_handle_error(msg, CMOR_CRITICAL);
                    }
                }
            }

            if (length > 1) {
                if (values2[0] < values2[1]) {
/* -------------------------------------------------------------------- */
/*      first keep adding 360 until each value                          */
/*      is greater than the previous                                    */
/* -------------------------------------------------------------------- */
                    for (i = 0; i < length - 1; i++) {
                        while (values[i + 1] < values[i]) {
                            values[i + 1] += 360.;
                        }
                    }
                } else {
/* -------------------------------------------------------------------- */
/*      first keep removing 360 until each value                        */
/*      is lower than the previous                                      */
/* -------------------------------------------------------------------- */
                    for (i = 0; i < length - 1; i++) {
                        while (values[i + 1] > values[i]) {
                            values[i + 1] -= 360.;
                        }
                    }
                }
            }
            free(values2);

/* -------------------------------------------------------------------- */
/*      stored_direction                                                */
/* -------------------------------------------------------------------- */

            if ((length > 1)
                && (((refaxis->stored_direction == 'i')
                     && (values[length - 1] < values[0]))
                    || ((refaxis->stored_direction == 'd')
                        && (values[0] < values[length - 1])))) {
/* -------------------------------------------------------------------- */
/*      need to flip that axis                                          */
/* -------------------------------------------------------------------- */
                if ((isbounds == 1) && (cmor_axes[axis_id].revert == 1)) {
                    snprintf(msg, CMOR_MAX_STRING,
                             "bounds of axis %s (table: %s), need to be\n! "
                             "flipped but axis values did not need to.\n! "
                             "This is inconsistent", name,
                             cmor_tables[cmor_axes[axis_id].
                                         ref_table_id].szTable_id);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }
                cmor_axes[axis_id].revert = -1;
                for (i = 0; i < length / 2; i++) {
                    tmp = values[i];
                    values[i] = values[length - 1 - i];
                    values[length - 1 - i] = tmp;
                }
            }

/* -------------------------------------------------------------------- */
/*      ok make sure we have data spanning only 1 modulo                */
/* -------------------------------------------------------------------- */
            if (fabs(values[length - 1] - values[0]) > 360.) {
                snprintf(msg, CMOR_MAX_STRING,
                         "axis %s (table: %s) has values spanning more\n! "
                         "360 degrees %lf, %lf", name,
                         cmor_tables[cmor_axes[axis_id].
                                     ref_table_id].szTable_id, values[0],
                         values[length - 1]);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }

/* -------------------------------------------------------------------- */
/*       ok now check the monotonic again                               */
/* -------------------------------------------------------------------- */
            for (i = 0; i < length - 2; i++) {
                if (((values[i] - values[i + 1])
                     * (values[i + 1] - values[i + 2])) <= 0.) {
                    snprintf(msg, CMOR_MAX_STRING,
                             "axis %s (table: %s) has non monotonic values : %lf, %lf and  %lf",
                             name,
                             cmor_tables[cmor_axes[axis_id].
                                         ref_table_id].szTable_id, values[i],
                             values[i + 1], values[i + 2]);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }
            }
        }
    }
    cmor_pop_traceback();
    return (treatlon);
}

/************************************************************************/
/*                       cmor_treat_axis_values()                       */
/************************************************************************/
int cmor_treat_axis_values(int axis_id, double *values, int length,
                           int n_requested, char *units, char *name,
                           int isbounds)
{
    extern ut_system *ut_read;
    ut_unit *user_units, *cmor_units;
    cv_converter *ut_cmor_converter;
    char local_unit[CMOR_MAX_STRING];
    int i, j, treatlon = 0;
    double tmp;
    char msg[CMOR_MAX_STRING];
    cdCalenType acal;
    double *tmplon;

    cmor_axis_def_t *refaxis;
    cmor_axis_t *axis;
    int found = 0;
    double eps, eps2;

    cmor_add_traceback("cmor_treat_axis_values");
    cmor_is_setup();
    axis = &cmor_axes[axis_id];
    refaxis = &cmor_tables[axis->ref_table_id].axes[axis->ref_axis_id];

    if ((refaxis->axis == 'T') || strstr(units, "since")) {

/* -------------------------------------------------------------------- */
/*      ok this part will try to convert time values to the right units */
/* -------------------------------------------------------------------- */
        cmor_get_cur_dataset_attribute("calendar", &msg[0]);
        if (cmor_calendar_c2i(msg, &acal) != 0) {
            snprintf(msg, CMOR_MAX_STRING,
                     "non-standard calendar... hum we will try to accommodate\n! "
                     "this later");
            cmor_handle_error(msg, CMOR_NORMAL);
            cmor_pop_traceback();
            return (1);
        }

        cmor_convert_time_values(&values[0], 'd', length, &values[0],
                                 units, refaxis->units, msg, msg);

    } else {
/* -------------------------------------------------------------------- */
/*      ok using udunits to convert                                     */
/* -------------------------------------------------------------------- */
        strcpy(local_unit, units);
        ut_trim(local_unit, UT_ASCII);
        user_units = ut_parse(ut_read, local_unit, UT_ASCII);

        if (ut_get_status() != UT_SUCCESS) {
            snprintf(msg, CMOR_MAX_STRING,
                     "In udunits analyzing units from user (%s),\n! "
                     "axis %s (table: %s)",
                     local_unit, cmor_axes[axis_id].id,
                     cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
            cmor_handle_error(msg, CMOR_CRITICAL);
            cmor_pop_traceback();
            return (1);
        }

        if (strcmp(refaxis->units, "?") == 0)
            strcpy(local_unit, units);
        else
            strcpy(local_unit, refaxis->units);

        ut_trim(local_unit, UT_ASCII);
        cmor_units = ut_parse(ut_read, local_unit, UT_ASCII);

        if (ut_get_status() != UT_SUCCESS) {
            snprintf(msg, CMOR_MAX_STRING,
                     "In udunits analyzing table defined units (%s)\n! "
                     "for axis: %s (table: %s)",
                     local_unit, refaxis->id,
                     cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
            cmor_handle_error(msg, CMOR_CRITICAL);
            cmor_pop_traceback();
            return (1);
        }

        if (ut_are_convertible(cmor_units, user_units) == 0) {
            snprintf(msg, CMOR_MAX_STRING,
                     "axis %i (%s, table: %s): cmor and user units are\n! "
                     "incompatible: %s and %s",
                     axis_id, cmor_axes[axis_id].id,
                     cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id,
                     refaxis->units, units);
            cmor_handle_error(msg, CMOR_CRITICAL);
            cmor_pop_traceback();
            return (1);
        }

        ut_cmor_converter = ut_get_converter(user_units, cmor_units);

        if (ut_get_status() != UT_SUCCESS) {
            snprintf(msg, CMOR_MAX_STRING,
                     "In udunits getting converter, for axis %s (table: %s)",
                     cmor_axes[axis_id].id,
                     cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
            cmor_handle_error(msg, CMOR_CRITICAL);
            cmor_pop_traceback();
            return (1);
        }

        cv_convert_doubles(ut_cmor_converter, values, length, values);

        if (ut_get_status() != UT_SUCCESS) {
            snprintf(msg, CMOR_MAX_STRING,
                     "In udunits converting values, for axis %s (table: %s)",
                     cmor_axes[axis_id].id,
                     cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
            cmor_handle_error(msg, CMOR_CRITICAL);
            cmor_pop_traceback();
            return (1);
        }

        cv_free(ut_cmor_converter);

        if (ut_get_status() != UT_SUCCESS) {
            snprintf(msg, CMOR_MAX_STRING,
                     "Udunits: Error freeing converter, for axis %s (table: %s)",
                     cmor_axes[axis_id].id,
                     cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
            cmor_handle_error(msg, CMOR_CRITICAL);
        }

        ut_free(cmor_units);

        if (ut_get_status() != UT_SUCCESS) {
            snprintf(msg, CMOR_MAX_STRING,
                     "Udunits: Error freeing units, for axis %s (table: %s)",
                     cmor_axes[axis_id].id,
                     cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
            cmor_handle_error(msg, CMOR_CRITICAL);
        }

        ut_free(user_units);

        if (ut_get_status() != UT_SUCCESS) {
            snprintf(msg, CMOR_MAX_STRING,
                     "Udunits: Error freeing units, for axis %s (table: %s)",
                     cmor_axes[axis_id].id,
                     cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
            cmor_handle_error(msg, CMOR_CRITICAL);
        }

    }

/* -------------------------------------------------------------------- */
/*      test for requested                                              */
/*      ok is there some requested values ?                             */
/* -------------------------------------------------------------------- */
    if (n_requested != 0) {
        for (j = 0; j < n_requested; j++) {
            found = 0;
/* -------------------------------------------------------------------- */
/*      First test is it within .001 of req_values                      */
/* -------------------------------------------------------------------- */
            eps = (double)fabs(1.e-3 *
                               refaxis->tolerance * refaxis->requested[j]);

            if (j > 0) {
                eps2 = (double)fabs(refaxis->requested[j] -
                                    refaxis->requested[j - 1]) *
                  refaxis->tolerance;

                if (eps2 < eps) {
                    eps = eps2;
                }
            }

            for (i = 0; i < length; i++) {
                if ((double)fabs(values[i] - refaxis->requested[j]) <= eps) {
                    found = 1;
                    break;
                }
            }

            if (found == 0) {
                snprintf(msg, CMOR_MAX_STRING,
                         "requested value %f for axis %s (table: %s) was not found",
                         refaxis->requested[j], name,
                         cmor_tables[cmor_axes[axis_id].ref_table_id].
                         szTable_id);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }
        }
    }

/* -------------------------------------------------------------------- */
/*      stored_direction                                                */
/* -------------------------------------------------------------------- */
    if ((length > 1) && (((refaxis->stored_direction == 'i') && (values[length - 1] < values[0])) || ((refaxis->stored_direction == 'd') && (values[0] < values[length - 1])))) {       /* need to flip that axis */

        if ((isbounds == 1) && (axis->revert == 1)) {

            if (length > 2) {
                snprintf(msg, CMOR_MAX_STRING,
                         "bounds of axis %s (table: %s) need to be\n! "
                         "flipped but axis values did not need to.\n! "
                         "This is inconsistent",
                         name,
                         cmor_tables[cmor_axes[axis_id].ref_table_id].
                         szTable_id);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }

        }

        axis->revert = -1;

        for (i = 0; i < length / 2; i++) {
            tmp = values[i];
            values[i] = values[length - 1 - i];
            values[length - 1 - i] = tmp;
        }
    }

    if (isbounds == 1) {
/* --------------------------------------------------------------------- */
/*      we need to see if the bounds are stored in                       */
/*      the correct first bound/second bounds order.                     */
/* --------------------------------------------------------------------- */

        if (refaxis->axis != 'X') {
/* -------------------------------------------------------------------- */
/*      values are going incresingly                                    */
/* -------------------------------------------------------------------- */
            if (refaxis->stored_direction == 'i') {
                for (i = 0; i < length; i = i + 2) {
/* -------------------------------------------------------------------- */
/*      ok bounds are not stored first                                  */
/*      bound lower that second one                                     */
/* -------------------------------------------------------------------- */
                    if (values[i] > values[i + 1]) {
                        tmp = values[i];
                        values[i] = values[i + 1];
                        values[i + 1] = tmp;
                    }
                }
            } else {
                for (i = 0; i < length; i = i + 2) {
/* -------------------------------------------------------------------- */
/*      bounds are not stored first bound greater that second one       */
/* -------------------------------------------------------------------- */
                    if (values[i] < values[i + 1]) {
                        tmp = values[i];
                        values[i] = values[i + 1];
                        values[i + 1] = tmp;
                    }
                }
            }
        }

/* -------------------------------------------------------------------- */
/*      need to check for requested bounds                              */
/* -------------------------------------------------------------------- */
        if (refaxis->n_requested_bounds != 0) {
/* -------------------------------------------------------------------- */
/*      let's loop thru it but basically                                */
/*      we need to loop every over ones                                 */
/* -------------------------------------------------------------------- */
            for (j = 0; j < refaxis->n_requested_bounds; j++) {
                found = 0;
/* -------------------------------------------------------------------- */
/*      First test is it within .001 of req_values                      */
/* -------------------------------------------------------------------- */
                eps =
                  (double)fabs(1.e-3 * refaxis->tolerance *
                               refaxis->requested_bounds[j]);
                if ((j % 2) == 0) {
                    eps2 =
                      (double)fabs(refaxis->requested_bounds[j] -
                                   refaxis->requested_bounds[j + 1]) *
                      refaxis->tolerance;
                } else {
                    eps2 =
                      (double)fabs(refaxis->requested_bounds[j] -
                                   refaxis->requested_bounds[j - 1]) *
                      refaxis->tolerance;
                }

                if (eps2 < eps) {
                    eps = eps2;
                }
                if ((j % 2) == 0) {
/* -------------------------------------------------------------------- */
/*      it is a req beg bounds need to test against beg ones only       */
/* -------------------------------------------------------------------- */
                    for (i = 0; i < length; i = i + 2) {

                        if ((double)fabs(values[i] -
                                         refaxis->requested_bounds[j]) <= eps) {
                            found = 1;
                            break;
                        }
                    }
                } else {
/* -------------------------------------------------------------------- */
/*      it is a second bounds, need to test against second nes only     */
/* -------------------------------------------------------------------- */
                    for (i = 1; i < length; i = i + 2) {

                        if ((double)fabs(values[i] -
                                         refaxis->requested_bounds[j]) <= eps) {
                            found = 1;
                            break;
                        }
                    }
                }
                if (found == 0) {
                    snprintf(msg, CMOR_MAX_STRING,
                             "requested value %f for axis %s (table: %s)\n! "
                             "was not found",
                             refaxis->requested_bounds[j], name,
                             cmor_tables[cmor_axes[axis_id].ref_table_id].
                             szTable_id);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }
            }
        }

    }

    if (refaxis->valid_min != 1.e20)
        for (i = 0; i < length; i++)
            if (values[i] < refaxis->valid_min) {
                if (cmor_isLongitude(refaxis) == 1) {
                    treatlon = 1;
                } else {
                    snprintf(msg, CMOR_MAX_STRING,
                             "axis %s (table: %s), detected value at: %f when\n! "
                             "valid_min is %f",
                             name,
                             cmor_tables[cmor_axes[axis_id].
                                         ref_table_id].szTable_id, values[i],
                             refaxis->valid_min);
                    cmor_handle_error(msg, CMOR_NORMAL);
                    cmor_pop_traceback();
                    return (1);
                }
            }

    if (treatlon == 1) {
/* -------------------------------------------------------------------- */
/*       Dealing with coords                                            */
/* -------------------------------------------------------------------- */
        if (isbounds == 0) {
/* -------------------------------------------------------------------- */
/*      lets add 360. until it's all good                               */
/* -------------------------------------------------------------------- */
            for (i = 0; i < length; i++)
                while (values[i] < refaxis->valid_min) {
                    values[i] += 360.;
                    cmor_axes[axis_id].wrapping[i] += 1;
                }
/* -------------------------------------------------------------------- */
/*      now need to determine the offset                                */
/* -------------------------------------------------------------------- */
            for (i = 0; i < length - 1; i++) {
                if (values[i] > values[i + 1]) {
                    axis->offset = i + 1;
                    break;
                }
            }
        }
    }
    treatlon = 0;
    if (refaxis->valid_max != 1.e20)
        for (i = 0; i < length; i++)
            if (values[i] > refaxis->valid_max) {
                if (cmor_isLongitude(refaxis) == 1) {
                    treatlon = 1;
                } else {
                    snprintf(msg, CMOR_MAX_STRING,
                             "axis %s (table: %s) , detected value at:\n! "
                             "%f when valid_max is %f",
                             name,
                             cmor_tables[cmor_axes[axis_id].
                                         ref_table_id].szTable_id, values[i],
                             refaxis->valid_max);
                    cmor_handle_error(msg, CMOR_NORMAL);
                    cmor_pop_traceback();
                    return (1);
                }
            }

    if ((treatlon == 1) && (isbounds == 0)) {
/* -------------------------------------------------------------------- */
/*      ok lets add 360. until it's all good                            */
/* -------------------------------------------------------------------- */
        for (i = 0; i < length; i++) {
            while (values[i] > refaxis->valid_max) {
                values[i] -= 360.;
                cmor_axes[axis_id].wrapping[i] -= 1;
            }
        }

/* -------------------------------------------------------------------- */
/*      ok now need to determine the offset                             */
/* -------------------------------------------------------------------- */
        for (i = 0; i < length - 1; i++) {
            if (values[i] > values[i + 1]) {
                axis->offset = i + 1;
                break;
            }
        }
    }
    if ((isbounds == 1) && (cmor_isLongitude(refaxis) == 1)) {
        for (i = 0; i < length / 2; i++) {
            values[2 * i] += 360. * cmor_axes[axis_id].wrapping[i];
            values[2 * i + 1] += 360. * cmor_axes[axis_id].wrapping[i];
        }
    }
/* -------------------------------------------------------------------- */
/*      move the offset thing                                           */
/* -------------------------------------------------------------------- */
    if (axis->offset != 0) {
        if (isbounds == 0) {
            tmplon = malloc(axis->offset * sizeof(double));
            for (i = 0; i < axis->offset; i++) {
                tmplon[i] = values[i];
            }
            for (i = axis->offset; i < length; i++) {
                values[i - axis->offset] = values[i];
            }
            for (i = 0; i < axis->offset; i++) {
                values[i + length - axis->offset] = tmplon[i];
            }
            free(tmplon);
        } else {
            tmplon = malloc(2 * axis->offset * sizeof(double));
            for (i = 0; i < 2 * axis->offset; i++) {
                tmplon[i] = values[i];
            }
            for (i = 2 * axis->offset; i < length; i++) {
                values[i - 2 * axis->offset] = values[i];
            }
            for (i = 0; i < 2 * axis->offset; i++) {
                values[i + length - 2 * axis->offset] = tmplon[i];
            }
            free(tmplon);
        }
    }
    i = cmor_check_monotonic(&values[0], length, name, isbounds, axis_id);
    cmor_pop_traceback();
    return (0);
}

/************************************************************************/
/*                        cmor_check_interval()                         */
/************************************************************************/
int cmor_check_interval(int axis_id, char *interval, double *values,
                        int nvalues, int isbounds)
{
    char ctmp[CMOR_MAX_STRING];
    char ctmp2[CMOR_MAX_STRING];
    char msg[CMOR_MAX_STRING];
    int i, j, n, nval;
    double interv, diff, diff2, tmp;
    extern ut_system *ut_read;
    ut_unit *user_units = NULL, *cmor_units = NULL;
    cv_converter *ut_cmor_converter = NULL;
    double *tmp_values = NULL;
    cmor_axis_def_t *refaxis;
    cmor_axis_t *axis;
    cdCalenType icali;
    cdCompTime comptime;

    cmor_add_traceback("cmor_check_interval");
    axis = &cmor_axes[axis_id];
    refaxis = &cmor_tables[axis->ref_table_id].axes[axis->ref_axis_id];

    if (isbounds == 0) {
        nval = nvalues;
        tmp_values = values;
    } else {
        nval = nvalues / 2 + 1;
        tmp_values = malloc(sizeof(double) * nval);
        if (tmp_values == NULL) {
            snprintf(ctmp, CMOR_MAX_STRING,
                     "Error allocating memory for %i values in "
                     "check_interval (%s), axis: %s (table: %s)",
                     nval, interval, cmor_axes[axis_id].id,
                     cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
            cmor_handle_error(ctmp, CMOR_CRITICAL);
        }
        for (i = 0; i < nval - 1; i++)
            tmp_values[i] = values[i * 2];
        tmp_values[nval - 1] = values[nvalues - 1];
    }
/* -------------------------------------------------------------------- */
/*      first of all need to figure out the                             */
/*      numeric and unit part of the interval                           */
/* -------------------------------------------------------------------- */
    cmor_trim_string(interval, ctmp);
    n = strlen(ctmp);
    for (i = 0; i < n; i++) {
        if (ctmp[i] == ' ') {
            ctmp2[i] = '\0';
            break;
        }
        ctmp2[i] = ctmp[i];
    }
    interv = atof(ctmp2);
    for (j = 0; j < n - i; j++) {
        ctmp2[j] = ctmp[j + i + 1];
    }
    ctmp2[j] = '\0';
/* -------------------------------------------------------------------- */
/*      convert this in some base units: seconds                        */
/* -------------------------------------------------------------------- */
    sprintf(msg, "seconds");
    ut_trim(msg, UT_ASCII);
    cmor_units = ut_parse(ut_read, msg, UT_ASCII);
    user_units = ut_parse(ut_read, ctmp2, UT_ASCII);
    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING,
                 "In udunuits parsing user units: %s, axis: %s (table: %s)",
                 ctmp2, cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }
    if (ut_are_convertible(cmor_units, user_units) == 0) {
        snprintf(ctmp, CMOR_MAX_STRING,
                 "axis interval units (%s) are incompatible with\n! "
                 "seconds, axis: %s (table: %s)",
                 ctmp2, cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(ctmp, CMOR_CRITICAL);
        cmor_pop_traceback();
        return (1);
    }
    ut_cmor_converter = ut_get_converter(user_units, cmor_units);
    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING,
                 "In udunuits getting converter, axis: %s (table: %s)",
                 cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }
    tmp = cv_convert_double(ut_cmor_converter, interv);
    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING,
                 "In udunuits converting, axis: %s (table: %s)",
                 cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }
    interv = tmp;

    cv_free(ut_cmor_converter);

    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Udunits: Error freeing converter, axis: %s (table: %s)",
                 cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }

    ut_free(user_units);

    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Udunits: Error freeing units, axis: %s (table: %s)",
                 cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }

    /* at this point we need to figure out the axis units interval */
    strcpy(ctmp, refaxis->units);
    n = strlen(ctmp);
    for (i = 0; i < n; i++) {
        if (ctmp[i] == ' ') {
            ctmp2[i] = '\0';
            break;
        }
        ctmp2[i] = ctmp[i];
    }

    user_units = ut_parse(ut_read, ctmp2, UT_ASCII);

    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING,
                 "In udunits parsing user units: %s, axis: %s (table: %s)",
                 ctmp2, cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }

    if (ut_are_convertible(cmor_units, user_units) == 0) {
        snprintf(ctmp, CMOR_MAX_STRING,
                 "axis interval units (%s) are incompatible with seconds, axis: %s (table: %s)",
                 ctmp2, cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(ctmp, CMOR_CRITICAL);
        cmor_pop_traceback();
        return (1);
    }

    ut_cmor_converter = ut_get_converter(user_units, cmor_units);

    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Udunits: Error getting converter from %s to %s, axis: %s (table: %s)",
                 ctmp2, msg, cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }

    tmp = 0.;
    for (i = 0; i < nval - 1; i++) {
        diff = tmp_values[i + 1] - tmp_values[i];       /* still in user units */
/* -------------------------------------------------------------------- */
/*      converts to seconds                                             */
/* -------------------------------------------------------------------- */
        tmp = cv_convert_double(ut_cmor_converter, diff);
        if (ut_get_status() != UT_SUCCESS) {
            snprintf(msg, CMOR_MAX_STRING,
                     "In udunits converting, axis: %s (table: %s)",
                     cmor_axes[axis_id].id,
                     cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
            cmor_handle_error(msg, CMOR_CRITICAL);
        }

        diff2 = tmp;
        tmp = (double)fabs(diff2 - interv);
        tmp = tmp / interv;
/* -------------------------------------------------------------------- */
/* more than 20% diff issues an error                                   */
/* -------------------------------------------------------------------- */

        if (tmp > cmor_tables[cmor_axes[axis_id].ref_table_id].interval_error) {
            if (isbounds == 1) {
                snprintf(ctmp, CMOR_MAX_STRING,
                         "approximate time axis interval is defined as %f "
                         "seconds (%s), for value %i we got a difference "
                         "(based on bounds) of %f seconds, (%f %s), which "
                         "is %f %% , seems too big, check your values",
                         interv, interval, i + 1, diff2, diff, ctmp2,
                         tmp * 100.);
            } else {
                if (isbounds == 1) {
                    snprintf(ctmp, CMOR_MAX_STRING,
                             "approximate time axis interval is defined as "
                             "%f seconds (%s), for value %i we got a difference "
                             "(based on bounds) of %f seconds (%f %s), which is "
                             "%f %% , seems too big, check your values",
                             interv, interval, i + 1, diff2, diff, ctmp2,
                             tmp * 100.);
                } else {
                    snprintf(ctmp, CMOR_MAX_STRING,
                             "approximate time axis interval is defined as %f "
                             "seconds (%s), for value %i we got a difference of "
                             "%f seconds (%f %s), which is %f %% , seems too big, "
                             "check your values",
                             interv, interval, i + 1, diff2, diff, ctmp2,
                             tmp * 100.);
                }
            }

            cmor_handle_error(ctmp, CMOR_CRITICAL);
        } else if (tmp >
                   cmor_tables[cmor_axes[axis_id].ref_table_id].
                   interval_warning) {
/* -------------------------------------------------------------------- */
/*      more than 10% diff issues a warning                             */
/* -------------------------------------------------------------------- */
            snprintf(ctmp, CMOR_MAX_STRING,
                     "approximate time axis interval is defined as %f "
                     "seconds (%s), for value %i we got a difference of %f "
                     "seconds (%f %s), which is %f %% , seems too big, check "
                     "your values",
                     interv, interval, i + 1, diff2, diff, ctmp2, tmp * 100.);
            cmor_handle_error(ctmp, CMOR_WARNING);
        }
    }

/* -------------------------------------------------------------------- */
/*      bounds being at begining and end of the month                   */
/* -------------------------------------------------------------------- */
    if ((isbounds == 1)
        && (fabs(interv - 2592000.) / 2592000. < .1)) {
        cmor_get_cur_dataset_attribute("calendar", ctmp);
        if (cmor_calendar_c2i(ctmp, &icali) != 0) {
            snprintf(ctmp, CMOR_MAX_STRING,
                     "Cannot convert times for calendar: %s", ctmp);
            cmor_handle_error(ctmp, CMOR_NORMAL);
            cmor_pop_traceback();
            return (1);
        }

        for (i = 0; i < nvalues; i++) {
            cmor_convert_time_units(axis->iunits,
                                    cmor_tables[axis->ref_table_id].
                                    axes[axis->ref_axis_id].units, ctmp);
            cdRel2Comp(icali, ctmp, values[i], &comptime);
            if (comptime.day != 1) {
                snprintf(ctmp, CMOR_MAX_STRING,
                         "Bounds value %ld-%d-%d is not beg or end of month\n! "
                         "and you seem to be writing monthly data, please check",
                         comptime.year, comptime.month, comptime.day);
                cmor_handle_error(ctmp, CMOR_WARNING);
            }
        }
    }

    cv_free(ut_cmor_converter);

    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Udunits: Error freeing converter, axis: %s (table: %s)",
                 cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }

    ut_free(cmor_units);

    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Udunits: Error freeing units, axis: %s (table: %s)",
                 cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }

    ut_free(user_units);

    if (ut_get_status() != UT_SUCCESS) {
        snprintf(msg, CMOR_MAX_STRING,
                 "Udunits: Error freeing units, axis: %s (table: %s)",
                 cmor_axes[axis_id].id,
                 cmor_tables[cmor_axes[axis_id].ref_table_id].szTable_id);
        cmor_handle_error(msg, CMOR_CRITICAL);
    }

    if (isbounds == 1)
        free(tmp_values);

    cmor_pop_traceback();
    return (0);
}

/************************************************************************/
/*                             cmor_axis()                              */
/************************************************************************/
int cmor_axis(int *axis_id, char *name, char *units, int length,
              void *coord_vals, char type, void *cell_bounds,
              int cell_bounds_ndim, char *interval)
{
    extern int cmor_naxes;
    extern int CMOR_TABLE;

    int i, iref, j, k, l;
    cmor_axis_def_t refaxis;
    char msg[CMOR_MAX_STRING];
    char ctmp[CMOR_MAX_STRING];

    cmor_add_traceback("cmor_axis");
    cmor_is_setup();

    if (CMOR_TABLE == -1) {

        cmor_handle_error("You did not define a table yet!", CMOR_CRITICAL);
    }

    if (cmor_naxes == CMOR_MAX_AXES - 1) {
        cmor_handle_error("Too many axes defined", CMOR_CRITICAL);
        cmor_pop_traceback();
        return (1);
    }
    cmor_naxes += 1;
    strncpytrim(cmor_axes[cmor_naxes].id, name, CMOR_MAX_STRING);

/* -------------------------------------------------------------------- */
/*        ok now look which axis is corresponding in                    */
/*        table if not found then error                                 */
/* -------------------------------------------------------------------- */
    iref = -1;
    cmor_trim_string(name, msg);

    for (i = 0; i <= cmor_tables[CMOR_TABLE].naxes; i++) {
        if (strcmp(cmor_tables[CMOR_TABLE].axes[i].id, msg) == 0) {
            iref = i;
            break;
        }
    }

    if (iref == -1) {
        snprintf(ctmp, CMOR_MAX_STRING,
                 "Could not find all axes for this variable. \n! "
                 "Check that all your axes name matches the ones found in \n! "
                 "the table %s. \n!", cmor_tables[CMOR_TABLE].szTable_id);
        cmor_handle_error(ctmp, CMOR_CRITICAL);
    }

    refaxis = cmor_tables[CMOR_TABLE].axes[iref];
    cmor_axes[cmor_naxes].ref_table_id = CMOR_TABLE;
    cmor_axes[cmor_naxes].ref_axis_id = iref;

    cmor_axes[cmor_naxes].length = length;
    cmor_axes[cmor_naxes].type = type;
    cmor_axes[cmor_naxes].store_in_netcdf = 1;

    if ((refaxis.axis == 'T') || strstr(units, "since")) {
        cmor_get_cur_dataset_attribute("calendar", ctmp);
        cmor_set_axis_attribute(cmor_naxes, "calendar", 'c', ctmp);
        cmor_convert_time_units(units, refaxis.units, &ctmp[0]);
        strncpytrim(cmor_axes[cmor_naxes].iunits, units, CMOR_MAX_STRING);
    } else {
        strcpy(ctmp, refaxis.units);
    }

    cmor_set_axis_attribute(cmor_naxes, "units", 'c', ctmp);
    ctmp[0] = refaxis.axis;
    ctmp[1] = '\0';
    cmor_axes[cmor_naxes].axis = refaxis.axis;

    if (refaxis.axis != '\0') {
        cmor_set_axis_attribute(cmor_naxes, "axis", 'c', ctmp);
    }

    if (refaxis.positive != '\0') {

        if (refaxis.positive == 'u')
            cmor_set_axis_attribute(cmor_naxes, "positive", 'c', "up");
        else if (refaxis.positive == 'd')
            cmor_set_axis_attribute(cmor_naxes, "positive", 'c', "down");

    }

    if (refaxis.long_name[0] != '\0') {
        cmor_set_axis_attribute(cmor_naxes, "long_name", 'c',
                                refaxis.long_name);
    }
    if (refaxis.standard_name[0] != '\0') {
        cmor_set_axis_attribute(cmor_naxes, "standard_name", 'c',
                                refaxis.standard_name);
    }
    if (refaxis.formula[0] != '\0') {
        cmor_set_axis_attribute(cmor_naxes, "formula", 'c', refaxis.formula);
    }
    if (refaxis.z_factors[0] != '\0') {
        cmor_set_axis_attribute(cmor_naxes, "z_factors", 'c',
                                refaxis.z_factors);
    }
    if ((refaxis.z_bounds_factors[0] != '\0') && (cell_bounds != NULL)) {
        cmor_set_axis_attribute(cmor_naxes, "z_bounds_factors", 'c',
                                refaxis.z_bounds_factors);
    }

    if (strcmp(cmor_axes[cmor_naxes].id, "standard_sigma") == 0) {
        cmor_axes[cmor_naxes].hybrid_in = 3;
    }

    if (strcmp(cmor_axes[cmor_naxes].id, "standard_hybrid_sigma") == 0) {
        cmor_axes[cmor_naxes].hybrid_in = 1;
    }

    if (strcmp(cmor_axes[cmor_naxes].id, "alternate_hybrid_sigma") == 0) {
        cmor_axes[cmor_naxes].hybrid_in = 2;
    }

    if (refaxis.convert_to[0] != '\0') {
        if (strcmp(refaxis.convert_to, name) != 0) {
            if (strcmp(refaxis.convert_to, "standard_hybrid_sigma") == 0) {
                /* remembers what the original type was */
                i = cmor_axes[cmor_naxes].hybrid_in;
                if ((strcmp(name, "standard_hybrid_sigma") != 0)
                    && (strcmp(name, "alternate_hybrid_sigma") != 0)
                    && (strcmp(name, "standard_sigma") != 0)) {
                    snprintf(msg, CMOR_MAX_STRING,
                             "axis: %s (table: %s) converting to \"standard_hybrid_sigma\" from unknown type: %s",
                             cmor_axes[cmor_naxes].id,
                             cmor_tables[CMOR_TABLE].szTable_id, name);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }

                cmor_axis(axis_id, refaxis.convert_to, units, length,
                          coord_vals, type, cell_bounds, cell_bounds_ndim,
                          interval);
                cmor_axes[cmor_naxes].hybrid_in = i;
                cmor_axes[cmor_naxes].hybrid_out = 1;
                *axis_id = cmor_naxes;
                cmor_pop_traceback();
                return (0);
            } else {
                snprintf(msg, CMOR_MAX_STRING,
                         "axis: %s (table: %s) is declared to be converted "
                         "to unknown type: %s",
                         cmor_axes[cmor_naxes].id,
                         cmor_tables[CMOR_TABLE].szTable_id,
                         refaxis.convert_to);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }
        }
    } else {
        cmor_axes[cmor_naxes].hybrid_out = cmor_axes[cmor_naxes].hybrid_in;
    }

/* -------------------------------------------------------------------- */
/*      interval attribute                                              */
/* -------------------------------------------------------------------- */

    if ((interval != NULL) && (interval[0] != '\0')) {
        cmor_set_axis_attribute(cmor_naxes, "interval", 'c', interval);
    }

/* -------------------------------------------------------------------- */
/*      test whether bounds are requested or not                        */
/* -------------------------------------------------------------------- */

    if ((cell_bounds == NULL) && (refaxis.must_have_bounds == 1)) {
        if (refaxis.axis != 'T') {
            snprintf(msg, CMOR_MAX_STRING,
                     "axis: %s (table: %s) must have bounds, you did not "
                     "pass any when creating it via cmor_axis",
                     cmor_axes[cmor_naxes].id,
                     cmor_tables[CMOR_TABLE].szTable_id);
            cmor_handle_error(msg, CMOR_CRITICAL);
        } else if (coord_vals != NULL) {
/* -------------------------------------------------------------------- */
/*      we passed time values that means we                             */
/*      do not intend to pass them later                                */
/* -------------------------------------------------------------------- */
            snprintf(msg, CMOR_MAX_STRING,
                     "axis: %s (table: %s) must have bounds, you did not "
                     "pass any when creating it via cmor_axis",
                     cmor_axes[cmor_naxes].id,
                     cmor_tables[CMOR_TABLE].szTable_id);
            cmor_handle_error(msg, CMOR_CRITICAL);
        }
    }
/* -------------------------------------------------------------------- */
/*      user passed coords need to convert this guys                    */
/* -------------------------------------------------------------------- */

    if (coord_vals != NULL) {
        if (type != 'c') {

            cmor_axes[cmor_naxes].values = malloc(length * sizeof(double));

            if (refaxis.axis == 'X') {

                cmor_axes[cmor_naxes].wrapping = malloc(length * sizeof(int));
                for (i = 0; i < length; i++)
                    cmor_axes[cmor_naxes].wrapping[i] = 0;
            }

            if (cmor_axes[cmor_naxes].values == NULL) {
                snprintf(msg, CMOR_MAX_STRING,
                         "cannot allocate memory for %i double elts for "
                         "axis %s (table: %s)",
                         length, cmor_axes[cmor_naxes].id,
                         cmor_tables[CMOR_TABLE].szTable_id);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }

            if (type == 'f') {
                for (i = 0; i < length; i++) {
                    cmor_axes[cmor_naxes].values[i] =
                      (double)((float *)coord_vals)[i];
                }
            } else if (type == 'l') {
                for (i = 0; i < length; i++) {
                    cmor_axes[cmor_naxes].values[i] =
                      (double)((long *)coord_vals)[i];
                }
            } else if (type == 'i') {
                for (i = 0; i < length; i++) {
                    cmor_axes[cmor_naxes].values[i] =
                      (double)((int *)coord_vals)[i];
                }
            } else if (type == 'd') {
                for (i = 0; i < length; i++) {
                    cmor_axes[cmor_naxes].values[i] =
                      (double)((double *)coord_vals)[i];
                }
            }
        } else {

/* -------------------------------------------------------------------- */
/*        check that output type is also character                      */
/* -------------------------------------------------------------------- */
            if (type != refaxis.type) {
                snprintf(msg, CMOR_MAX_STRING,
                         "You are trying to define axis %s (table: %s) as "
                         "character when it should be of type: %c\n! ",
                         cmor_axes[cmor_naxes].id,
                         cmor_tables[CMOR_TABLE].szTable_id, refaxis.type);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }

            cmor_axes[cmor_naxes].cvalues = malloc(length * sizeof(char *));

            if (cmor_axes[cmor_naxes].cvalues == NULL) {
                snprintf(msg, CMOR_MAX_STRING,
                         "cannot allocate memory for %i char elts for axis "
                         "%s (table: %s)",
                         length, cmor_axes[cmor_naxes].id,
                         cmor_tables[CMOR_TABLE].szTable_id);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }

            if (cell_bounds_ndim == 0)
                k = CMOR_MAX_STRING;
            else
                k = cell_bounds_ndim;

            for (i = 0; i < length; i++) {
                j = strlen(&((char *)coord_vals)[i * k]);

                if (j > k)
                    j = k;
                cmor_axes[cmor_naxes].cvalues[i] =
                  malloc((j + 1) * sizeof(char));

                if (cmor_axes[cmor_naxes].cvalues[i] == NULL) {
                    snprintf(msg, CMOR_MAX_STRING,
                             "cannot allocate memory for %i char elts for axis "
                             "%s (table: %s)",
                             j + 1, cmor_axes[cmor_naxes].id,
                             cmor_tables[CMOR_TABLE].szTable_id);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }

                strncpy(cmor_axes[cmor_naxes].cvalues[i],
                        &((char *)coord_vals)[i * k], j);
                cmor_axes[cmor_naxes].cvalues[i][j] = '\0';

            }
/* -------------------------------------------------------------------- */
/*      we don't want to do the bounds stuff                            */
/*      but we need to look at the requested thing!                     */
/* -------------------------------------------------------------------- */
            if (refaxis.crequested != NULL) {
                i = 0;
                j = strlen(&refaxis.crequested[0]);

                for (i = 0; i < j; i++) {
                    k = 0;
                    while ((refaxis.crequested[i] != ' ')
                           && (refaxis.crequested[i] != '\0')) {
                        ctmp[k] = refaxis.crequested[i];
                        i++;
                        k++;
                    }

                    ctmp[k] = '\0';
                    k = -1;
                    for (l = 0; l < length; l++) {
                        if (strncmp(ctmp, cmor_axes[cmor_naxes].cvalues[l],
                                    strlen(ctmp)) == 0)
                            k = l;
                    }

                    if (k == -1) {
                        snprintf(msg, CMOR_MAX_STRING,
                                 "Requested region for axis '%s' (table: %s) "
                                 "is not passed: '%s'",
                                 cmor_axes[cmor_naxes].id,
                                 cmor_tables[CMOR_TABLE].szTable_id, ctmp);
                        cmor_handle_error(msg, CMOR_CRITICAL);
                    }
                }
            }

            if (refaxis.out_name[0] != '\0') {
                strcpy(cmor_axes[cmor_naxes].id, refaxis.out_name);
            }
            *axis_id = cmor_naxes;
            cmor_pop_traceback();
            return (0);
        }

        cmor_treat_axis_values(cmor_naxes,
                               &cmor_axes[cmor_naxes].values[0],
                               length, refaxis.n_requested, units, name, 0);

/* -------------------------------------------------------------------- */
/*      puts bounds on 2d array                                         */
/* -------------------------------------------------------------------- */
        if ((cell_bounds != NULL) && (cell_bounds_ndim != 0)) {

            cmor_axes[cmor_naxes].bounds = malloc(2 * length * sizeof(double));

            if (cmor_axes[cmor_naxes].bounds == NULL) {
                snprintf(msg, CMOR_MAX_STRING,
                         "cannot allocate memory for %i bounds elts for axis "
                         "%s (table: %s)",
                         2 * length, cmor_axes[cmor_naxes].id,
                         cmor_tables[CMOR_TABLE].szTable_id);
                free(cmor_axes[cmor_naxes].bounds);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }

            if (cell_bounds_ndim == 2) {
                if (type == 'f') {
                    for (i = 0; i < length; i++) {
                        cmor_axes[cmor_naxes].bounds[2 * i] =
                          (double)((float *)cell_bounds)[2 * i];
                        cmor_axes[cmor_naxes].bounds[2 * i + 1] =
                          (double)((float *)cell_bounds)[2 * i + 1];
                    }
                } else if (type == 'd') {
                    for (i = 0; i < length; i++) {
                        cmor_axes[cmor_naxes].bounds[2 * i] =
                          (double)((double *)cell_bounds)[2 * i];
                        cmor_axes[cmor_naxes].bounds[2 * i + 1] =
                          (double)((double *)cell_bounds)[2 * i + 1];
                    }
                } else if (type == 'l') {
                    for (i = 0; i < length; i++) {
                        cmor_axes[cmor_naxes].bounds[2 * i] =
                          (double)((long *)cell_bounds)[2 * i];
                        cmor_axes[cmor_naxes].bounds[2 * i + 1] =
                          (double)((long *)cell_bounds)[2 * i + 1];
                    }
                } else if (type == 'i') {
                    for (i = 0; i < length; i++) {
                        cmor_axes[cmor_naxes].bounds[2 * i] =
                          (double)((int *)cell_bounds)[2 * i];
                        cmor_axes[cmor_naxes].bounds[2 * i + 1] =
                          (double)((int *)cell_bounds)[2 * i + 1];
                    }
                } else {        /* ??? charcter axis code here */
                    snprintf(msg, CMOR_MAX_STRING,
                             "CMOR cannot handle axes of type %c please "
                             "change type, axis: %s (table: %s)",
                             type, cmor_axes[cmor_naxes].id,
                             cmor_tables[CMOR_TABLE].szTable_id);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }
            } else if (cell_bounds_ndim == 1) {
                if (type == 'f') {
                    for (i = 0; i < length; i++) {
                        cmor_axes[cmor_naxes].bounds[2 * i] =
                          (double)((float *)cell_bounds)[i];
                        cmor_axes[cmor_naxes].bounds[2 * i + 1] =
                          (double)((float *)cell_bounds)[i + 1];
                    }
                } else if (type == 'd') {
                    for (i = 0; i < length; i++) {
                        cmor_axes[cmor_naxes].bounds[2 * i] =
                          (double)((double *)cell_bounds)[i];
                        cmor_axes[cmor_naxes].bounds[2 * i + 1] =
                          (double)((double *)cell_bounds)[i + 1];
                    }
                } else if (type == 'i') {
                    for (i = 0; i < length; i++) {
                        cmor_axes[cmor_naxes].bounds[2 * i] =
                          (double)((int *)cell_bounds)[i];
                        cmor_axes[cmor_naxes].bounds[2 * i + 1] =
                          (double)((int *)cell_bounds)[i + 1];
                    }
                } else if (type == 'l') {
                    for (i = 0; i < length; i++) {
                        cmor_axes[cmor_naxes].bounds[2 * i] =
                          (double)((long *)cell_bounds)[i];
                        cmor_axes[cmor_naxes].bounds[2 * i + 1] =
                          (double)((long *)cell_bounds)[i + 1];
                    }
                } else {        /* ??? charcter axis code here */
                    snprintf(msg, CMOR_MAX_STRING,
                             "CMOR cannot handle axes of type %c please "
                             "change type, axis: %s (table: %s)",
                             type, cmor_axes[cmor_naxes].id,
                             cmor_tables[CMOR_TABLE].szTable_id);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }
            }

            cmor_treat_axis_values(cmor_naxes,
                                   &cmor_axes[cmor_naxes].bounds[0], 2 * length,
                                   0, units, name, 1);
/* -------------------------------------------------------------------- */
/*      At this point we are checking that                              */
/*      the axis values are within bounds                               */
/* -------------------------------------------------------------------- */
            cmor_check_values_inside_bounds(&cmor_axes[cmor_naxes].values[0],
                                            &cmor_axes[cmor_naxes].bounds[0],
                                            length, name);

            if ((refaxis.axis == 'T') && (refaxis.climatology == 0)) {
/* -------------------------------------------------------------------- */
/*      ok now we need to overwrite the time values with mid point      */
/* -------------------------------------------------------------------- */
                for (i = 0; i < length; i++)
                    cmor_axes[cmor_naxes].values[i] =
                      (cmor_axes[cmor_naxes].bounds[2 * i]
                       + cmor_axes[cmor_naxes].bounds[2 * i + 1]) / 2.;
/* -------------------------------------------------------------------- */
/*      here we check if interval is about right                        */
/*      just keep the begining of units out                             */
/*      no need to know the since par                                   */
/* -------------------------------------------------------------------- */
                j = 0;
                while (refaxis.units[j] == ' ')
                    j++;
                i = 0;
                while ((refaxis.units[i + j] != ' ') &&
                       (refaxis.units[i + j] != '\0')) {
                    ctmp[i] = refaxis.units[i + j];
                    i++;
                }
                ctmp[i] = '\0';
                snprintf(msg, CMOR_MAX_STRING, "%lf %s",
                         cmor_tables[cmor_axes[cmor_naxes].
                                     ref_table_id].interval, ctmp);
/* -------------------------------------------------------------------- */
/*      skip this for non standard cal                                  */
/* -------------------------------------------------------------------- */
                i = cmor_check_interval(cmor_naxes, msg,
                                        &cmor_axes[cmor_naxes].values[0],
                                        length, 0);
            }
        }
    } else {
        if ((refaxis.axis != 'T') && (refaxis.index_only == 'n')) {
            snprintf(msg, CMOR_MAX_STRING,
                     "function called for axis '%s' (table: %s) w/o any values",
                     cmor_axes[cmor_naxes].id,
                     cmor_tables[CMOR_TABLE].szTable_id);
            cmor_handle_error(msg, CMOR_CRITICAL);
        }
        if (refaxis.index_only != 'n') {
/* -------------------------------------------------------------------- */
/*       index no need to store values                                  */
/* -------------------------------------------------------------------- */
            cmor_axes[cmor_naxes].store_in_netcdf = 0;
        }
    }

    if (refaxis.out_name[0] != '\0')
        strcpy(cmor_axes[cmor_naxes].id, refaxis.out_name);
    *axis_id = cmor_naxes;

    cmor_pop_traceback();
    free(cmor_axes[cmor_naxes].wrapping);

/* -------------------------------------------------------------------- */
/*     Fix issue #6 setting the pointer to NULL                         */
/* -------------------------------------------------------------------- */
    cmor_axes[cmor_naxes].wrapping = NULL;

    return (0);
}

/************************************************************************/
/*                       cmor_init_axis_def()                           */
/************************************************************************/
void cmor_init_axis_def(cmor_axis_def_t * axis, int table_id)
{
    cmor_is_setup();
    axis->table_id = table_id;
    axis->climatology = 0;
    axis->standard_name[0] = '\0';
    axis->units[0] = '\0';
    axis->axis = '\0';
    axis->positive = '\0';
    axis->long_name[0] = '\0';
    axis->out_name[0] = '\0';
    axis->type = 'd';
    axis->stored_direction = 'i';
    axis->valid_min = 1.e20;    /* means no check */
    axis->valid_max = 1.e20;
    if (axis->requested != NULL)
        free(axis->requested);
    axis->requested = NULL;
    if (axis->requested_bounds != NULL)
        free(axis->requested_bounds);
    axis->requested_bounds = NULL;
    axis->tolerance = 1.e-3;
    axis->value = 1.e20;
    axis->cvalue[0] = '\0';
    axis->bounds_value[0] = 1.e20;
    axis->bounds_value[1] = 1.e20;
    axis->convert_to[0] = '\0';
    axis->formula[0] = '\0';
    axis->z_factors[0] = '\0';
    axis->z_bounds_factors[0] = '\0';
    if (axis->crequested != NULL)
        free(axis->requested);
    axis->crequested = NULL;
    axis->cname[0] = '\0';
    if (axis->requested_bounds != NULL)
        free(axis->requested_bounds);
    axis->requested = NULL;
    axis->n_requested = 0;
    axis->n_requested_bounds = 0;
    axis->index_only = 'n';
    axis->must_have_bounds = 0;
    axis->must_call_cmor_grid = 0;
}

/************************************************************************/
/*                      cmor_set_axis_def_att()                         */
/************************************************************************/

int cmor_set_axis_def_att(cmor_axis_def_t * axis, char att[CMOR_MAX_STRING],
                          char val[CMOR_MAX_STRING])
{
    int i, n, j;
    char dim[CMOR_MAX_STRING];
    char msg[CMOR_MAX_STRING];
    double vals[CMOR_MAX_ELEMENTS];
    double *tmp = NULL;

    cmor_add_traceback("cmor_set_axis_def_att");
    cmor_is_setup();
/*----------------------------------------------------------------------*/
/*  If val is an empty string keep default values set in init_axs_def   */
/*----------------------------------------------------------------------*/
    if (strcmp(val, "") == 0) {
        cmor_pop_traceback();
        return (0);
    }
    if (strcmp(att, AXIS_ATT_REQUIRED) == 0) {

        strcpy(axis->required, att);

    } else if (strcmp(att, AXIS_ATT_ID) == 0) {

        strcpy(axis->id, val);

    } else if (strcmp(att, AXIS_ATT_CLIMATOLOGY) == 0) {

        if (strcmp(val, "yes") == 0) {
            axis->climatology = 1;
        }

    } else if (strcmp(att, AXIS_ATT_OUTNAME) == 0) {

        strcpy(axis->out_name, val);

    } else if (strcmp(att, AXIS_ATT_STANDARDNAME) == 0) {

        strcpy(axis->standard_name, val);

    } else if (strcmp(att, AXIS_ATT_LONGNAME) == 0) {

        strcpy(axis->long_name, val);

    } else if (strcmp(att, AXIS_ATT_CONVERTTO) == 0) {

        strcpy(axis->convert_to, val);

    } else if (strcmp(att, AXIS_ATT_FORMULA) == 0) {

        strcpy(axis->formula, val);

    } else if (strcmp(att, AXIS_ATT_ZFACTORS) == 0) {

        strcpy(axis->z_factors, val);

    } else if (strcmp(att, AXIS_ATT_ZBOUNDSFACTORS) == 0) {

        strcpy(axis->z_bounds_factors, val);

    } else if (strcmp(att, AXIS_ATT_UNITS) == 0) {

        strncpy(axis->units, val, CMOR_MAX_STRING);

    } else if (strcmp(att, AXIS_ATT_STOREDDIRECTION) == 0) {

        if (val[0] == 'i')
            axis->stored_direction = 'i';
        else if (val[0] == 'd')
            axis->stored_direction = 'd';

    } else if (strcmp(att, AXIS_ATT_POSITIVE) == 0) {

        axis->positive = val[0];

    } else if (strcmp(att, AXIS_ATT_AXIS) == 0) {

        axis->axis = val[0];

    } else if (strcmp(att, AXIS_ATT_INDEXONLY) == 0) {

        axis->index_only = val[0];

    } else if (strcmp(att, AXIS_ATT_MUSTBOUNDS) == 0) {

        if (strcmp(val, "yes") == 0) {
            axis->must_have_bounds = 1;
        }

    } else if (strcmp(att, AXIS_ATT_MUSTCALLGRID) == 0) {

        if (strcmp(val, "yes") == 0)
            axis->must_call_cmor_grid = 1;

    } else if (strcmp(att, AXIS_ATT_TYPE) == 0) {

        if (strcmp(val, "real") == 0)
            axis->type = 'f';
        else if (strcmp(val, "double") == 0)
            axis->type = 'd';
        else if (strcmp(val, "integer") == 0)
            axis->type = 'i';
        else if (strcmp(val, "long") == 0)
            axis->type = 'l';
        else if (strcmp(val, "character") == 0)
            axis->type = 'c';

    } else if (strcmp(att, AXIS_ATT_VALIDMIN) == 0) {

        axis->valid_min = atof(val);

    } else if (strcmp(att, AXIS_ATT_VALIDMAX) == 0) {

        axis->valid_max = atof(val);

    } else if (strcmp(att, AXIS_ATT_TOLERANCE) == 0) {

        axis->tolerance = atof(val);

    } else if (strcmp(att, AXIS_ATT_TOLONREQUESTS) == 0) {

        axis->tolerance = atof(val);

    } else if (strcmp(att, AXIS_ATT_VALUE) == 0) {

        strncpytrim(axis->cvalue, val, CMOR_MAX_STRING);
        axis->value = atof(val);

    } else if (strcmp(att, AXIS_ATT_BOUNDVALUES) == 0) {

        sscanf(val, "%lf %lf", &axis->bounds_value[0], &axis->bounds_value[1]);

    } else if (strcmp(att, AXIS_ATT_COORDSATTRIB) == 0) {

        strncpytrim(axis->cname, val, CMOR_MAX_STRING);

    } else if ((strcmp(att, AXIS_ATT_BOUNDSREQUESTED) == 0)
               || (strcmp(att, AXIS_ATT_REQUESTEDBOUNDS) == 0)) {
/* -------------------------------------------------------------------- */
/*      requested values for axis                                       */
/* -------------------------------------------------------------------- */
        dim[0] = '\0';
        n = 0;
        for (i = 0; i < strlen(val); i++) {
            while (((val[i] == '[') || (val[i] == ']') || (val[i] == ' ') ||
                    (val[i] == ',') || (val[i] == '"')) && (i < strlen(val))) {
                i++;
            }
            j = i;
            while (((val[i] != ' ') && (val[i] != '\0')) && (val[i] != '"')
                   && (i < strlen(val))) {
                dim[i - j] = val[i];
                i++;

            }
            dim[i - j] = '\0';
            sscanf(dim, "%lf", &vals[n]);
            while (((val[i] == '[') || (val[i] == ']') ||
                    (val[i] == ' ') || (val[i] == ',') || (val[i] == '"')) &&
                   (val[i] != '\0')) {
                i++;
            }
            i--;
            n++;
        }
        if (axis->n_requested_bounds != 0) {
/* -------------------------------------------------------------------- */
/*      we already had some read in need to memorize it                 */
/* -------------------------------------------------------------------- */
            if (axis->requested_bounds == NULL) {

                snprintf(msg, CMOR_MAX_STRING,
                         "axis (%s, table: %s): looks like we already read "
                         "%d requested bounds but they are not stored in the "
                         "internal tables, maybe some bad cleanup",
                         axis->id, cmor_tables[axis->table_id].szTable_id,
                         axis->n_requested_bounds);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }

            tmp = malloc(axis->n_requested_bounds * sizeof(double));

            if (tmp == NULL) {

                snprintf(msg, CMOR_MAX_STRING,
                         "axis (%s, table: %s): cannot allocate memory for "
                         "%i requested bounds elts for axis %s",
                         axis->id, cmor_tables[axis->table_id].szTable_id,
                         axis->n_requested, axis->id);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }

            for (i = 0; i < axis->n_requested_bounds; i++) {
                tmp[i] = axis->requested_bounds[i];
            }

            free(axis->requested_bounds);
            axis->n_requested_bounds += n;
        } else {
            axis->n_requested_bounds = n;

        }
        axis->requested_bounds =
          malloc(axis->n_requested_bounds * sizeof(double));
        if (axis->requested_bounds == NULL) {
            snprintf(msg, CMOR_MAX_STRING,
                     "axis (%s, table: %s): cannot allocate memory for %i "
                     "requested bounds elts for axis %s",
                     axis->id, cmor_tables[axis->table_id].szTable_id,
                     axis->n_requested, axis->id);
            cmor_handle_error(msg, CMOR_CRITICAL);
        }

        for (i = 0; i < n; i++) {
            axis->requested_bounds[axis->n_requested_bounds - n + i] = vals[i];
        }
/* -------------------------------------------------------------------- */
/*      ok we had previously read in data need to add this              */
/* -------------------------------------------------------------------- */
        if (tmp != NULL) {
            for (i = 0; i < axis->n_requested_bounds - n; i++) {
                axis->requested_bounds[i] = tmp[i];
            }
            free(tmp);
            tmp = NULL;

        }
    } else if (strcmp(att, AXIS_ATT_REQUESTED) == 0) {
        if (axis->type != 'c') {
            dim[0] = '\0';
            n = 0;
            for (i = 0; i < strlen(val); i++) {

/* -------------------------------------------------------------------- */
/*      Skip double quote (")                                           */
/* -------------------------------------------------------------------- */
                while ((val[i] == '[') || (val[i] == ']') ||
                       (val[i] == '"') || (val[i] == ' ') || val[i] == ',') {
                    i++;
                }
                j = i;
                while (((val[i] != ' ') && (val[i] != '\0') && (val[i] != '"'))
                       && (i < strlen(val))) {
                    dim[i - j] = val[i];
                    i++;
                }
                dim[i - j] = '\0';
                sscanf(dim, "%lf", &vals[n]);

                while (((val[i] == '[') || (val[i] == ']') ||
                        (val[i] == ' ') || (val[i] == ',') || (val[i] == '"'))
                       && (val[i] != '\0')) {
                    i++;
                }
                i--;
                n++;
            }
/* -------------------------------------------------------------------- */
/*      ok we already had some read in                                  */
/*      need to memorize it                                             */
/* -------------------------------------------------------------------- */
            if (axis->n_requested != 0) {
                if (axis->requested == NULL) {
                    snprintf(msg, CMOR_MAX_STRING,
                             "axis (%s, table: %s): looks like we already read "
                             "%d requested values but they are not stored in "
                             "the internal tables, maybe some bad cleanup",
                             axis->id, cmor_tables[axis->table_id].szTable_id,
                             axis->n_requested);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }

                tmp = malloc(axis->n_requested * sizeof(double));

                if (tmp == NULL) {
                    snprintf(msg, CMOR_MAX_STRING,
                             "axis (%s, table: %s): cannot allocate memory for "
                             "%i requested elts for axis %s",
                             axis->id, cmor_tables[axis->table_id].szTable_id,
                             axis->n_requested, axis->id);
                    cmor_handle_error(msg, CMOR_CRITICAL);
                }
                for (i = 0; i < axis->n_requested; i++) {
                    tmp[i] = axis->requested[i];
                }
                free(axis->requested);
                axis->n_requested += n;
            } else {
                axis->n_requested = n;
            }
            axis->requested = malloc(axis->n_requested * sizeof(double));
            if (axis->requested == NULL) {
                snprintf(msg, CMOR_MAX_STRING,
                         "axis (%s, table: %s): cannot allocate memory for %i "
                         "requested elts for axis %s",
                         axis->id, cmor_tables[axis->table_id].szTable_id,
                         axis->n_requested, axis->id);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }
            for (i = 0; i < n; i++) {
                axis->requested[i] = vals[i];
            }
/* -------------------------------------------------------------------- */
/*       ok we had previously read in data need to add this             */
/* -------------------------------------------------------------------- */
            if (tmp != NULL) {
                for (i = 0; i < axis->n_requested - n; i++) {
                    axis->requested[i + n] = tmp[i];
                }
                free(tmp);
                tmp = NULL;
            }
        } else {
            axis->n_requested = 1;
            axis->crequested = malloc((strlen(val) + 1) * sizeof(char));

            if (axis->crequested == NULL) {
                snprintf(msg, CMOR_MAX_STRING,
                         "axis (%s, table: %s): cannot allocate memory "
                         "for %ld requested char elts for axis %s",
                         axis->id, cmor_tables[axis->table_id].szTable_id,
                         (long int)strlen(val) + 1, axis->id);
                cmor_handle_error(msg, CMOR_CRITICAL);
            }
            strcpy(axis->crequested, val);
        }

    } else {
        snprintf(msg, CMOR_MAX_STRING,
                 "Unknown attribute >>>%s<<< for axis section "
                 "(%s, table: %s), value: %s",
                 att, axis->id, cmor_tables[axis->table_id].szTable_id, val);
        cmor_handle_error(msg, CMOR_WARNING);
    }
    cmor_pop_traceback();
    return (0);
}
