#include "CAMCodec.hh"
#include "Asn1cEncDec.hh"

//asn1s
#include "asn1/CAM.h" // from asn1c
#include "asn1/CoopAwareness.h"
#include "asn1/BasicContainer.h"
#include "ITS_ContainerCodec.hh"


void titan2asn1c(const CAM__PDU__Descriptions::BasicContainer& t, BasicContainer_t& a)
{
	titan2asn1c(t.stationType(), a.stationType);
	titan2asn1c(t.referencePosition(), a.referencePosition);
}

CAM__PDU__Descriptions::BasicContainer asn1c2titan(const BasicContainer_t& a)
{
	return CAM__PDU__Descriptions::BasicContainer(
		asn1c2titan(a.stationType),
		asn1c2titan(a.referencePosition)
		);
}

void titan2asn1c(const CAM__PDU__Descriptions::BasicVehicleContainerHighFrequency& t, BasicVehicleContainerHighFrequency_t& a)
{
	titan2asn1c(t.heading(), a.heading);
	titan2asn1c(t.speed(), a.speed);
	titan2asn1c(t.driveDirection(), a.driveDirection);
	titan2asn1c(t.vehicleLength(), a.vehicleLength);
	titan2asn1c(t.vehicleWidth(), a.vehicleWidth);
	titan2asn1c(t.longitudinalAcceleration(), a.longitudinalAcceleration);
	titan2asn1c(t.curvature(), a.curvature);
	titan2asn1c(t.curvatureCalculationMode(), a.curvatureCalculationMode);
	titan2asn1c(t.yawRate(), a.yawRate);
	titan2asn1c_opt(t.accelerationControl(), a.accelerationControl);
	titan2asn1c_opt(t.lanePosition(), a.lanePosition);
	titan2asn1c_opt(t.steeringWheelAngle(), a.steeringWheelAngle);
	titan2asn1c_opt(t.lateralAcceleration(), a.lateralAcceleration);
	titan2asn1c_opt(t.verticalAcceleration(), a.verticalAcceleration);
	titan2asn1c_opt(t.performanceClass(), a.performanceClass);
	titan2asn1c_opt(t.cenDsrcTollingZone(), a.cenDsrcTollingZone);
}

CAM__PDU__Descriptions::BasicVehicleContainerHighFrequency asn1c2titan(const BasicVehicleContainerHighFrequency&a)
{
	return CAM__PDU__Descriptions::BasicVehicleContainerHighFrequency(
		asn1c2titan(a.heading),
		asn1c2titan(a.speed),
		asn1c2titan<ITS__Container::DriveDirection>(a.driveDirection),
		asn1c2titan(a.vehicleLength),
		asn1c2titan(a.vehicleWidth),
		asn1c2titan(a.longitudinalAcceleration),
		asn1c2titan(a.curvature),
		asn1c2titan<ITS__Container::CurvatureCalculationMode>(a.curvatureCalculationMode),
		asn1c2titan(a.yawRate),
		asn1c2titan_opt<BITSTRING>(a.accelerationControl),
		asn1c2titan_opt<INTEGER>(a.lanePosition),
		asn1c2titan_opt<ITS__Container::SteeringWheelAngle>(a.steeringWheelAngle),
		asn1c2titan_opt<ITS__Container::LateralAcceleration>(a.lateralAcceleration),
		asn1c2titan_opt<ITS__Container::VerticalAcceleration>(a.verticalAcceleration),
		asn1c2titan_opt<INTEGER>(a.performanceClass),
		asn1c2titan_opt<ITS__Container::CenDsrcTollingZone>(a.cenDsrcTollingZone)
		);
}

void titan2asn1c(const CAM__PDU__Descriptions::RSUContainerHighFrequency& t, RSUContainerHighFrequency_t& a)
{
	titan2asn1c_opt(t.protectedCommunicationZonesRSU(), a.protectedCommunicationZonesRSU);
}

CAM__PDU__Descriptions::RSUContainerHighFrequency asn1c2titan(const RSUContainerHighFrequency_t&a)
{
	return CAM__PDU__Descriptions::RSUContainerHighFrequency(
		asn1c2titan_opt<ITS__Container::ProtectedCommunicationZonesRSU>(a.protectedCommunicationZonesRSU)
		);
}

