#include "mars.h"
#include "ncmerge.h"


static boolean netcdf_same_attribute(netcdf_attribute *a, netcdf_attribute *b) {

    if (a->cleared || b->cleared) {
        return false;
    }

    if (a->name == b->name) {
        if (a->type == b->type) {

            if (a->long_value != b->long_value) {
                return false;
            }

            if (a->char_value != b->char_value) {
                return false;
            }

            if (a->float_value != b->float_value) {
                return false;
            }

            if (a->double_value != b->double_value) {
                return false;
            }
            return true;
        }
        else {
            marslog(LOG_WARN, "Attributes %s:%s and %s:%s have different types (%s and %s), currently considered as a mismatch.",
                    a->owner, a->name, b->owner, b->name, netcdf_type_name(a->type), netcdf_type_name(b->type) );
            return false;
        }
    }
    return false;
}

static boolean netcdf_same_dimension(netcdf_dimension *a, netcdf_dimension *b) {

    if (a->cleared || b->cleared) {
        return false;
    }
    return a->name == b->name;
}

static boolean netcdf_same_variable(netcdf_variable *a, netcdf_variable *b)
{
    if (a->cleared || b->cleared) {
        return false;
    }
    return a->name == b->name;
}

static err netcdf_attribute_merge(netcdf_attribute_list *result, netcdf_attribute_list *list) {
    netcdf_attribute *c = list->first;
    int e;

    while (c) {
        netcdf_attribute *match = NULL;
        netcdf_attribute *r = result->first;

        while (r) {
            if (r->name == c->name) {
                match = r;
                break;
            }
            r = r->next;
        }

        if (match) {
            if (netcdf_same_attribute(c, match)) {

            }
            else {
                if (!match->cleared) {
                    marslog(LOG_WARN, "Discarding attribute %s:%s", c->owner, c->name);
                    match->cleared = true;
                }
            }
        }
        else
        {
            netcdf_attribute_clone(result, c);
        }

        c = c->next;
    }

    return 0;
}

static err netcdf_dimension_merge(netcdf_field *target, netcdf_dimension_list *result, netcdf_field *source, netcdf_dimension_list *list) {
    netcdf_dimension *c = list->first;
    int e;
    netcdf_variable* variables[20140];
    size_t count = 0;
    int i;

    while (c) {
        netcdf_dimension *match = NULL;
        netcdf_dimension *r = result->first;

        while (r) {
            if (r->name == c->name) {
                match = r;
                break;
            }
            r = r->next;
        }

        if (match) {

            marslog(LOG_INFO, "Dimension %s matches", match->name);

            count = NUMBER(variables);
            netcdf_variable_by_dimension(&source->variables, c, variables, &count);
            for(i = 0; i < count; i++) {
                marslog(LOG_INFO,"        var: %s", variables[i]->name);
            }


        }
        else
        {
            netcdf_dimension_clone(target, result, c);
        }

        c = c->next;
    }

    return 0;
}

