#include "jsoncdaccord.h"
#include "internal.h"
#include "optional.h"

enum subschematype {
    JDAC_ALLOF = 0,
    JDAC_ANYOF,
    JDAC_ONEOF
};

// shall return valid or invalid based on subschema type
int _jdac_test_subschema_array(json_object *jobj, json_object *jsubschema_array, enum subschematype type) {
    if (jsubschema_array == NULL) {
        return JDAC_ERR_VALID;
    }

    // MUST be a non-empty array
    if (!json_object_is_type(jsubschema_array, json_type_array)) {
        return JDAC_ERR_SCHEMA_ERROR;
    }

    int arraylen = (int)json_object_array_length(jsubschema_array);
    if (arraylen == 0) {
        return JDAC_ERR_SCHEMA_ERROR;
    }

    int number_of_valid_schemas = 0;

    for (int i = 0; i < arraylen; i++) {
        json_object *jsubschema = json_object_array_get_idx(jsubschema_array, i);
        if (!json_object_is_type(jsubschema, json_type_object) && !json_object_is_type(jsubschema, json_type_boolean)) {
            return JDAC_ERR_SCHEMA_ERROR;
        }

        int err = _jdac_validate_instance(jobj, jsubschema);

        if (err == JDAC_ERR_VALID) {
            number_of_valid_schemas++;
        } else if (err == JDAC_ERR_SCHEMA_ERROR) {
            return JDAC_ERR_SCHEMA_ERROR;
        } else {
            // continue
        }
    }

    if (type == JDAC_ALLOF) {
        if (number_of_valid_schemas == arraylen) {
            return JDAC_ERR_VALID;
        }
    } else if (type == JDAC_ANYOF) {
        if (number_of_valid_schemas > 0) {
            return JDAC_ERR_VALID;
        }
    } else if (type == JDAC_ONEOF) {
        if (number_of_valid_schemas == 1) {
            return JDAC_ERR_VALID;
        }
    }
    return JDAC_ERR_INVALID_SUBSCHEMALOGIC;
}

int _jdac_check_subschemalogic(json_object *jobj, json_object *jschema) {
    int          err;
    json_object *jarray;

    jarray = json_object_object_get(jschema, "allOf");
    err    = _jdac_test_subschema_array(jobj, jarray, JDAC_ALLOF);
    if (err) {
        return JDAC_ERR_INVALID_SUBSCHEMALOGIC;
    }

    jarray = json_object_object_get(jschema, "anyOf");
    err    = _jdac_test_subschema_array(jobj, jarray, JDAC_ANYOF);
    if (err) {
        return JDAC_ERR_INVALID_SUBSCHEMALOGIC;
    }

    jarray = json_object_object_get(jschema, "oneOf");
    err    = _jdac_test_subschema_array(jobj, jarray, JDAC_ONEOF);
    if (err) {
        return JDAC_ERR_INVALID_SUBSCHEMALOGIC;
    }

    json_object *jnot = json_object_object_get(jschema, "not");
    if (jnot) {
        // "not" is special, and MUST be a json object
        if (json_object_is_type(jnot, json_type_object) || json_object_is_type(jnot, json_type_boolean)) {
            err = _jdac_validate_instance(jobj, jnot);
            if (err == JDAC_ERR_VALID) {
                return JDAC_ERR_INVALID_SUBSCHEMALOGIC;
            } else if (err == JDAC_ERR_SCHEMA_ERROR) {
                return JDAC_ERR_SCHEMA_ERROR;
            } else {
                return JDAC_ERR_VALID;
            }
        } else {
            return JDAC_ERR_SCHEMA_ERROR;
        }
    }

    json_object *if_schema   = json_object_object_get(jschema, "if");
    json_object *then_schema = json_object_object_get(jschema, "then");
    json_object *else_schema = json_object_object_get(jschema, "else");
    if (if_schema) {
        err = _jdac_validate_instance(jobj, if_schema);
        if (err == JDAC_ERR_VALID && then_schema) {
            err = _jdac_validate_instance(jobj, then_schema);
            return err;
        } else if (err != JDAC_ERR_VALID && else_schema) {
            err = _jdac_validate_instance(jobj, else_schema);
            return err;
        }
    }

    return JDAC_ERR_VALID;
}