#include "Asn1cEncDec.hh"
#include "loggers.hh"

// basic types
void        titan2asn1c(const INTEGER& t, long& a)
{
	if(t.is_native()){
		a = (int)t;
	}else{
		long long int n = t.get_long_long_val();
		if(n >= LONG_MIN && n <= LONG_MAX){
			a = (long)n;
		}else{
			// TODO: what to do here?
		}
	}
}

void        titan2asn1c(const INTEGER& t, unsigned long& a)
{
	if(t.is_native()){
		int n = (int)t;
		if(n >= 0) {
			a = (unsigned)n;
		}else{
			// TODO: what to do here?
		}
	}else{
		long long int n = t.get_long_long_val();
		if(n >= 0 && n <= (long long int)ULONG_MAX){
			a = (unsigned long)n;
		}else{
			// TODO: what to do here?
		}
	}
}

void        titan2asn1c(const INTEGER& t, INTEGER_t& a) {
	if(t.is_native()){
		asn_long2INTEGER(&a, (int)t);
	}else{
		long long int n = t.get_long_long_val();
		if(n >= LONG_MIN && n <= LONG_MAX){
			asn_long2INTEGER(&a, (long)n);
		}else if(n <= (long long int)ULONG_MAX){
			asn_ulong2INTEGER(&a, (unsigned long)n);
		}else{
			// TODO: what to do here?
		}
	}
}

INTEGER     asn1c2titan(const INTEGER_t& a) {
	long n;
	unsigned long un;
	INTEGER i;

	if( 0 == asn_INTEGER2long(&a, &n)) {
		i.set_long_long_val((long long int)n);
	}else if( 0 == asn_INTEGER2ulong(&a, &un)) {
		i.set_long_long_val((long long int)un);
	}else{
		//TODO: What to do?
	}
	return i;
}

INTEGER     asn1c2titan(long n)
{
	INTEGER i;
	i.set_long_long_val(n);
	return i;
}

INTEGER     asn1c2titan(unsigned long n)
{
	INTEGER i;
	i.set_long_long_val(n);
	return i;
}

void        titan2asn1c(const BOOLEAN& t, BOOLEAN_t& a) {
	a = ((boolean)t) ? 0xFF : 0x00;
}

BOOLEAN     asn1c2titan(const BOOLEAN_t& a) {
	return BOOLEAN(!!a);
}

void        titan2asn1c(const OCTETSTRING& t, OCTET_STRING_t& a) {
	OCTET_STRING_fromBuf(&a, (const char*)(const unsigned char*)t, t.lengthof());
}

OCTETSTRING asn1c2titan(const OCTET_STRING_t& a) {
	return OCTETSTRING(a.size, a.buf);
}

void        titan2asn1c(const BITSTRING& t, BIT_STRING_t& a) {
	int buf_size = (t.lengthof()+7)/8;
	OCTET_STRING_fromBuf((OCTET_STRING_t*)&a, (const char*)(const unsigned char*)t, buf_size);
	a.bits_unused = (buf_size*8) - t.lengthof();
}
BITSTRING   asn1c2titan(const BIT_STRING_t& a) {
	return BITSTRING(a.size*8 - a.bits_unused, a.buf);
}


extern "C" int asn1c_collect_encoded_data(const void *buffer, size_t size, void *application_specific_key)
{
	TTCN_Buffer * tb = (TTCN_Buffer *)application_specific_key;
  tb->put_s(size, (unsigned char *)buffer);
  loggers::get_instance().log_to_hexa("asn1c_collect_encoded_data: Encoding=", *tb);
	return 0;
}

// PER Encoding
#include <ber_decoder.h>
#include <der_encoder.h>
#include <per_encoder.h>
#include <per_decoder.h>

int asn1c_per2ber(asn_TYPE_descriptor_t &td, const TTCN_Buffer & per, TTCN_Buffer & ber, void** ctx )
{
	//void** obj = ctx;
	int rc = -1;
	asn_dec_rval_t rc_d;

	rc_d = uper_decode_complete(NULL, 
                              &td, ctx, 
                              per.get_read_data(), per.get_read_len());

	if(rc_d.code == RC_OK){
		asn_enc_rval_t rc_e;
		ber.clear();
		rc_e = der_encode(&td,
                      (const asn_per_constraints_t*)ctx,
                      asn1c_collect_encoded_data,
                      &ber);
		rc = rc_e.encoded;
	}
	/*if(obj) {
		ASN_STRUCT_FREE(td, obj);
    }*/
	return rc;
}

int asn1c_ber2per(asn_TYPE_descriptor_t &td, const TTCN_Buffer & ber, TTCN_Buffer & per, void** ctx )
{
	//void** obj = ctx;
	int rc = -1;
	asn_dec_rval_t rc_d;
	rc_d = ber_decode( NULL, &td,
                     ctx,
                     ber.get_data(), ber.get_len());
	if(rc_d.code == RC_OK){
    loggers::get_instance().log("asn1c_ber2per: BER decoding succeed");
		asn_enc_rval_t rc_e;
		//per.clear();
		rc_e = uper_encode(&td,
                       NULL,
                       ctx, 
                       asn1c_collect_encoded_data,
                       &per);
		rc = rc_e.encoded;
    if(rc < 0) {
      loggers::get_instance().warning("asn1c_ber2per: PER encoding failed");
    }
	} else {
    loggers::get_instance().warning("asn1c_ber2per: BER decoding failed");
    //    td.free_struct(&td, *ctx, 0);
  }
	/*if(obj) {
		ASN_STRUCT_FREE(td, obj);
    }*/
	return rc;
}

// OER Encoding
#include <xer_decoder.h>
#include <xer_encoder.h>
#include <oer_decoder.h>
#include <oer_encoder.h>

int asn1c_oer2xer(asn_TYPE_descriptor_t &td, const TTCN_Buffer & oer, TTCN_Buffer & xer, void** ctx )
{
	//void** obj = ctx;
	int rc = -1;
	asn_dec_rval_t rc_d;

	rc_d = oer_decode(NULL, 
                    &td, ctx, 
                    oer.get_read_data(), oer.get_read_len());
  
	if(rc_d.code == RC_OK){
		asn_enc_rval_t rc_e;
		xer.clear();
		rc_e = xer_encode(&td,
                      (const asn_oer_constraints_t*)ctx,
                      XER_F_BASIC,
                      asn1c_collect_encoded_data,
                      &xer);
		rc = rc_e.encoded;
	}
	/*if(obj) {
		ASN_STRUCT_FREE(td, obj);
    }*/
	return rc;
}


int asn1c_xer2oer(asn_TYPE_descriptor_t &td, const TTCN_Buffer & xer, TTCN_Buffer & oer, void** ctx )
{
	//void** obj = ctx;
	int rc = -1;
	asn_dec_rval_t rc_d;
	rc_d = xer_decode( NULL, &td,
                     ctx,
                     xer.get_data(), xer.get_len());
	if(rc_d.code == RC_OK){
    loggers::get_instance().log("asn1c_ber2per: XER decoding succeed");
		asn_enc_rval_t rc_e;
		//per.clear();
		rc_e = oer_encode(&td,
                       ctx, 
                       asn1c_collect_encoded_data,
                       &oer);
		rc = rc_e.encoded;
    if(rc < 0) {
      loggers::get_instance().warning("asn1c_ber2per: OER encoding failed");
    }
	} else {
    loggers::get_instance().warning("asn1c_ber2per: XER decoding failed");
    //    td.free_struct(&td, *ctx, 0);
  }
	/*if(obj) {
		ASN_STRUCT_FREE(td, obj);
    }*/
	return rc;
}

