#include "../json_helpers.c"
#include <assert.h>
#include <ccan/mem/mem.h>
#include <common/json.h>
#include <common/utils.h>
#include <inttypes.h>
#include <stdio.h>
#include <wire/wire.h>

/* AUTOGENERATED MOCKS START */
/* Generated stub for node_id_from_hexstr */
bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED)
{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */


// issue #577

static void do_json_tok_bitcoin_amount(const char* val, uint64_t expected)
{
	uint64_t amount;
	jsmntok_t tok;

	tok.start = 0;
	tok.end = strlen(val);

	fprintf(stderr, "do_json_tok_bitcoin_amount(\"%s\", %"PRIu64"): ", val, expected);

	assert(json_to_bitcoin_amount(val, &tok, &amount) == true);
	assert(amount == expected);

	fprintf(stderr, "ok\n");
}


static int test_json_tok_bitcoin_amount(void)
{
	do_json_tok_bitcoin_amount("0.00000001", 1);
	do_json_tok_bitcoin_amount("0.00000007", 7);
	do_json_tok_bitcoin_amount("0.00000008", 8);
	do_json_tok_bitcoin_amount("0.00000010", 10);
	do_json_tok_bitcoin_amount("0.12345678", 12345678);
	do_json_tok_bitcoin_amount("0.01234567", 1234567);
	do_json_tok_bitcoin_amount("123.45678900", 12345678900);

	return 0;
}

static void do_json_tok_millionths(const char *val, bool ok, uint64_t expected)
{
	uint64_t amount;
	jsmntok_t tok;

	tok.start = 0;
	tok.end = strlen(val);

	assert(json_to_millionths(val, &tok, &amount) == ok);
	if (ok)
		assert(amount == expected);
}

static int test_json_tok_millionths(void)
{
	do_json_tok_millionths("", false, 0);
	do_json_tok_millionths("0..0", false, 0);
	do_json_tok_millionths("0.0.", false, 0);
	do_json_tok_millionths(".", false, 0);
	do_json_tok_millionths("..", false, 0);

	do_json_tok_millionths("0", true, 0);
	do_json_tok_millionths(".0", true, 0);
	do_json_tok_millionths("0.", true, 0);
	do_json_tok_millionths("100", true, 100 * 1000000);
	do_json_tok_millionths("100.0", true, 100 * 1000000);
	do_json_tok_millionths("100.", true, 100 * 1000000);
	do_json_tok_millionths("100.000001", true, 100 * 1000000 + 1);
	do_json_tok_millionths("100.0000001", true, 100 * 1000000);
	do_json_tok_millionths(".000009", true, 9);
	do_json_tok_millionths(".0000099", true, 9);
	do_json_tok_millionths("18446744073709.551615", true,
			       18446744073709551615ULL);
	do_json_tok_millionths("18446744073709.551616", false, 0);
	do_json_tok_millionths("18446744073709.551625", false, 0);
	do_json_tok_millionths("18446744073709.551715", false, 0);
	do_json_tok_millionths("18446744073709.552615", false, 0);
	do_json_tok_millionths("18446744073709.561615", false, 0);
	do_json_tok_millionths("18446744073709.651615", false, 0);
	do_json_tok_millionths("18446744073710.551615", false, 0);
	do_json_tok_millionths("18446744073809.551615", false, 0);
	do_json_tok_millionths("18446744074709.551615", false, 0);
	do_json_tok_millionths("18446744083709.551615", false, 0);
	do_json_tok_millionths("18446744173709.551615", false, 0);
	do_json_tok_millionths("18446745073709.551615", false, 0);
	do_json_tok_millionths("18446754073709.551615", false, 0);
	do_json_tok_millionths("18446844073709.551615", false, 0);
	do_json_tok_millionths("18447744073709.551615", false, 0);
	do_json_tok_millionths("18456744073709.551615", false, 0);
	do_json_tok_millionths("18546744073709.551615", false, 0);
	do_json_tok_millionths("19446744073709.551615", false, 0);
	do_json_tok_millionths("28446744073709.551615", false, 0);

	return 0;
}

static void test_json_tok_size(void)
{
	jsmntok_t *toks;
	char *buf;
	bool ok, complete;
	jsmn_parser parser;

	buf = "[\"e1\", [\"e2\", \"e3\"]]";
	toks = toks_alloc(tmpctx);
	jsmn_init(&parser);
	ok = json_parse_input(&parser, &toks, buf, strlen(buf), &complete);
	assert(ok);
	assert(complete);
	/* size only counts *direct* children */
	assert(toks[0].size == 2);
	assert(toks[2].size == 2);

	buf = "[[\"e1\", \"e2\"], \"e3\"]";
	toks_reset(toks);
	jsmn_init(&parser);
	ok = json_parse_input(&parser, &toks, buf, strlen(buf), &complete);
	assert(ok);
	assert(complete);
	/* size only counts *direct* children */
	assert(toks[0].size == 2);
	assert(toks[1].size == 2);

	buf = "{\"e1\" : {\"e2\": 2, \"e3\": 3}}";
	toks_reset(toks);
	jsmn_init(&parser);
	ok = json_parse_input(&parser, &toks, buf, strlen(buf), &complete);
	assert(ok);
	assert(complete);
	/* size only counts *direct* children */
	assert(toks[0].size == 1);
	assert(toks[2].size == 2);

	buf = "{\"e1\" : {\"e2\": 2, \"e3\": 3}, \"e4\" : {\"e5\": 5, \"e6\": 6}}";
	toks_reset(toks);
	jsmn_init(&parser);
	ok = json_parse_input(&parser, &toks, buf, strlen(buf), &complete);
	assert(ok);
	assert(complete);
	/* size only counts *direct* children */
	assert(toks[0].size == 2);
	assert(toks[2].size == 2);
	assert(toks[8].size == 2);

	/* This should *not* parse! (used to give toks[0]->size == 3!) */
	buf = "{ \"\" \"\" \"\" }";
	toks_reset(toks);
	jsmn_init(&parser);
	ok = json_parse_input(&parser, &toks, buf, strlen(buf), &complete);
	assert(!ok);

	/* This should *not* parse! (used to give toks[0]->size == 2!) */
	buf = "{ 'satoshi', '546' }";
	toks = json_parse_simple(tmpctx, buf, strlen(buf));
	assert(!toks);
}

static void test_json_delve(void)
{
	const jsmntok_t *toks, *t;
	char *buf;

	buf = "{\"1\":\"one\", \"2\":\"two\", \"3\":[\"three\", {\"deeper\": 17}]}";
	toks = json_parse_simple(tmpctx, buf, strlen(buf));
	assert(toks);
	assert(toks->size == 3);

	t = json_delve(buf, toks, ".1");
	assert(t);
	assert(t->type == JSMN_STRING);
	assert(json_tok_streq(buf, t, "one"));
	assert(t == toks+2);

	t = json_delve(buf, toks, ".2");
	assert(t);
	assert(t->type == JSMN_STRING);
	assert(json_tok_streq(buf, t, "two"));
	assert(t == toks+4);

	t = json_delve(buf, toks, ".3");
	assert(t);
	assert(t->type == JSMN_ARRAY);
	assert(t == toks+6);
	assert(t->size == 2);

	t = json_delve(buf, toks, ".3[0]");
	assert(t);
	assert(t->type == JSMN_STRING);
	assert(json_tok_streq(buf, t, "three"));
	assert(t == toks+7);

	t = json_delve(buf, toks, ".3[1]");
	assert(t);
	assert(t->type == JSMN_OBJECT);
	assert(t == toks+8);
	assert(t->size == 1);

	t = json_delve(buf, toks, ".3[1].deeper");
	assert(t);
	assert(t->type == JSMN_PRIMITIVE);
	assert(memeq(buf + t->start, t->end - t->start, "17", strlen("17")));
	assert(t == toks+10);

	t = json_delve(buf, toks, ".4");
	assert(!t);
	t = json_delve(buf, toks, "[0]");
	assert(!t);
	t = json_delve(buf, toks, ".deeper");
	assert(!t);
	t = json_delve(buf, toks, ".3[2]");
	assert(!t);
	t = json_delve(buf, toks, ".3[2].deeper");
	assert(!t);
	t = json_delve(buf, toks, ".3[0].deeper");
	assert(!t);
	t = json_delve(buf, toks, ".3[1].deeper[0]");
	assert(!t);
	t = json_delve(buf, toks, ".3[1][0]");
	assert(!t);

	/* Now a real example. */
	buf = "{\n"
		"  \"jsonrpc\": \"2.0\", \n"
		"  \"method\": \"init\", \n"
		"  \"id\": 1, \n"
		"  \"params\": {\n"
		"    \"options\": {\n"
		"    }, \n"
		"    \"configuration\": {\n"
		"      \"lightning-dir\": \"/tmp/ltests-n2hyd543/test_pay_plugin_1/lightning-2/\", \n"
		"      \"rpc-file\": \"lightning-rpc\"\n"
		"    }\n"
		"  }\n"
		"}\n"
		"\n";
	toks = json_parse_simple(tmpctx, buf, strlen(buf));
	assert(toks);
	assert(toks->size == 4);

	t = json_delve(buf, toks, ".rpcfile");
	assert(!t);
	t = json_delve(buf, toks, ".configuration.rpc-file");
	assert(!t);
	t = json_delve(buf, toks, ".params.configuration");
	assert(t);
	assert(t->size == 2);
	t = json_delve(buf, toks, ".params.configuration.rpc-file");
	assert(t);
	assert(t->type == JSMN_STRING);
	assert(json_tok_streq(buf, t, "lightning-rpc"));
}

int main(void)
{
	setup_locale();
	setup_tmpctx();

	test_json_tok_size();
	test_json_tok_bitcoin_amount();
	test_json_tok_millionths();
	test_json_delve();
	assert(!taken_any());
	take_cleanup();
	tal_free(tmpctx);
}