static err netcdf_variable_merge_content(netcdf_variable *target, netcdf_variable *source) {
    int e = 0;
    int i;
    netcdf_variable *dim_var;

     if (target->type != source->type) {
        marslog(LOG_WARN, "netcdf_variable_merge_content: target type is %ld, source type is %ld", netcdf_type_name(target->type), netcdf_type_name(source->type));
        return -2;
    }


    printf("netcdf_variable_merge_content source %s (%ld) [", source->name, source->cube.ndims);
    for (i = 0; i < source->cube.ndims; i++) {
        netcdf_dimension *d = source->cube.dims[i];
        if (i) printf(", ");
        printf(" %s(%d)", d->name, d->len);
    }
    printf("]\n");

    printf("netcdf_variable_merge_content target %s [", target->name);
    for (i = 0; i < target->cube.ndims; i++) {
        netcdf_dimension *d = target->cube.dims[i];
        if (i) printf(", ");
        printf(" %s(%d)", d->name, d->len);
    }
    printf("]\n");


    size_t target_value_count = netcdf_variable_number_of_values(target);
    size_t source_value_count = netcdf_variable_number_of_values(source);

    if (target_value_count != source_value_count) {
        marslog(LOG_WARN, "netcdf_variable_merge_content: target value count is %ld, source value count is %ld", target_value_count, source_value_count);
        return -2;
    }

    if (target->type != source->type) {
        marslog(LOG_WARN, "netcdf_variable_merge_content: target type is %ld, source type is %ld", netcdf_type_name(target->type), netcdf_type_name(source->type));
        return -2;
    }

    void *source_values = netcdf_variable_get_values(source, &source_value_count);
    void *target_values = netcdf_variable_get_values(target, &target_value_count);

    if (memcmp(source_values, target_values, source_value_count * netcdf_type_size(source->type)) != 0) {
        marslog(LOG_WARN, "netcdf_variable_merge_content: content change for variable %s", target->name);
        marslog(LOG_WARN, "============================================== ==============================================");



        // netcdf_attribute *source_coordinates = netcdf_attribute_by_name(&source->attributes, COORDINATES);
        // netcdf_attribute *target_coordinates = netcdf_attribute_by_name(&target->attributes, COORDINATES);

        // if (source_coordinates != NULL) {
        //     marslog(LOG_INFO, "Variable %s has coordinates [%s]", source->name, source_coordinates->char_value );
        // }
        // else {
        //     marslog(LOG_INFO, "Variable %s does not have coordinates", target->name);
        //     for (i = 0; i < source->cube.ndims; i++) {
        //         netcdf_dimension *d = source->cube.dims[i];
        //         if (i) printf(", ");
        //         printf(" %s(%d)", d->name, d->len);
        //     }
        // }
    }

    FREE(source_values);
    FREE(target_values);

    return 0;
}

static err netcdf_variable_merge(netcdf_field *target, netcdf_variable_list *result, netcdf_variable_list *list) {

    netcdf_variable *c = list->first;
    int e;

    while (c) {
        netcdf_variable *match = NULL;
        netcdf_variable *r = result->first;

        while (r) {
            if (r->name == c->name) {
                match = r;
                break;
            }
            r = r->next;
        }

        if (match) {
            if (netcdf_same_variable(c, match)) {
                if ( (e = netcdf_attribute_merge(&match->attributes, &c->attributes)) != 0) {
                    return e;
                }
                if ( (e = netcdf_variable_merge_content(match, c)) != 0) {
                    return e;
                }

            }
            else {
                if (!match->cleared) {
                    marslog(LOG_WARN, "Discarding variable %s", c->name);
                    match->cleared = true;
                }
            }
        }
        else
        {
            netcdf_variable_clone(target, result, c);
        }

        // if( (e = netcdf_attribute_merge(&target->global_attributes, &c->global_attributes)) != 0) {
        //     return e;
        // }

        // netcdf_variable_merge(&c->variables, depth + 1);
        // netcdf_dimension_merge(&c->dimensions, depth + 1);
        c = c->next;
    }

    return 0;
}


static err netcdf_field_merge(const char *target_path, netcdf_field_list *result, netcdf_field_list *list) {

    netcdf_field *target = netcdf_field_new(result, NULL /*target_path*/, 0);
    netcdf_field *c = list->first;
    int e;

    while (c) {

        if ( (e = netcdf_attribute_merge(&target->global_attributes, &c->global_attributes)) != 0) {
            return e;
        }

        if ( (e = netcdf_dimension_merge(target, &target->dimensions, c, &c->dimensions)) != 0) {
            return e;
        }

        if ( (e = netcdf_variable_merge(target, &target->variables, &c->variables)) != 0) {
            return e;
        }
        c = c->next;
    }

    return 0;
}

err netcdf_merge(const char *target, netcdf_field_list *result, netcdf_field_list *fields)
{
    /* Only one field */
    if (fields->first == fields->last) {

        result->first = result->last = fields->first;
        fields->first = fields->last = NULL;
        return 0;
    }

    return netcdf_field_merge(target, result, fields);
}
