#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "iobuffer.h"

#define ALIGN(x, a) (((x) + (a)-1) & ~((a)-1))

static int pow2gt (int x)	{
    --x;	
    x|=x>>1;	
    x|=x>>2;
    x|=x>>4;	
    x|=x>>8;	
    x|=x>>16;	
    return x+1;	
}


void iobuffer_init(iobuffer* p) {
    p->iodata   = NULL;
    p->size     = 0;
    p->capacity = 0;
}

void iobuffer_deinit(iobuffer* p) {
    free(p->iodata);
    p->iodata   = NULL;
    p->size     = 0;
    p->capacity = 0;
}

int iobuffer_reserve(iobuffer* p, int cap) {
    if (cap > p->capacity) {
        // round up
         //extra 1 byte for null-terminated char,
        int alloc = pow2gt(cap+1);

         printf(" allcate iobuffer %d=>%d\n", cap, alloc);
        // at least 16 byte align for AES encrypt
        alloc = ALIGN(alloc,16);

        assert(alloc >= cap + 1);
        assert(alloc >= p->size + 1);

        char* new_data = (char*)realloc(p->iodata, alloc);
        if (!new_data) {
            printf("can't allcate iobuffer %d=>%d\n", p->capacity, alloc);
            return ENOMEM;
        }
        p->iodata          = new_data;
        p->capacity        = alloc - 1;
        p->iodata[p->size] = '\0';
    }
    return 0;
}

int iobuffer_freesize(iobuffer* p){
    assert(p->capacity >= p->size);
    return p->capacity - p->size;
}


int iobuffer_appendData(iobuffer* p, const void* data, int count) {
    if (count <= 0)
        return EINVAL;
        
    int ret = iobuffer_reserve(p, p->size + count);
    if (ret == 0) {
        memcpy(p->iodata + p->size, data, count);
        p->size += count;
        p->iodata[p->size] = '\0';
    }
    return ret;
}

int iobuffer_append_string(iobuffer* p, const void* str)
{
    return iobuffer_appendData(p, str, strlen(str));
}

int iobuffer_prependData(iobuffer* p, const void* data, int count) {
    int ret = iobuffer_reserve(p, p->size + count);
    if (ret == 0) {
        memmove(p->iodata + count, p->iodata, p->size);
        memcpy(p->iodata, data, count);
        p->size += count;
        p->iodata[p->size] = '\0';
    }
    return ret;
}

void iobuffer_erase(iobuffer* p, int from, int to) {
    int n = to - from;
    if (n > 0) {
        if ( n != p->size ){
            memmove(p->iodata + from, p->iodata + to, p->size - to);
        }
        p->size -= n;
        p->iodata[p->size] = '\0';
    }
}

void iobuffer_eraseall(iobuffer* p)
{
    iobuffer_erase(p, 0, p->size);
}


static int ioprintf(iobuffer* this, const char *format, va_list ap)
{
    int n;
    int buflen = this->capacity - this->size;
    if ( buflen < 128) {
        if ( 0 != iobuffer_reserve(this, this->size + 1024))
            return ENOMEM;
        buflen = this->capacity - this->size;
    }

    while(1) {
        char * buf = this->iodata + this->size;
        n = vsnprintf(buf, buflen, format, ap);
        if ( n <= 0) {
            printf("vsnprintf err %d/%d,format=%s", n, errno, format);
            return -2;
        }
        if ( n > buflen) {
            if ( 0 != iobuffer_reserve(this, this->size + n + 1024))
                return ENOMEM;
            buflen = this->capacity - this->size;
        }
        else {
            this->size += n;
            return 0;
        }
    }
}

int iobuffer_printf(iobuffer* p, const char *format, ...){
	int result;
    va_list ap;
    va_start(ap, format);
    result = ioprintf(p, format, ap);
    va_end(ap);
    return result;
}

void iobuffer_read(iobuffer* this, void* to, int size)
{
    memcpy(to, this->iodata, size);
    iobuffer_erase(this, 0, size);
}