void titan2asn1c(const CAM__PDU__Descriptions::HighFrequencyContainer&t, HighFrequencyContainer_t& a)
{
	switch (t.get_selection()){
	case CAM__PDU__Descriptions::HighFrequencyContainer::ALT_basicVehicleContainerHighFrequency:
		a.present = HighFrequencyContainer_PR_basicVehicleContainerHighFrequency;
		titan2asn1c(t.basicVehicleContainerHighFrequency(), a.choice.basicVehicleContainerHighFrequency);
		break;
	case CAM__PDU__Descriptions::HighFrequencyContainer::ALT_rsuContainerHighFrequency:
		a.present = HighFrequencyContainer_PR_rsuContainerHighFrequency;
		titan2asn1c(t.rsuContainerHighFrequency(), a.choice.rsuContainerHighFrequency);
		break;
	default:
		a.present = HighFrequencyContainer_PR_NOTHING;
	}
}

const CAM__PDU__Descriptions::HighFrequencyContainer asn1c2titan(const HighFrequencyContainer_t& a)
{
	CAM__PDU__Descriptions::HighFrequencyContainer t;
	switch (a.present){
	case HighFrequencyContainer_PR_basicVehicleContainerHighFrequency:
		t.basicVehicleContainerHighFrequency() = asn1c2titan(a.choice.basicVehicleContainerHighFrequency);
		break;
	case HighFrequencyContainer_PR_rsuContainerHighFrequency:
		t.rsuContainerHighFrequency() = asn1c2titan(a.choice.rsuContainerHighFrequency);
		break;
	default:
		break;
	}
	return t;
}

void titan2asn1c(const CAM__PDU__Descriptions::BasicVehicleContainerLowFrequency&t, BasicVehicleContainerLowFrequency_t& a)
{
	titan2asn1c(t.vehicleRole(), a.vehicleRole);
	titan2asn1c(t.exteriorLights(), a.exteriorLights);
	titan2asn1c(t.pathHistory(), a.pathHistory);
}

CAM__PDU__Descriptions::BasicVehicleContainerLowFrequency asn1c2titan(const BasicVehicleContainerLowFrequency_t& a)
{
	return CAM__PDU__Descriptions::BasicVehicleContainerLowFrequency(
		asn1c2titan<ITS__Container::VehicleRole>(a.vehicleRole),
		asn1c2titan(a.exteriorLights),
		asn1c2titan(a.pathHistory)
		);
}

void titan2asn1c(const CAM__PDU__Descriptions::LowFrequencyContainer&t, LowFrequencyContainer_t& a)
{
	switch (t.get_selection()){
	case CAM__PDU__Descriptions::LowFrequencyContainer::ALT_basicVehicleContainerLowFrequency:
		a.present = LowFrequencyContainer_PR_basicVehicleContainerLowFrequency;
		titan2asn1c(t.basicVehicleContainerLowFrequency(), a.choice.basicVehicleContainerLowFrequency);
		break;
	default:
		a.present = LowFrequencyContainer_PR_NOTHING;
		break;
	}
}

CAM__PDU__Descriptions::LowFrequencyContainer asn1c2titan(const LowFrequencyContainer_t& a)
{
	CAM__PDU__Descriptions::LowFrequencyContainer t;
	switch (a.present){
	case LowFrequencyContainer_PR_basicVehicleContainerLowFrequency:
		t.basicVehicleContainerLowFrequency() = asn1c2titan(a.choice.basicVehicleContainerLowFrequency);
		break;
	default:
		break;
	}
	return t;
}

void titan2asn1c(const CAM__PDU__Descriptions::PublicTransportContainer& t, PublicTransportContainer_t& a)
{
	titan2asn1c(t.embarkationStatus(), a.embarkationStatus);
	titan2asn1c_opt(t.ptActivation(), a.ptActivation);
}
CAM__PDU__Descriptions::PublicTransportContainer asn1c2titan(const PublicTransportContainer_t& a)
{
	return CAM__PDU__Descriptions::PublicTransportContainer(
		asn1c2titan<BOOLEAN>(a.embarkationStatus),
		asn1c2titan_opt<ITS__Container::PtActivation>(a.ptActivation)
		);
}

