#include "dbmail.h"
#include <libmemcache/memcache.h>
#include "dm_memcache.h"

#define MEMCACHE "Y"
#define MC_SERVER1 "127.0.0.1"
#define KVSTORE_PRFX "dbm"
#define KVSTORE_KEY_SIZE 100
#define KVSTORE_KVSTORE_MAX_KEYS 20
#define KVSTORE_MAX_VALUE 1024
#define KVSTORE_KVSTORE_VALUE_SIZE 1024
#define KVSTORE_KEY_BUFFER
#define DAY_SECONDS 86400
#define THIS_MODULE "kvstore"

#define KVSTORE_MSG_BUFFER 65535
#define KVSTORE_MAX_CHUNK_SIZE 1800
#define KVSTORE_MAX_CHUNKS 65535

// GET buffers for kvstore


struct kvstore_key_buffer{
        char **keys;
} kvstore_key_buffer;

struct kvstore_value_buffer{
        char **values;
}kvstore_value_buffer;

struct kvstore_return_buffer{
        char **returns;
}kvstore_return_buffer;

struct kvstore_proccess_args{
        int action, elements;
        struct kvstore_return_buffer *returns;
        struct kvstore_value_buffer *values;
        struct kvstore_key_buffer *keys;
} kvstore_process_args;

void kvstore_process(void *ptr);
int kvstore_check(){
	if(MEMCACHE == "Y" || MEMCACHE == "y" || MEMCACHE == "yes" || MEMCACHE == "YES")
		return 1;
	else
		return 0;
}

char* kvstore_construct_keyname(char *key, char *postfix){
        char *keyname;
        keyname = g_new0(char,KEY_SIZE); //allocate memory that is freed inkvstore_gstr and kvstore_gid

        snprintf(keyname,
                KVSTORE_KEY_SIZE,
                "%s%s_%s",
                KVSTORE_PRFX,
                key,
                postfix);
	
        return keyname;
}

char *kvstore_gstr(char *key, u64_t id){
        char *ret, *keyname, keyid[KVSTORE_KVSTORE_VALUE_SIZE];
	ret = g_new0(char,KVSTORE_KVSTORE_VALUE_SIZE);
	keyname = g_new0(char,KVSTORE_KEY_SIZE);
	memset(keyid, 0, sizeof(keyid));

        sprintf(keyid,"%ld",id);
        keyname= construct_keyname(key,keyid);
        printf("key: %s\n", keyname);
	ret = kvstore_get(keyname);
        g_free(keyname);
	return ret;
}

void kvstore_sstr(char *key, u64_t*id,char*value){
        char keyid[KVSTORE_KVSTORE_VALUE_SIZE];
	memset(keyid, 0, sizeof(keyid));

        sprintf(keyid,"%d",id);
     //   key = construct_keyname(key,keyid);
        printf("sstr: %s,%d,%s\n",key,id,value);
        kvstore_set(kvstore_construct_keyname(key,keyid),value);
//        g_free(key);
}

int kvstore_gint(char*key,char*postfix){
	char *keyuser, *ret;
        int *ret64u;
	keyuser = g_new0(char,KVSTORE_KEY_SIZE);
	ret = g_new0(char,KVSTORE_KVSTORE_VALUE_SIZE);
	ret64u = g_new0(int,4);

        keyuser = construct_keyname(key,postfix);
        ret = kvstore_get(keyuser);
        free(keyuser);
                *ret64u=atoi(ret);
	free(ret);	
        return ret64u;

}

void kvstore_sint(char*key, int id,char*value){
//	char *keyuser;
	//char *value;
//	value = (char *)malloc(MAX_VALUE*sizeof(char));
//	sprintf(value,"%d",id);
//	keyuser = construct_keyname(key,
}

u64_t kvstore_gid(char*username){
	char *ret;
        u64_t ret64u;
	ret=g_new0(char, KVSTORE_VALUE_SIZE);
	ret=kvstore_get(construct_keyname("idnr",username));
	if(ret!=NULL){
        	ret64u = atoi(ret);
//		free(ret);
		return ret64u;	
	}else{
	free(ret);
//	printf("%ld====",ret64u);
	return NULL;
}}

void kvstore_sid(char*username, u64_t *id){
	char *value;
	value=g_new0(char,KVSTORE_VALUE_SIZE);
	sprintf(value,"%d",id);
	kvstore_set(construct_keyname("idnr",username),value);
	g_free(value);
}

void kvstore_rid(char*username){
	kvstore_rm(construct_keyname("idnr",username));
}
int kvstore_intreturn(char*string){
	return atoi(string);
}

