#include "bio.h"
#include "errno.h"
#include "cryptlib.h"
#include "lhash.h"
#include "myfunction.h"

static int ex_data_check(void);

#define EX_IMPL(a) impl->cb_##a
#define IMPL_CHECK                                                             \
	if (!impl)                                                             \
		impl_check();
#define EX_DATA_CHECK(iffail)                                                  \
	if (!ex_data && !ex_data_check()) {                                    \
		iffail                                                         \
	}

typedef struct st_ex_class_item {
	int class_index;
	STACK_OF(CRYPTO_EX_DATA_FUNCS) * meth;
	int meth_num;
} EX_CLASS_ITEM;

typedef struct st_CRYPTO_EX_DATA_IMPL CRYPTO_EX_DATA_IMPL;

static const CRYPTO_EX_DATA_IMPL *impl = NULL;
static LHASH *ex_data		       = NULL;

struct st_CRYPTO_EX_DATA_IMPL {
	int (*cb_new_ex_data)(int class_index, void *obj, CRYPTO_EX_DATA *ad);
	void (*cb_free_ex_data)(int class_index, void *obj, CRYPTO_EX_DATA *ad);
};

///////////////////impl_default/////////////

static int int_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad);

static void int_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad);

static CRYPTO_EX_DATA_IMPL impl_default = { int_new_ex_data, int_free_ex_data };

///////////////ex_hash_cb//////////////////////ok

static unsigned long ex_hash_cb(const void *a_void)
{
	return ((const EX_CLASS_ITEM *)a_void)->class_index;
}

///////////////ex_cmp_cb/////////////////////////////ok

static int ex_cmp_cb(const void *a_void, const void *b_void)
{
	return (((const EX_CLASS_ITEM *)a_void)->class_index -
		((const EX_CLASS_ITEM *)b_void)->class_index);
}

///////////IMPL_CHECK///////////////////////ok

static void impl_check(void)
{
	//CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
	if (!impl)
		impl = &impl_default;
	//CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
}

////////////////CRYPTO_new_ex_data/////////////////ok

int CRYPTO_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
{
	IMPL_CHECK
	return EX_IMPL(new_ex_data)(class_index, obj, ad);
}

/////////////////CRYPTO_free_ex_data///////////////////////////ok

void CRYPTO_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
{
	IMPL_CHECK
	EX_IMPL(free_ex_data)(class_index, obj, ad);
}

//////////bio_set////////////////////////ok

int BIO_set(BIO *bio, BIO_METHOD *method)
{
	bio->method       = method;
	bio->callback     = NULL;
	bio->cb_arg       = NULL;
	bio->init	 = 0;
	bio->shutdown     = 1;
	bio->flags	= 0;
	bio->retry_reason = 0;
	bio->num	  = 0;
	bio->ptr	  = NULL;
	bio->prev_bio     = NULL;
	bio->next_bio     = NULL;
	bio->references   = 1;
	bio->num_read     = 0L;
	bio->num_write    = 0L;

	CRYPTO_new_ex_data(CRYPTO_EX_INDEX_BIO, bio, &bio->ex_data);
	if (method->create != NULL)
		if (!method->create(bio)) {
			CRYPTO_free_ex_data(CRYPTO_EX_INDEX_BIO, bio,
					    &bio->ex_data);
			return (0);
		}
	return (1);
}

/////////////////BI0_new//////////////ok

BIO *BIO_new(BIO_METHOD *method)
{
	BIO *ret = NULL;

	ret = (BIO *)OPENSSL_malloc(sizeof(BIO));
	if (ret == NULL) {
		return (NULL);
	}
	if (!BIO_set(ret, method)) {
		OPENSSL_free(ret);
		ret = NULL;
	}
	return (ret);
}

///////////////////def_get_class////////////////////////////////////////ok

static EX_CLASS_ITEM *def_get_class(int class_index)
{
	EX_CLASS_ITEM d, *p, *gen;

	EX_DATA_CHECK(return NULL;)
	d.class_index = class_index;
	//CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
	p = lh_retrieve(ex_data, &d);
	if (!p) {
		gen = OPENSSL_malloc(sizeof(EX_CLASS_ITEM));
		if (gen) {
			gen->class_index = class_index;
			gen->meth_num    = 0;
			gen->meth	= sk_CRYPTO_EX_DATA_FUNCS_new_null();
			if (!gen->meth)
				OPENSSL_free(gen);
			else {
				lh_insert(ex_data, gen);
				p = gen;
			}
		}
	}
	//CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
	if (!p)
		CRYPTOerr(CRYPTO_F_DEF_GET_CLASS, ERR_R_MALLOC_FAILURE);
	return p;
}

///////////////////int_new_ex_data/////////////////////////////////////////ok

static int int_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
{
	int mx, i;
	CRYPTO_EX_DATA_FUNCS **storage = NULL;

	EX_CLASS_ITEM *item = def_get_class(class_index);

	if (!item)
		/* error is already set */
		return 0;
	ad->sk = NULL;
	//CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
	mx = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth);
	if (mx > 0) {
		storage = OPENSSL_malloc(mx * sizeof(CRYPTO_EX_DATA_FUNCS *));
		if (!storage)
			goto skip;
		for (i = 0; i < mx; i++)
			storage[i] =
				sk_CRYPTO_EX_DATA_FUNCS_value(item->meth, i);
	}
skip:
	//CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
	if ((mx > 0) && !storage) {
		CRYPTOerr(CRYPTO_F_INT_NEW_EX_DATA, ERR_R_MALLOC_FAILURE);
		return 0;
	}
	for (i = 0; i < mx; i++) {
		if (storage[i] && storage[i]->new_func) {
			;
		}
	}
	if (storage)
		OPENSSL_free(storage);
	return 1;
}

////////////ex_data_check//////////////////////////////////////////ok

static int ex_data_check(void)
{
	int toret = 1;

	//CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
	if (!ex_data && ((ex_data = lh_new(ex_hash_cb, ex_cmp_cb)) == NULL))
		toret = 0;
	//CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
	return toret;
}

///////////////int_free_ex_data/////////////////////////////////////////ok

static void int_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
{
	int mx, i;
	EX_CLASS_ITEM *item;
	CRYPTO_EX_DATA_FUNCS **storage = NULL;

	if ((item = def_get_class(class_index)) == NULL)
		return;
	//CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
	mx = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth);
	if (mx > 0) {
		storage = OPENSSL_malloc(mx * sizeof(CRYPTO_EX_DATA_FUNCS *));
		if (!storage)
			goto skip;
		for (i = 0; i < mx; i++)
			storage[i] =
				sk_CRYPTO_EX_DATA_FUNCS_value(item->meth, i);
	}
skip:
	//CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
	if ((mx > 0) && !storage) {
		CRYPTOerr(CRYPTO_F_INT_FREE_EX_DATA, ERR_R_MALLOC_FAILURE);
		return;
	}
	for (i = 0; i < mx; i++) {
		if (storage[i] && storage[i]->free_func) {
			;
		}
	}
	if (storage)
		OPENSSL_free(storage);
	if (ad->sk) {
		sk_free(ad->sk);
		ad->sk = NULL;
	}
}

void reset_BIO_reset(void)
{
	impl    = NULL;
	ex_data = NULL;
}