void titan2asn1c(const CAM__PDU__Descriptions::SpecialTransportContainer& t, SpecialTransportContainer_t& a)
{
	titan2asn1c(t.specialTransportType(), a.specialTransportType);
	titan2asn1c(t.lightBarSirenInUse(), a.lightBarSirenInUse);
}
CAM__PDU__Descriptions::SpecialTransportContainer asn1c2titan(const SpecialTransportContainer_t& a)
{
	return CAM__PDU__Descriptions::SpecialTransportContainer(
		asn1c2titan(a.specialTransportType),
		asn1c2titan(a.lightBarSirenInUse)
		);
}

void titan2asn1c(const CAM__PDU__Descriptions::DangerousGoodsContainer& t, DangerousGoodsContainer_t& a)
{
	titan2asn1c(t.dangerousGoodsBasic(), a.dangerousGoodsBasic);
}

CAM__PDU__Descriptions::DangerousGoodsContainer asn1c2titan(const DangerousGoodsContainer_t& a)
{
	return CAM__PDU__Descriptions::DangerousGoodsContainer(asn1c2titan<ITS__Container::DangerousGoodsBasic>(a.dangerousGoodsBasic));
}

void titan2asn1c(const CAM__PDU__Descriptions::RoadWorksContainerBasic& t, RoadWorksContainerBasic_t& a)
{
	titan2asn1c_opt(t.roadworksSubCauseCode(), a.roadworksSubCauseCode);
	titan2asn1c(t.lightBarSirenInUse(), a.lightBarSirenInUse);
	titan2asn1c_opt(t.closedLanes(), a.closedLanes);
}

CAM__PDU__Descriptions::RoadWorksContainerBasic asn1c2titan(const RoadWorksContainerBasic_t& a)
{
	return CAM__PDU__Descriptions::RoadWorksContainerBasic(
		asn1c2titan_opt<INTEGER>(a.roadworksSubCauseCode),
		asn1c2titan(a.lightBarSirenInUse),
		asn1c2titan_opt<ITS__Container::ClosedLanes>(a.closedLanes)
		);
}

void titan2asn1c(const CAM__PDU__Descriptions::RescueContainer& t, RescueContainer_t& a)
{
	titan2asn1c(t.lightBarSirenInUse(), a.lightBarSirenInUse);
}

CAM__PDU__Descriptions::RescueContainer asn1c2titan(const RescueContainer_t& a)
{
	return CAM__PDU__Descriptions::RescueContainer(
		asn1c2titan(a.lightBarSirenInUse)
	);
}

void titan2asn1c(const CAM__PDU__Descriptions::EmergencyContainer& t, EmergencyContainer_t& a)
{
	titan2asn1c(t.lightBarSirenInUse(), a.lightBarSirenInUse);
	titan2asn1c_opt(t.incidentIndication(), a.incidentIndication);
	titan2asn1c_opt(t.emergencyPriority(), a.emergencyPriority);
}
CAM__PDU__Descriptions::EmergencyContainer asn1c2titan(const EmergencyContainer_t& a)
{
	return CAM__PDU__Descriptions::EmergencyContainer(
		asn1c2titan(a.lightBarSirenInUse),
		asn1c2titan_opt<ITS__Container::CauseCode>(a.incidentIndication),
		asn1c2titan_opt<BITSTRING>(a.emergencyPriority)
		);
}

