#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <pbc/pbc.h>

#include "gabe.h"
#include "private.h"

void
serialize_uint32( GByteArray* b, uint32_t k )
{
	int i;
	guint8 byte;

	for( i = 3; i >= 0; i-- )
	{
		byte = (k & 0xff<<(i*8))>>(i*8);
		g_byte_array_append(b, &byte, 1);
	}
}

uint32_t
unserialize_uint32( GByteArray* b, int* offset )
{
	int i;
	uint32_t r;

	r = 0;
	for( i = 3; i >= 0; i-- )
		r |= (b->data[(*offset)++])<<(i*8);

	return r;
}

void
serialize_element( GByteArray* b, element_t e )
{
	uint32_t len;
	unsigned char* buf;

	len = element_length_in_bytes(e);
	serialize_uint32(b, len);

	buf = (unsigned char*) malloc(len);
	element_to_bytes(buf, e);
	g_byte_array_append(b, buf, len);
	free(buf);
}

void
unserialize_element( GByteArray* b, int* offset, element_t e )
{
	uint32_t len;
	unsigned char* buf;

	len = unserialize_uint32(b, offset);

	buf = (unsigned char*) malloc(len);
	memcpy(buf, b->data + *offset, len);
	*offset += len;

	element_from_bytes(e, buf);
	free(buf);
}

void
serialize_string( GByteArray* b, char* s )
{
	g_byte_array_append(b, (unsigned char*) s, strlen(s) + 1);
}

char*
unserialize_string( GByteArray* b, int* offset )
{
	GString* s;
	char* r;
	char c;

	s = g_string_sized_new(32);
	while( 1 )
	{
		c = b->data[(*offset)++];
		if( c && c != EOF )
			g_string_append_c(s, c);
		else
			break;
	}

	r = s->str;
	g_string_free(s, 0);

	return r;
}

int
gabe_pub_serialize( gabe_pub_t* pub,
                    char **data )
{
	GByteArray* b;
  int len;

	b = g_byte_array_new();
	serialize_string(b,  pub->pairing_desc);
	serialize_element(b, pub->g);
	serialize_element(b, pub->h);
	serialize_element(b, pub->gp);
	serialize_element(b, pub->g_hat_alpha);
  
  *data = g_memdup (b->data, b->len);
  len = b->len;
  g_byte_array_free (b, TRUE);
	return len;
}

gabe_pub_t*
gabe_pub_unserialize( char* data, int size )
{
	gabe_pub_t* pub;
	guint8 *g_data;
  GByteArray *b;
  int offset;

	pub = (gabe_pub_t*) malloc(sizeof(gabe_pub_t));
	offset = 0;
	g_data = g_memdup (data, size);
  b = g_byte_array_new_take (g_data, size);


	pub->pairing_desc = unserialize_string(b, &offset);
	pairing_init_set_buf(pub->p, pub->pairing_desc, strlen(pub->pairing_desc));

	element_init_G1(pub->g,           pub->p);
	element_init_G1(pub->h,           pub->p);
	element_init_G2(pub->gp,          pub->p);
	element_init_GT(pub->g_hat_alpha, pub->p);

	unserialize_element(b, &offset, pub->g);
	unserialize_element(b, &offset, pub->h);
	unserialize_element(b, &offset, pub->gp);
	unserialize_element(b, &offset, pub->g_hat_alpha);

	g_byte_array_free(b, 1);

	return pub;
}

int
gabe_msk_serialize( gabe_msk_t* msk,
                    char **data )
{
	GByteArray* b;
  int len;

	b = g_byte_array_new();
	serialize_element(b, msk->beta);
	serialize_element(b, msk->g_alpha);
  
  *data = g_memdup (b->data, b->len);
	len = b->len;
  g_byte_array_free (b, TRUE);
  return len;
}

gabe_msk_t*
gabe_msk_unserialize( gabe_pub_t* pub, char* data, int size )
{
	gabe_msk_t* msk;
	int offset;
  guint8 *g_data;
  GByteArray *b;

	msk = (gabe_msk_t*) malloc(sizeof(gabe_msk_t));
	offset = 0;

	element_init_Zr(msk->beta, pub->p);
	element_init_G2(msk->g_alpha, pub->p);
  g_data = g_memdup (data, size);
  b = g_byte_array_new_take (g_data, size);

	unserialize_element(b, &offset, msk->beta);
	unserialize_element(b, &offset, msk->g_alpha);

  g_byte_array_free(b, 1);
	return msk;
}

int
gabe_prv_serialize( gabe_prv_t* prv,
                    char **data )
{
	GByteArray* b;
	int i;
  int len;

	b = g_byte_array_new();

	serialize_element(b, prv->d);
	serialize_uint32( b, prv->comps->len);

	for( i = 0; i < prv->comps->len; i++ )
	{
		serialize_string( b, g_array_index(prv->comps, gabe_prv_comp_t, i).attr);
		serialize_element(b, g_array_index(prv->comps, gabe_prv_comp_t, i).d);
		serialize_element(b, g_array_index(prv->comps, gabe_prv_comp_t, i).dp);
	}
  len = b->len;
  *data = g_memdup (b->data, b->len);
  g_byte_array_free (b, TRUE);
	return len;
}

