#include "tfe_rpc.h"
#include "tfe_utils.h"
#include "MESA/MESA_prof_load.h"
#include "MESA/MESA_handle_logger.h"
#include "string.h"
#include "cjson/cJSON.h"
#include "ssl_utils.h"
#include "key_keeper.h"
#include <assert.h>
struct keyring_private
{
	struct keyring head;
	pthread_mutex_t mutex;
	size_t references;
};

static struct keyring_private* keyring_new(void)
{
	struct keyring_private *kyr;
	if (!(kyr = (struct keyring_private *)ALLOC(struct keyring_private, 1)))
	{
		return NULL;
	}
	if (pthread_mutex_init(&kyr->mutex, NULL)) {
		free(kyr);
		return NULL;
	}
	kyr->references = 1;
	return kyr;
}

/*
 * Thread-safe setter functions; they copy the value (refcounts are inc'd).
 */
static void keyring_set_key(struct keyring_private* kyr, EVP_PKEY *key)
{
	pthread_mutex_lock(&kyr->mutex);
	if ((kyr->head).key)
	{
		EVP_PKEY_free((kyr->head).key);
	}
	(kyr->head).key = key;
	if (key)
	{
		ssl_key_refcount_inc((kyr->head).key);
	}
	pthread_mutex_unlock(&kyr->mutex);
}

static void keyring_set_cert(struct keyring_private* kry, X509 *cert)
{
	pthread_mutex_lock(&kry->mutex);
	if (kry->head.cert)
	{
		X509_free((kry->head).cert);
	}
	kry->head.cert = cert;
	if (cert)
	{
		ssl_x509_refcount_inc((kry->head).cert);
	}
	pthread_mutex_unlock(&kry->mutex);
}

static void keyring_set_chain(struct keyring_private* kyr, STACK_OF(X509) *chain)
{
	pthread_mutex_lock(&(kyr->mutex));
	if (kyr->head.chain)
	{
		sk_X509_pop_free(kyr->head.chain, X509_free);
	}
	if (chain)
	{
		kyr->head.chain = sk_X509_dup(chain);
		int i = 0;
        for (i = 0; i < sk_X509_num(kyr->head.chain); i++)
		{
			ssl_x509_refcount_inc(sk_X509_value(kyr->head.chain, i));
		}
	} else
	{
		kyr->head.chain = NULL;
	}
	pthread_mutex_unlock(&(kyr->mutex));
}

static X509* transform_cert_to_x509(const char* str)
{
	BIO *bio;
	X509 *cert;
	bio = BIO_new(BIO_s_mem());
	BIO_write(bio, (const void*)str, strnlen(str, TFE_STRING_MAX));
	cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
	return cert;
}

static EVP_PKEY* transform_key_to_EVP(const char* str)
{
	BIO *mem;
	mem = BIO_new_mem_buf(str, -1);
	EVP_PKEY* key = PEM_read_bio_PrivateKey(mem, NULL, NULL, 0);
	return key;
}


static void err_out(X509* cert, EVP_PKEY* key, STACK_OF(X509)* chain)
{
	if(cert)
	{
		X509_free(cert);
	}
	if(key)
	{
		EVP_PKEY_free(key);
	}
	if(chain)
	{
		sk_X509_pop_free(chain, X509_free);
	}
	return;
}