void titan2asn1c(const CAM__PDU__Descriptions::SafetyCarContainer& t, SafetyCarContainer_t& a)
{
	titan2asn1c(t.lightBarSirenInUse(), a.lightBarSirenInUse);
	titan2asn1c_opt(t.incidentIndication(), a.incidentIndication);
	titan2asn1c_opt(t.trafficRule(), a.trafficRule);
	titan2asn1c_opt(t.speedLimit(), a.speedLimit);
}
CAM__PDU__Descriptions::SafetyCarContainer asn1c2titan(const SafetyCarContainer_t& a)
{
	return CAM__PDU__Descriptions::SafetyCarContainer(
		asn1c2titan(a.lightBarSirenInUse),
		asn1c2titan_opt<ITS__Container::CauseCode>(a.incidentIndication),
		asn1c2titan_opt<ITS__Container::TrafficRule>(a.trafficRule),
		asn1c2titan_opt<INTEGER>(a.speedLimit)
		);
}

void titan2asn1c(const CAM__PDU__Descriptions::SpecialVehicleContainer& t, SpecialVehicleContainer_t& a)
{
	switch (t.get_selection()){
	case CAM__PDU__Descriptions::SpecialVehicleContainer::ALT_publicTransportContainer:
		a.present = SpecialVehicleContainer_PR_publicTransportContainer;
		titan2asn1c(t.publicTransportContainer(), a.choice.publicTransportContainer);
		break;
	case CAM__PDU__Descriptions::SpecialVehicleContainer::ALT_specialTransportContainer:
		a.present = SpecialVehicleContainer_PR_specialTransportContainer;
		titan2asn1c(t.specialTransportContainer(), a.choice.specialTransportContainer);
		break;
	case CAM__PDU__Descriptions::SpecialVehicleContainer::ALT_dangerousGoodsContainer:
		a.present = SpecialVehicleContainer_PR_dangerousGoodsContainer;
		titan2asn1c(t.dangerousGoodsContainer(), a.choice.dangerousGoodsContainer);
		break;
	case CAM__PDU__Descriptions::SpecialVehicleContainer::ALT_roadWorksContainerBasic:
		a.present = SpecialVehicleContainer_PR_roadWorksContainerBasic;
		titan2asn1c(t.roadWorksContainerBasic(), a.choice.roadWorksContainerBasic);
		break;
	case CAM__PDU__Descriptions::SpecialVehicleContainer::ALT_rescueContainer:
		a.present = SpecialVehicleContainer_PR_rescueContainer;
		titan2asn1c(t.rescueContainer(), a.choice.rescueContainer);
		break;
	case CAM__PDU__Descriptions::SpecialVehicleContainer::ALT_emergencyContainer:
		a.present = SpecialVehicleContainer_PR_emergencyContainer;
		titan2asn1c(t.emergencyContainer(), a.choice.emergencyContainer);
		break;
	case CAM__PDU__Descriptions::SpecialVehicleContainer::ALT_safetyCarContainer:
		a.present = SpecialVehicleContainer_PR_safetyCarContainer;
		titan2asn1c(t.safetyCarContainer(), a.choice.safetyCarContainer);
		break;
	default:
		a.present = SpecialVehicleContainer_PR_NOTHING;
	}

}
CAM__PDU__Descriptions::SpecialVehicleContainer asn1c2titan(const SpecialVehicleContainer& a){
	CAM__PDU__Descriptions::SpecialVehicleContainer t;
	switch (a.present){
	case SpecialVehicleContainer_PR_publicTransportContainer:
		t.publicTransportContainer() = asn1c2titan(a.choice.publicTransportContainer);
		break;
	case SpecialVehicleContainer_PR_specialTransportContainer:
		t.specialTransportContainer() = asn1c2titan(a.choice.specialTransportContainer);
		break;
	case SpecialVehicleContainer_PR_dangerousGoodsContainer:
		t.dangerousGoodsContainer() = asn1c2titan(a.choice.dangerousGoodsContainer);
		break;
	case SpecialVehicleContainer_PR_roadWorksContainerBasic:
		t.roadWorksContainerBasic() = asn1c2titan(a.choice.roadWorksContainerBasic);
		break;
	case SpecialVehicleContainer_PR_rescueContainer:
		t.rescueContainer() = asn1c2titan(a.choice.rescueContainer);
		break;
	case SpecialVehicleContainer_PR_emergencyContainer:
		t.emergencyContainer() = asn1c2titan(a.choice.emergencyContainer);
		break;
	case SpecialVehicleContainer_PR_safetyCarContainer:
		t.safetyCarContainer() = asn1c2titan(a.choice.safetyCarContainer);
		break;
	default:
		break;
	}
	return t;
}

