#include "LibItsDenm_TestSystem.hh"

#include "DENMCodec.hh"
#include "Asn1cEncDec.hh"
#include "DENM.h"

#include "loggers.hh"

int DENMCodec::encode (const DENM__PDU__Descriptions::DENM& p_denm, OCTETSTRING& p_data)
{
  loggers::get_instance().log_msg(">>> DENMCodec::encode: ", p_denm);
  
  BITSTRING b;
  int rc = asnCodec.encode(p_denm, b);
  if(rc){
    p_data = bit2oct(b);
  }
  
  loggers::get_instance().log("<<< DENMCodec::encode: %d", rc);
  return rc;
}

int DENMCodec::decode (const OCTETSTRING& p_data, DENM__PDU__Descriptions::DENM& p_denm, Params* params)
{
  loggers::get_instance().log(">>> DENMCodec::decode");
  
  int rc = asnCodec.decode(oct2bit(p_data), p_denm);
  if(rc) {
    // TODO: fill other Indication fields
  }
  
  loggers::get_instance().log("<<< DENMCodec::decode: %d", rc);
  return rc;
}

extern "C" int asn1c_collect_denm_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);
	return 0;
}

int DENMPDUCodec::encode (const DENM__PDU__Descriptions::DENM& p_denm, BITSTRING& p_data)
{
  loggers::get_instance().log_msg(">>> DENMPDUCodec::encode: ", p_denm);
  
  int rc = -1;
  // Encode as BER
  // Encode message in BER (CER variant, but can be any)
  TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING);
  TTCN_Buffer ber_buf;
  p_denm.encode(DENM__PDU__Descriptions::DENM_descr_, ber_buf, TTCN_EncDec::CT_BER, BER_ENCODE_DER);
  loggers::get_instance().log_to_hexa("DENMPDUCodec::encode: BER encoding=", ber_buf);

  // Fill DENM_t structure
  DENM_t* denm_t = (DENM_t*)calloc(1, sizeof(DENM_t));
  asn_dec_rval_t rval = ber_decode(0, &asn_DEF_DENM, (void **)&denm_t, ber_buf.get_data(), ber_buf.get_len());
  if (rval.code == RC_OK) {
    loggers::get_instance().log("DENMPDUCodec::encode: DENM_t datat strucrue filled: StationID=%lld", denm_t->header.stationID);
    // Recode DENM_t in PER
    TTCN_Buffer per_buf;
    asn_enc_rval_t erv = uper_encode(&asn_DEF_DENM, (void**)denm_t, asn1c_collect_denm_encoded_data, &per_buf);
    if (erv.encoded >= 0) {
      loggers::get_instance().log_to_hexa("DENMPDUCodec::encode: PER encoding=", per_buf);
      p_data = oct2bit(OCTETSTRING(per_buf.get_len(), per_buf.get_data()));
      rc = p_data.lengthof();
    } else {
      loggers::get_instance().warning("DENMPDUCodec::encode: BER to PER encoding failure");
    }
  } else {
    loggers::get_instance().warning("DENMPDUCodec::encode: Failed to fill DENM_t data structure");
  }
  free(denm_t);
  
  loggers::get_instance().log("<<< DENMPDUCodec::encode: %d", rc);
  return rc;
  /*  loggers::get_instance().log_msg(">>> DENMPDUCodec::encode: ", p_denm);
  
  int rc = -1;
  // Encode as BER
  // Encode message in BER (CER variant, but can be any)
  TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING);
  TTCN_Buffer ber_buf;
  ber_buf.clear();
  p_denm.encode(DENM__PDU__Descriptions::DENM_descr_, ber_buf, TTCN_EncDec::CT_BER, BER_ENCODE_DER);
  ber_buf.rewind();
  loggers::get_instance().log_to_hexa("DENMPDUCodec::encode: BER encoding=", ber_buf);

  // Recode p_data as PER
  TTCN_Buffer per_buf;
  DENM_t* denm_t = (DENM_t*)(new unsigned char[sizeof(DENM_t)]);
  if (asn1c_ber2per(asn_DEF_DENM, ber_buf, per_buf, (void**)&denm_t)) {
    loggers::get_instance().log_to_hexa("DENMPDUCodec::encode: PER encoding=", per_buf);
    p_data = oct2bit(OCTETSTRING(per_buf.get_len(), per_buf.get_data()));
    rc = p_data.lengthof();
  } else {
    loggers::get_instance().warning("DENMPDUCodec::encode: BER to PER encoding failure");
  }
  delete [] denm_t;
  
  loggers::get_instance().log("<<< DENMPDUCodec::encode: %d", rc);
  return rc;*/
}

int DENMPDUCodec::decode (const BITSTRING& p_data, DENM__PDU__Descriptions::DENM& p_denm)
{
  loggers::get_instance().log(">>> DENMPDUCodec::decode");

  int rc = -1;
  TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING);
  // Fill DENM_t structure
  DENM_t* denm_t = (DENM_t*)calloc(1, sizeof(DENM_t));
  OCTETSTRING os = bit2oct(p_data);
  asn_dec_rval_t rval = uper_decode_complete(NULL, &asn_DEF_DENM, (void **)&denm_t, static_cast<const unsigned char*>(os), os.lengthof());
  if (rval.code == RC_OK) {
    loggers::get_instance().log("DENMPDUCodec::decode: DENM_t datat strucrue filled: StationID=%lld", denm_t->header.stationID);
    // Recode DENM_t in BER/DER
    TTCN_Buffer ber_buf;
    asn_enc_rval_t erv = der_encode(&asn_DEF_DENM, (void**)denm_t, asn1c_collect_denm_encoded_data, &ber_buf);
    if (erv.encoded >= 0) {
      loggers::get_instance().log_to_hexa("DENMPDUCodec::decode: PER encoding=", ber_buf);
      p_denm.decode(DENM__PDU__Descriptions::DENM_descr_, ber_buf, TTCN_EncDec::CT_BER, BER_ACCEPT_ALL);
      rc = ber_buf.get_len() * 8;
    } else {
      loggers::get_instance().warning("DENMPDUCodec::decode: PER to BER encoding failure");
    }
  } else {
    loggers::get_instance().warning("DENMPDUCodec::decode: Failed to fill DENM_t data structure");
  }
  free(denm_t);
  
  /*int rc = -1;
  // Recode p_data to ber
  TTCN_Buffer per_buf, ber_buf;
  DENM_t* denm_t = (DENM_t*)(new unsigned char[sizeof(DENM_t)]);
  per_buf = TTCN_Buffer(bit2oct(p_data));
  loggers::get_instance().log_to_hexa("DENMPDUCodec::decode: PER=", per_buf);
  ber_buf.clear();
  if (asn1c_per2ber(asn_DEF_DENM, per_buf, ber_buf, (void**)&denm_t)) {
    loggers::get_instance().log_to_hexa("DENMPDUCodec::decode: BER=", ber_buf);
    TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING);
    ber_buf.rewind();
    p_denm.decode(DENM__PDU__Descriptions::DENM_descr_, ber_buf, TTCN_EncDec::CT_BER, BER_ACCEPT_ALL);
    rc = ber_buf.get_len() * 8;
  }
  delete [] denm_t;
  */
  loggers::get_instance().log("<<< DENMPDUCodec::decode: %d", rc);
  return rc;
}