char *kvstore_charreturn(u64_t number){
	char *buffer;
	buffer = g_new0(u64_t,KVSTORE_VALUE_SIZE);
	sprintf(buffer,"%ld",(long double)number);
	return buffer;
}

char *kvstore_gencode(int id){
        return kvstore_gstr("encode",id);
}
long int kvstore_gmaxmail(int id){
	return strtol(kvstore_gstr("maxmail",id),NULL,0);
}

char* kvstore_gusername(u64_t id){
	return kvstore_gstr("username",id);
}

long int kvstore_gcurmail(int id){
	return strtol(kvstore_gstr("curmail",id),NULL,0);
}

long int *kvstore_gmaxseive(int id){
	return strtol(kvstore_gstr("maxseive",id),NULL,0);
}

u64_t kvstore_gmboxidnr(u64_t id){
	return strtol(kvstore_gstr("mboxid",id),NULL,0);
}
u64_t kvstore_gmboxowner(u64_t mbox_id){
	return (u64_t)strtol(kvstore_get(construct_keyname("mboxid",mbox_id)),NULL,0);
}

void kvstore_smboxowner(u64_t id, u64_t value){
	char*cid,*mboxid;
	cid=g_new0(char,KEY_SIZE);
	mboxid=g_new0(char,KEY_SIZE);

	sprintf(mboxid,"%s",value);
	sprintf(cid,"%s",id);

	kvstore_set(construct_keyname("mboxid",mboxid), cid);
	g_free(cid);
	g_free(mboxid);
}

void kvstore_smaxmail(int id, u64_t *value){
	kvstore_sstr("maxmail",id,kvstore_charreturn(value));
}

void kvstore_sencode(int id,char *value){
        kvstore_sstr("encode",id,value);
}
void kvstore_scurmail(u64_t id, u64_t value){
	kvstore_sstr("curmail",id,kvstore_charreturn(value));
}
void kvstore_sseive(int id, long int *value){
	kvstore_sstr("maxseive",id,kvstore_charreturn(value));
}

char* kvstore_gpasswd(int id){
	return kvstore_gstr("passwd",id);
}

void kvstore_spasswd(int id,char *value){
	kvstore_sstr("passwd",id,value);
}

void kvstore_rm(char * key){
	kvstore_single(key,NULL,0);
}

char *kvstore_single(char *key,char *value,int action){
	pthread_t process_t;
	struct kvstore_process_args *args=malloc(sizeof(*args));
	args->action=action;
	args->elements=1;
	struct kvstore_key_buffer *keybuf=malloc(sizeof(char)*KVSTORE_KEY_SIZE);
	struct kvstore_value_buffer *valuebuf;
	struct kvstore_returns_buffer *returnbuf;

	char *values,*keys;
	*keys = &keybuf->keys[0];
	snprintf(keys,KEY_SIZE,"%s",key);
	if(action == 2){
		*values=malloc((sizeof(char)*KVSTORE_MAX_CHUNK_SIZE));
		*values = &valuebuf->values[0];
	}
	if(value!=NULL)
		snprintf(values,KVSTORE_VALUE_SIZE,"%s",value);


	pthread_create(&process_t,NULL,kvstore_process,args);	
	if(args->action==1){
		pthread_join(process_t,NULL);
		*returns = malloc(sizeof(char)*KVSTORE_MAX_CHUNK_SIZE);
        	*values = &returnbuf->returns[0];
        	char ret[KVSTORE_MAX_CHUNK_SIZE];
       	 	sprintf(returns,"%s",values);
        	free(keybuf);
		free(returns);
		free(valuebuf);
        	return ret;
	}else
		return NULL;

}

void kvstore_set(char * key,char*value){
	kvstore_single(key,value,2);
}

