#include "kpacket.h"
#include "ngx_slab.h"
#include "hlog.h"
#include "crc16.h"

static ngx_slab_pool_t *slab;

TLV *TLV_init(char *buf, uint32_t cap){
   TLV *tlv = (TLV*)buf;
}

void kpacket_init(uint32_t packet_pool_size){
    slab = ngx_slab_sizes_init(packet_pool_size);
}


uint16_t kpacket_calc_checksum(kpacket *packet){
    char *p = (char*)&packet->tlv.magic;
    return crc16(p, packet->tlv.L + sizeof(packet->tlv) -sizeof(packet->tlv.checksum));
}

uint16_t kpacket_checksum(kpacket *packet){
    packet->tlv.checksum = kpacket_calc_checksum(packet);
    return packet->tlv.checksum;
}

kpacket *kpacket_slab_alloc(uint32_t size){
    if (slab) {
        kpacket * p = (kpacket *)ngx_slab_alloc(slab, size);
        return p;
    }
    hloge("%s err kpacket_init first ", __func__);
    return NULL;
}


void kpacket_slab_free(kpacket *packet){
    ngx_slab_free(slab, packet);
}


static uint32_t put_rawdata(kpacket *p, const void *data, uint32_t size){
    if (p){
		if (kpacket_get_freesize(p) >= size){
			char *payload = p->tlv.V;
			memcpy(payload + p->tlv.L, data, size);
			p->tlv.L  += size;
			return 0;
		}else{
			hloge("%s error outofmemory kpacket_get_freesize:%d, need:%u", __func__, kpacket_get_freesize(p), size);
            exit(0);
		}
	}else{
		hloge("%s error p NULL", __func__);
	}
    return 1;
}

uint32_t kpacket_put_int(kpacket *p, int32_t val){
	return put_rawdata(p, &val, sizeof(val));
}

uint32_t kpacket_put_string(kpacket *p, const char *str){
    return put_rawdata(p, str, strlen(str) + 1);
}

uint32_t kpacket_put_data(kpacket *p, const char *data, uint32_t len){
    uint32_t ret = 0;
    ret += put_rawdata(p, &len, sizeof(len)) ;
    ret += put_rawdata(p, data, len);
    if (ret){
        hloge("%s len err capacity:%u, and len:%u, tatal:%d, request:%u",
            __func__, p->capacity, p->tlv.L,  (uint32_t)(len+sizeof(len)),  (uint32_t)(p->tlv.L + len + sizeof(len) - p->capacity));
    }
	return ret;
}


uint32_t kpacket_put_data1data2(kpacket *p, const char *data, uint32_t len, const char *data2, uint32_t len2){
    uint32_t ret = 0;
    uint32_t total = len + len2;
    ret += put_rawdata(p, &total, sizeof(total));
    ret += put_rawdata(p, data, len);
    ret += put_rawdata(p, data2, len2);
    if (ret){
        hloge("%s len err capacity:%u, and len:%u, tatal:%d, need:%u",
            __func__, p->capacity, p->tlv.L,  (uint32_t)(len+sizeof(len)),  (uint32_t)(p->tlv.L + len + sizeof(len) - p->capacity));
    }
	return ret;
}

void kpacket_reset(kpacket *p){
	if (p){
		p->tlv.L = 0;
	}
}

uint32_t kpacket_get_freesize(const kpacket *p){
	return p->capacity - p->tlv.L;
}


uint32_t kpacket_pop_int(kpacket *p, const int32_t defVal){
	uint32_t val = defVal;
	if (p && (p->tlv.L - p->offset >= sizeof(val))){
		memcpy(&val, p->tlv.V + p->offset, sizeof(val));
		p->offset += sizeof(val);
	}
	return val;
}

const char *kpacket_pop_string(kpacket *p, const char *defVal){
	char *str = (char *)defVal;
	uint32_t val;
	if (p && p->tlv.L - p->offset > 1 && (val = strlen(p->tlv.V + p->offset)+1) <= p->tlv.L - p->offset){
		str = p->tlv.V + p->offset;
		p->offset += val;
	}
	return (const char *)str;
}

uint32_t kpacket_pop_data(kpacket   *p, char **data){
	uint32_t len = 0;
	if (p && p->tlv.L - p->offset > sizeof(len)){
		memcpy(&len, p->tlv.V + p->offset, sizeof(len));
        if (len + sizeof(len) <= p->tlv.L - p->offset){
			p->offset += sizeof(len);
			*data = p->tlv.V + p->offset;
			p->offset += len;
		}else{
			len = 0;
		}
	}
	return len;
}

uint32_t kpacket_pop_reset(kpacket   *p){
    if (p){
        p->offset = 0;
        return 0;
    }
    return 1;
}

void kpacket_dump(kpacket *p){
    hlogi("ref:%u capacity:%u magic:0x%X event:%u pay_loadsize:%u", p->ref, p->capacity, p->tlv.magic, p->tlv.T, p->tlv.L);
}

void kpacket_test(void){
    #define TEST_STR "123456\r\n"
    kpacket *packet = kpacket(0, 100);
    kpacket_put_int(packet, 987);
    kpacket_put_int(packet, 789);
    kpacket_put_data(packet, TEST_STR, strlen(TEST_STR));
    kpacket_put_data1data2(packet, TEST_STR, strlen(TEST_STR), TEST_STR, strlen(TEST_STR));
    kpacket_put_string(packet, TEST_STR);
    kpacket_dump(packet);
    
    char *data; 
    int len;
    hlogi("kpacket kpacket_pop_int:%d", kpacket_pop_int(packet, 0));
    hlogi("kpacket kpacket_pop_int:%d", kpacket_pop_int(packet, 0));
    len = kpacket_pop_data(packet, &data);
    hlogi("kpacket kpacket_pop_data sz:%d %s", len, data);
    len = kpacket_pop_data(packet, &data);
    hlogi("kpacket kpacket_pop_data sz:%d %s", len, data);
    hlogi("kpacket kpacket_pop_string:%s", kpacket_pop_string(packet, "err"));
    kpacket_dec(packet);
   
}