void titan2asn1c(const CAM__PDU__Descriptions::CamParameters& t, CamParameters_t& a)
{
	titan2asn1c(t.basicContainer(), a.basicContainer);
	titan2asn1c(t.highFrequencyContainer(), a.highFrequencyContainer);
	titan2asn1c_opt(t.lowFrequencyContainer(), a.lowFrequencyContainer);
	titan2asn1c_opt(t.specialVehicleContainer(), a.specialVehicleContainer);
}

CAM__PDU__Descriptions::CamParameters asn1c2titan(const CamParameters_t& a)
{
	return CAM__PDU__Descriptions::CamParameters(
		asn1c2titan(a.basicContainer),
		asn1c2titan(a.highFrequencyContainer),
		asn1c2titan_opt<CAM__PDU__Descriptions::LowFrequencyContainer>(a.lowFrequencyContainer),
		asn1c2titan_opt<CAM__PDU__Descriptions::SpecialVehicleContainer>(a.specialVehicleContainer)
		);
}

void titan2asn1c(const CAM__PDU__Descriptions::CoopAwareness& t, CoopAwareness_t& a)
{
	titan2asn1c(t.generationDeltaTime(), a.generationDeltaTime);
	titan2asn1c(t.camParameters(), a.camParameters);
}

CAM__PDU__Descriptions::CoopAwareness asn1c2titan(const CoopAwareness_t& a)
{
	return CAM__PDU__Descriptions::CoopAwareness(
		asn1c2titan(a.generationDeltaTime),
		asn1c2titan(a.camParameters)
		);
}


static void titan2asn1c(const CAM__PDU__Descriptions::CAM& cam, CAM_t& _cam)
{
	titan2asn1c(cam.header(), _cam.header);
	titan2asn1c(cam.cam(), _cam.cam);
}

static CAM__PDU__Descriptions::CAM asn1c2titan(const CAM_t& a)
{
	return CAM__PDU__Descriptions::CAM(
		asn1c2titan(a.header),
		asn1c2titan(a.cam)
		);
}

int CAMCodec::encode (const LibItsCam__TestSystem::CamReq& req, OCTETSTRING& data)
{
	BITSTRING b;
	int rc = asnCodec.encode(req.msgOut(), b);
	if(rc){
		data = bit2oct(b);
	}
	return rc;
}

int CAMCodec::decode (const OCTETSTRING& data, LibItsCam__TestSystem::CamInd& ind)
{
	int rc = asnCodec.decode(oct2bit(data), ind.msgIn());
	if(rc) {
		/* TODO: fill other Indication fields */
	}
	return rc;
}

int CAMPDUCodec::encode (const CAM__PDU__Descriptions::CAM& cam, BITSTRING& data)
{
	// use asn1c to encode CAM message
	CAM_t _cam;
	asn_enc_rval_t encbits;
	TTCN_Buffer buf;

	titan2asn1c(cam, _cam);

	encbits = uper_encode(
		&asn_DEF_CAM, /* Type descriptor */
		&_cam,        /* Structure to be encoded */
		asn1c_collect_encoded_data,
		&buf);
	if(encbits.encoded > 0) {
		data = BITSTRING(encbits.encoded, buf.get_data());
		return 1;
	}
	return 0;
}

int CAMPDUCodec::decode (const BITSTRING& data, CAM__PDU__Descriptions::CAM& cam)
{
	CAM_t * p_cam = NULL;
	asn_dec_rval_t rc;
	asn_codec_ctx_s ctx;
	ctx.max_stack_size = 0;

	rc = uper_decode_complete(&ctx,
		&asn_DEF_CAM,
		(void**)&p_cam,
		(const unsigned char*)data,
		(data.lengthof()+7)/8);

	if (rc.code == RC_OK){
		cam = asn1c2titan(*p_cam);
	}

	if(p_cam){
		ASN_STRUCT_FREE(asn_DEF_CAM, p_cam);
	}

	return (rc.code == RC_OK);
}