gabe_prv_t*
gabe_prv_unserialize( gabe_pub_t* pub, char* data, int size )
{
	gabe_prv_t* prv;
  guint8 *g_data;
  GByteArray *b;
	int i;
	int len;
	int offset;

	prv = (gabe_prv_t*) malloc(sizeof(gabe_prv_t));
	offset = 0;
  g_data = g_memdup (data, size);
  b = g_byte_array_new_take (g_data, size);


	element_init_G2(prv->d, pub->p);
	unserialize_element(b, &offset, prv->d);

	prv->comps = g_array_new(0, 1, sizeof(gabe_prv_comp_t));
	len = unserialize_uint32(b, &offset);

	for( i = 0; i < len; i++ )
	{
		gabe_prv_comp_t c;

		c.attr = unserialize_string(b, &offset);

		element_init_G2(c.d,  pub->p);
		element_init_G2(c.dp, pub->p);

		unserialize_element(b, &offset, c.d);
		unserialize_element(b, &offset, c.dp);

		g_array_append_val(prv->comps, c);
	}

	g_byte_array_free(b, 1);

	return prv;
}

void
serialize_policy( GByteArray* b, gabe_policy_t* p )
{
	int i;

	serialize_uint32(b, (uint32_t) p->k);

	serialize_uint32(b, (uint32_t) p->children->len);
	if( p->children->len == 0 )
	{
		serialize_string( b, p->attr);
		serialize_element(b, p->c);
		serialize_element(b, p->cp);
	}
	else
		for( i = 0; i < p->children->len; i++ )
			serialize_policy(b, g_ptr_array_index(p->children, i));
}

gabe_policy_t*
unserialize_policy( gabe_pub_t* pub, GByteArray* b, int* offset )
{
	int i;
	int n;
	gabe_policy_t* p;

	p = (gabe_policy_t*) malloc(sizeof(gabe_policy_t));

	p->k = (int) unserialize_uint32(b, offset);
	p->attr = 0;
	p->children = g_ptr_array_new();
  p->q = 0;

	n = unserialize_uint32(b, offset);
	if( n == 0 )
	{
		p->attr = unserialize_string(b, offset);
		element_init_G1(p->c,  pub->p);
		element_init_G1(p->cp, pub->p);
		unserialize_element(b, offset, p->c);
		unserialize_element(b, offset, p->cp);
	}
	else
		for( i = 0; i < n; i++ )
			g_ptr_array_add(p->children, unserialize_policy(pub, b, offset));

	return p;
}

int
gabe_cph_serialize( gabe_cph_t* cph,
                    char **data )
{
	GByteArray* b;
  int len;

	b = g_byte_array_new();
	serialize_element(b, cph->cs);
	serialize_element(b, cph->c);
	serialize_policy( b, cph->p);
  *data = g_memdup (b->data, b->len);
  len = b->len;
	g_byte_array_free (b, TRUE);
  return len;
}

gabe_cph_t*
gabe_cph_unserialize( gabe_pub_t* pub, char* data, int size )
{
	gabe_cph_t* cph;
	guint8 *g_data;
  GByteArray *b;
  int offset;

	cph = (gabe_cph_t*) malloc(sizeof(gabe_cph_t));
	offset = 0;
  g_data = g_memdup (data, size);
  b = g_byte_array_new_take (g_data, size);


	element_init_GT(cph->cs, pub->p);
	element_init_G1(cph->c,  pub->p);
	unserialize_element(b, &offset, cph->cs);
	unserialize_element(b, &offset, cph->c);
	cph->p = unserialize_policy(pub, b, &offset);

  g_byte_array_free(b, 1);

	return cph;
}

void
gabe_pub_free( gabe_pub_t* pub )
{
	element_clear(pub->g);
	element_clear(pub->h);
	element_clear(pub->gp);
	element_clear(pub->g_hat_alpha);
	pairing_clear(pub->p);
	free(pub->pairing_desc);
	free(pub);
}

void
gabe_msk_free( gabe_msk_t* msk )
{
	element_clear(msk->beta);
	element_clear(msk->g_alpha);
	free(msk);
}

void
gabe_prv_free( gabe_prv_t* prv )
{
	int i;
	
	element_clear(prv->d);

	for( i = 0; i < prv->comps->len; i++ )
	{
		gabe_prv_comp_t c;

		c = g_array_index(prv->comps, gabe_prv_comp_t, i);
		free(c.attr);
		element_clear(c.d);
		element_clear(c.dp);
	}

	g_array_free(prv->comps, 1);

	free(prv);
}

void
gabe_policy_free( gabe_policy_t* p )
{
	int i;

	if( p->attr )
	{
		free(p->attr);
		element_clear(p->c);
		element_clear(p->cp);
	}
  if (p->q)
  {
    for( i = 0; i < p->q->deg + 1; i++ )
      element_clear (p->q->coef[i]);
    free (p->q->coef);
    free (p->q);
  }
	for( i = 0; i < p->children->len; i++ )
		gabe_policy_free(g_ptr_array_index(p->children, i));

	g_ptr_array_free(p->children, 1);
  free(p);
}

void
gabe_cph_free( gabe_cph_t* cph )
{
	element_clear(cph->cs);
	element_clear(cph->c);
	gabe_policy_free(cph->p);
}