static struct keyring* get_keyring_from_response(const char* data)
{
	X509* cert = NULL;
	EVP_PKEY* key = NULL;
	STACK_OF(X509)* chain = NULL;
	if(data == NULL)
	{
		return NULL;
	}
	cJSON* data_json = cJSON_Parse(data);
	if(data_json == NULL)
	{
		return NULL;
	}
	cJSON* cert_json = NULL;
	cJSON* key_json = NULL;
	cJSON* chain_json = NULL;
	cert_json = cJSON_GetObjectItemCaseSensitive(data_json, "CERTIFICATE");
	key_json = cJSON_GetObjectItemCaseSensitive(data_json, "PRIVATE_KEY");
	chain_json = cJSON_GetObjectItemCaseSensitive(data_json, "CERTIFICATE_CHAIN");
	if (cert_json && cert_json->valuestring != NULL)
	{
		cert = transform_cert_to_x509(cert_json->valuestring);
	}
	if(cert == NULL)
	{
		err_out(cert, key, chain);
		return NULL;
	}
	if (key_json && key_json->valuestring != NULL)
	{
		key = transform_key_to_EVP(key_json->valuestring);
	}
	if(key == NULL)
	{
		err_out(cert, key, chain);
		return NULL;
	}
	if(chain_json == NULL)
	{
		err_out(cert, key, chain);
		return NULL;
	}
	cJSON* chain_cert_json = NULL;
	chain = sk_X509_new_null();
	cJSON_ArrayForEach(chain_cert_json, chain_json)
	{
		X509* chain_cert = NULL;
		if (chain_cert_json && chain_cert_json->valuestring != NULL)
		{
			chain_cert = transform_cert_to_x509(chain_cert_json->valuestring);
		}
		if(chain_cert == NULL)
		{
			err_out(cert, key, chain);
			return NULL;
		}
		if(chain_cert)
			printf("push to chain\n");
		sk_X509_push(chain, chain_cert);
		ssl_x509_refcount_inc(chain_cert);
	}
	struct keyring_private* _kyr= keyring_new();
	printf("cert is %s", cert == NULL ? "null" : "not null\n");
	printf("key is %s", key == NULL ? "null" : "not null\n");
	printf("chain is %s", chain == NULL ? "null" : "not null\n");
	keyring_set_cert(_kyr, cert);
	keyring_set_key(_kyr, key);
	keyring_set_chain(_kyr, chain);
	X509_free(cert);
	EVP_PKEY_free(key);
	sk_X509_pop_free(chain, X509_free);
	return &(_kyr->head);
}

static void tfe_rpc_on_succ(void* result, void* user)
{
	struct tfe_rpc_response_result* response = tfe_rpc_release(result);
	int status_code = response->status_code;
	const char* status_msg = response->status_msg;
	char* data = (char*)response->data;
	int len = response->len;
	*(data+len) = '\0';
	printf("status_code is %d\n", status_code);
	printf("status_msg is %s\n", status_msg);
	//printf("data is %s\n", data);
	printf("len is %d\n", len);
	struct keyring* kyr = get_keyring_from_response(data);
	assert(kyr!=NULL);
	(void)kyr;
	//add to hash table
}


static void tfe_rpc_on_fail(enum e_future_error err, const char * what, void * user){
	printf("err is %d\n", err);
	printf("what is %s\n", what);
	printf("user is %s\n", (char*)user);
}



int main()
{
	char cert_store_host[TFE_STRING_MAX];
	unsigned int cert_store_port;
	future_promise_library_init(NULL);
	//const char* file_path = "./log/test_tfe_rpc.log",*host="localhost";
	//void * logger = MESA_create_runtime_log_handle(file_path, RLOG_LV_INFO);
	const char* profile = "./conf/tfe.conf";
	const char* section = "key_keeper";
	int keyring_id = 0;
	MESA_load_profile_string_def(profile, section, "cert_store_host", cert_store_host, sizeof(cert_store_host), "xxxxx");
	MESA_load_profile_uint_def(profile, section, "cert_store_port", &cert_store_port, 80);
	struct event_base* evbase = event_base_new();
	struct evdns_base* dnsbase=evdns_base_new(evbase, EVDNS_BASE_INITIALIZE_NAMESERVERS);
	struct future* f_tfe_rpc = future_create("tfe_rpc", tfe_rpc_on_succ, tfe_rpc_on_fail, NULL);
	char url[TFE_STRING_MAX];
	char sni[TFE_STRING_MAX] = "www.baidu.com";
	snprintf(url, TFE_STRING_MAX, "http://%s:%d/ca?host=%s&flag=1&valid=1&kering_id=%d", cert_store_host, cert_store_port, sni, keyring_id);
	printf("url is %s\n", url);
	tfe_rpc_async_ask(f_tfe_rpc, url, GET, DONE_CB, NULL, 0, evbase, dnsbase);
	event_base_dispatch(evbase);
}