void kvstore_msgset(char * buf,u64_t msg_id){
                char *keyname=malloc(sizeof(char)*255);
  		char *keys, *values;

		long long buf_size=(strlen(buf)*sizeof(char));
		long long buf_chunks=(buf_size / KVSTORE_CHUNK_SIZE);

		struct kvstore_key_buffer *keybuffer=malloc((sizeof(char)*KVSTORE_KEY_SIZE)*(buf_chunks));
		struct kvstore_value_buffer *valuebuffer=malloc(buf_size);
		

		long long cbuf_pos = 0, buf_pos = 0, cbuf_size=0, key_num = 1, count, msg_id, msg_size = (long long)strlen(buf);
                while(buf_pos < msg_size+1){
                    if(buf_pos % 1800 == 0&&buf_pos !=0){
                            valuebuffer->values[key_num][cbuf_pos]='\0';
                            key_num++;
                                TRACE(TRACE_DEBUG,"Making Keynumber [%llu]", key_num);

                            cbuf_pos=0;
                            valuebuffer->values[key_num][cbuf_pos]=buf[buf_pos];
                    }else{
                            valuebuffer->values[key_num][cbuf_pos]=buf[buf_pos];
                        }
                //            printf("cbuf_pos:%llu buf_pos:%llu\n",cbuf_pos,buf_pos);
                            cbuf_pos++;
                            buf_pos++;
                }
                
		*keys=&keybuffer->keys[0];
                *values=&valuebuffer->values[0];

		sprintf(keys,"%s_msg_%llu",MEMCACHE_PRFX,msg_id);
                sprintf(values,"0:%llu",num_keys);

                keys=construct_keyname(keys,count);

                for(count=0;count < num_keys;count++){
                        // msgid segment-number
                        *keys=&keybuffer->keys[count+1];
			sprintf(keys[count+1],"%s",contruct_msg_keyname(msg_id,count));
                }

                //add one for the basekey
		struct kvstore_process_args *args=malloc(sizeof(*args));
		args->action=2;
		args->elements=num_keys;
		*args->keys=&keybuffer;
		*args->values=&valuebuffer;

//              kvstore_setmulti(keys,values,num_keys+1,NULL);


}

char *kvstore_get(char *key){
	return kvstore_single(key,NULL,1);
}

/**
void kvstore_set(char * key, char * value){
	printf("keyname: %s value: %s\n",key,value);
	pthread_t process_t;
	struct kvstore_buffer *buffer=kvstore_allocate_buffer(1);
	char *keys = &buffer->keys[0][0];
	char *values = &buffer->values[0][0];
	buffer->elements = 1;
	buffer->action=2;
	snprintf(keys,KEY_SIZE,"%s",key); 
	free(key);

	snprintf(values,KVSTORE_VALUE_SIZE,"%s",value);
	pthread_create(&process_t,NULL,kvstore_process,buffer);
}


void kvstore_unread_msg_add(){
	pthread_t process_t;
	struct kvstore_buffer *buffer=kvstore_allocate_buffer(1);
	
}

void kvstore_read_msg_add(u64_t *id,u64_t * msg_id,struct * msgbuffer){

}

void kvstore_msg_add(char*keyname,char*data){

}
*/
void kvstore_setmulti(char *keys[],char * values[], int size, u64_t id){
	printf("kvstoreset multi\n");
	kvstore_multi(keys,values,id,size,2);
}
char* kvstore_getmulti(char *keys[],char * values[], u64_t id, int size){
	return	kvstore_multi(keys,values,id,size,1);
}

char * kvstore_multi(char * keys[], char * values[],u64_t id,int size,int action){
        pthread_t process_t;
	struct kvstore_key_buffer *keybuffer=malloc((sizeof(char)*KVSTORE_KEY_SIZE)*size);
	struct kvstore_value_buffer *valuebuffer=malloc((sizeof(char)*KVSTORE_MAX_CHUNK_SIZE)*size);
	struct kvstore_proccess_args *args=malloc(sizeof(*args));
	
	int a;
	char *cid,*key, *value;

	cid=g_new0(char,10);
	snprintf(cid,10,"%d",id);
        args->elements=size;
	args->action=action;
	
	printf("kvstore_multi: %d - %d\n",buffer->elements,buffer->action);
	for(a=0;a<args->elements;a++){
		key=&keybuffer->keys[a];
		value=&valuebuffer->values[a];
		if(id != NULL){
			printf(key,KEY_SIZE,"%s",construct_keyname(keys[a],cid));
        		printf("creating keynames %s\n",key);
		}
		if(args->action==2){
				snprintf(value,KVSTORE_VALUE_SIZE,"%s",values[a]);
		printf("setting values %s\n",value);
		}
	}

	g_free(cid);
	struct kvstore_return_buffer *returns;
	returns = pthread_create(&process_t,NULL,kvstore_process,args);
	if(args->action==1){
		pthread_join(process_t,NULL);
	        char ret[size][KVSTORE_VALUE_SIZE];
        	char *returns, *returns2;
		for(a=0;a<args->elements;a++){
                	returns=&returns->returns[a];
			returns2=&ret[a];
			snprintf(returns2,KVSTORE_VALUE_SIZE,"%s",returns);
		}
        	free(returns);
        	return *ret;
	}
	return NULL;
}
/**
char* kvstore_get(char * key){
	pthread_t process_t;
        struct kvstore_buffer *buffer=kvstore_allocate_buffer(1);
	buffer->elements=1;
	buffer->action=1;
	char *setkey = &buffer->keys[0][0];
	snprintf(setkey,KEY_SIZE,"%s",key);
	free(key);
	pthread_create(&process_t,NULL,kvstore_process,buffer);
	pthread_join(process_t,NULL);
	char *values = &buffer->returns[0];
	char returns[KVSTORE_VALUE_SIZE];
	sprintf(returns,"%s",values);
	free(buffer);
	return returns;	
}
**/
struct kvstore_buffer* kvstore_allocate_buffer(int size){
	return malloc(sizeof(struct kvstore_buffer));
}

