#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <json.h>
#include "internal.h"


typedef struct json_object obj;

static int eq(obj* expected, obj* actual);

static int obj_eq(obj* expected, obj* actual) {
	int eql = 1;
	struct json_object_iterator it = json_object_iter_begin(expected);
	struct json_object_iterator itEnd = json_object_iter_end(expected);
	while (!json_object_iter_equal(&it, &itEnd)) {
		obj *actual_val;
		json_object_object_get_ex(actual, json_object_iter_peek_name(&it), &actual_val);
		eql &= eq(json_object_iter_peek_value(&it), actual_val);
		json_object_iter_next(&it);
	}
	return eql;
}

static int arr_eq(obj* expected, obj* actual) {
	int eql = 1;
	int expected_len = json_object_array_length(expected);
	int actual_len = json_object_array_length(actual);
	if (expected_len != actual_len) return 0;
		for (int i = 0; i < expected_len; i++) {
			obj* _exp = json_object_array_get_idx(expected, i);
			obj* act = json_object_array_get_idx(actual, i);
			eql &= eq(_exp, act);
		}
	return eql;
}

static int str_eq(obj* expected, obj* actual) {
	const char* exp_str = json_object_to_json_string(expected);
	const char* act_str = json_object_to_json_string(actual);
	return strcmp(exp_str, act_str) == 0;
}

static int eq(obj* expected, obj* actual) {
	if (expected == NULL && actual == NULL) {
		return 1;
	} else if (expected == NULL) {
		return 0;
	} else if (actual == NULL) {
		return 0;
	}
	enum json_type expected_type = json_object_get_type(expected);
	enum json_type actual_type = json_object_get_type(actual);
	if (expected_type != actual_type) return 0;
	switch(expected_type) {
	case json_type_null:
		return 1;
	case json_type_boolean:
		return json_object_get_boolean(expected) == json_object_get_boolean(actual);
	case json_type_double:
		return (fabs(json_object_get_double(expected) - json_object_get_double(actual)) < 0.001);
	case json_type_int:
		return json_object_get_int64(expected) == json_object_get_int64(actual);
	case json_type_object:
		return obj_eq(expected, actual);
	case json_type_array:
		return arr_eq(expected, actual);
	case json_type_string:
		return str_eq(expected, actual);
	default:
		fprintf(stderr, "unexpected type in %s:%d\n", __FILE__, __LINE__);
		abort();
	}
	return 0;
}

int main(int argc, char** argv) {
	if (argc != 3) {
		fprintf(stderr, "expected and actual json not given, number of args was: %d\n", argc);
		exit(100);
	}
	obj* expected = json_tokener_parse(argv[1]);
	obj* actual = json_tokener_parse(argv[2]);
	int result = eq(expected, actual) ? 0 : 1;
	json_object_put(expected);
	json_object_put(actual);
	if (result != 0) {
		printf("JSONs weren't equal. \n\tExpected: \n\t\t%s\n\tActual: \n\t\t%s\n", argv[1], argv[2]);
	}
	return result;
}
