#include "mars.h"
#include "mars_client_config.h"

boolean message_is_netcdf(unsigned char* buf) {
    if (buf != NULL) {
        static const unsigned long netcdf[3] = {0x89484446,    // ".HDF"
                                                0x43444601,    // "CDF."
                                                0x43444602 };  // "CDF."

        unsigned long magic = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
        return magic == netcdf[0] || magic == netcdf[1] || magic == netcdf[2];
    }

    return 0;
}


#if mars_client_HAVE_NETCDF

#include <netcdf.h>

static const char* get_type(int type) {

    switch (type) {

        case NC_BYTE:
            return "NC_BYTE";
            break;

        case NC_SHORT:
            return "NC_SHORT";
            break;

        case NC_LONG:
            return "NC_LONG";
            break;

        case NC_CHAR:
            return "NC_CHAR";
            break;

        case NC_FLOAT:
            return "NC_FLOAT";
            break;

        case NC_DOUBLE:
            return "NC_DOUBLE";
            break;

        default:
            marslog(LOG_EROR, "Unknow attribute type %d", type);
            return "Unknow";
    }
}


static boolean file_is_netcdf(const char* path) {
    /* See https://www.unidata.ucar.edu/software/netcdf/docs/netcdf/Classic-Format-Spec.html#Classic-Format-Spec */
    FILE* f = fopen(path, "r");
    unsigned char buf[8];

    if (f == NULL) {
        marslog(LOG_EROR | LOG_PERR, "Cannot open '%s'", path);
        return false;
    }

    if (fread(buf, 1, 4, f) != 4) {
        marslog(LOG_WARN, "file_is_netcdf: Cannot read enough bytes from %s", path);
        fclose(f);
        return false;
    }

    /* http://www.hdfgroup.org/HDF5/doc/H5.format.html#FileMetaData */

    if (buf[0] == 137 && buf[1] == 'H' && buf[2] == 'D' && buf[3] == 'F') {

        if (fread(buf + 4, 1, 4, f) != 4) {
            marslog(LOG_WARN, "file_is_netcdf: Cannot read enough bytes from %s", path);
            fclose(f);
            return false;
        }
        if (buf[4] == '\r' && buf[5] == '\n' && buf[6] == 26 && buf[7] == '\n') {
            marslog(LOG_DBUG, "%s is a netcdf-4 (HDF)", path);
            fclose(f);
            return true;
        }

        marslog(LOG_WARN, "%s looks like HDF", path);
    }


    fclose(f);

    if (buf[0] == 'C' && buf[1] == 'D' && buf[2] == 'F') {

        switch (buf[3]) {
            case 1:
                /* Classic format */
                marslog(LOG_DBUG, "%s is a netcdf-3 (32 bits)", path);
                return true;
                break;

            case 2:
                /* Classic format */
                marslog(LOG_DBUG, "%s is a netcdf-3 (64 bits)", path);
                return true;
                break;

            default:
                marslog(LOG_EROR, "%s: invalid netcdf version (%ld)", path, (long)buf[3]);
                return false;
                break;
        }
    }

    return false;
}

boolean source_is_netcdf(const request* r) {
    int i = 0;
    const char* path;
    boolean ok = true;

    marslog(LOG_DBUG, "Checking is source is NETCDF");

    while ((path = get_value(r, "SOURCE", i++)) != NULL) {
        path       = no_quotes(path);
        boolean nc = file_is_netcdf(path);

        if (nc) {
            /* For now, only hdf5 */
            if (check_hdf5_superblock(path) != 0) {
                marslog(LOG_EXIT, "Open NETCDF-4 data with HDF5 Super Block version 2 supported");
            }
        }
        // marslog(LOG_DBUG, "%s is %sNetCDF", path, nc ? "": "not ");
        if (i > 1) {
            if (ok != nc) {
                marslog(LOG_EXIT, "Cannot support a mix of netcdf/non-netcdf files");
            }
        }
        else {
            ok = nc;
        }
    }

    if (ok)
        marslog(LOG_DBUG, "Source is NETCDF");
    else
        marslog(LOG_DBUG, "Source is not NETCDF");

    return ok;
}


request* netcdf_to_request(const char* path, int merge, mars_field_index* idx, err* e) {
    err ret = 0;
    request* r;

    netcdf_schema* s = netcdf_schema_new(path);
    r                = netcdf_schema_to_request(s, merge, idx, e);
    netcdf_schema_delete(s);

    return r;
}


#else

static const char* get_type(int type) {
    marslog(LOG_EROR, "Attempt to get_type() NETCDF, NETCDF support not enabled");
    return 0;
}

static boolean file_is_netcdf(const char* path) {
    marslog(LOG_EROR, "Attempt to access NETCDF file %s, NETCDF support not enabled", path);
    return -2;
}

boolean source_is_netcdf(const request* r) {
    const char* path = get_value(r, "SOURCE", 0);
    marslog(LOG_EROR, "Attempt to access NETCDF file %s, NETCDF support not enabled", path);
    return false;
}

request* netcdf_to_request(const char* path, int merge, mars_field_index* idx, err* e) {
    marslog(LOG_EROR, "Attempt to access NETCDF file %s, NETCDF support not enabled", path);
    return NULL;
}

#endif