struct kvstore_return_buffer *kvstore_process(void *ptr){
	uint32_t flags;
	size_t return_length;
	int a;
	struct kvstore_process_args*args=(struct kvstore_process_args*)ptr;

        struct kvstore_value_buffer *values=&args->values;
	struct kvstore_key_buffer *keys=&args->keys;
	
	struct kvstore_return_buffer *returns;
	if(args->action==1)
		*returns=malloc((sizeof(char)*KVSTORE_MAX_CHUNK_SIZE)*args->elements);


        memcache_st * memc = memcache_create(NULL);
        memcache_server_st *servers = NULL;
        memcache_return rc;
	char *key, *value;
	char *ret, *returns;
		if(args->action==1)
			ret=g_new0(char,KVSTORE_VALUE_SIZE);

        servers= memcache_server_list_append(servers,"localhost",11211, &rc);
        rc= memcache_server_push(memc, servers);
        for(a=0;a<args->elements;a++){
		key=&keys->keys[a];
		switch(args->action){
		case 0:
		  rc= memcache_delete(memc,
			key, strlen(key),
			(time_t)0);
			break;
		case 1:
		returns=&returns->returns[a];  
		ret = memcache_get(memc,
                        key,strlen(key),
                        &return_length,
                        &flags,
                        &rc);
		
                        snprintf(returns,KVSTORE_VALUE_SIZE,"%s",ret);
			break;
		case 2:
		value=&values->values[a];
		rc= memcache_set(memc,
                  	key,
                  	strlen(key),
                  	value,strlen(value),
                  	(time_t)0, (uint32_t)0);
         		break;
		
		case 3: //apendvalue to key
		rc= memcache_append(memc,
			key,
			strlen(key),
			value,strlen(value),
			(time_t)0, (uint32_t)0);
			break;
		case 4:
		rc= memcache_prepend(memc,
			key,
			strlen(key),
			value,strlen(value),
			(time_t)0, (uint32_t)0);
			break;
		
                  if(rc != MEMCACHED_SUCCESS)
                        fprintf(stderr,"Error: %s\n",memcache_strerror(memc,rc));
                
		}
	}
        	if(args->action==2||args->action==0){
			g_free(keys);
			g_free(values);
		if(args->action==1)
			g_free(ret);
        
	memcache_free(memc);

	if(args->action==1)
		return returns;
	else
		return NULL;
}

int *kvstore_count_msg_numbers(char*str, char*seperator){
	int len=strlen(str), a,occurance=0;
	for(a=0;a<len;a++){
		if(seperator==str[a])
			occurance++;
	}
	return occurance;
}

//explode taken from 
//http://pthreads.blogspot.com/2008/10/explode-function-in-c.html
char **explode(char *string, char separator, int *arraySize)
{
        int start = 0, i, k = 0, count = 2;
        char **strarr;
        for (i = 0; string[i] != '\0'; i++){
                /* Number of elements in the array */
                if (string[i] == separator){
                        count++;
                }
        }
        arraySize[0] = count-1;
        /* count is at least 2 to make room for the entire string
         * and the ending NULL */
        strarr = calloc(count, sizeof(char*));
        i = 0;
        while (*string != '\0') {
                if (*string == separator) {
                        strarr[i] = calloc(k - start + 2,sizeof(char));
                        strncpy(strarr[i], string - k + start, k - start);
                        strarr[i][k - start + 1] = '\0'; /* ensure null termination */
                        start = k;
                        start++;
                        i++;
                }
                string++;
                k++;
        }
        /* copy the last part of the string after the last separator */
        strarr[i] = calloc(k - start + 1,sizeof(char));
        strncpy(strarr[i], string - k + start, k - start);
        strarr[++i] = NULL;
 
        return strarr;
}
