#include "NGSetupResponse.hpp"

extern "C"{
  #include "constr_TYPE.h"
  #include "asn_codecs.h"
  #include "per_encoder.h"
  #include "per_decoder.h"
  #include "constraints.h"
}

#include <iostream>
using namespace std;

namespace ngap{
	NGSetupResponseMsg::NGSetupResponseMsg()
	{
		ngSetupResponsePdu = NULL;
		ngSetupResponsIEs = NULL;
		amfName = NULL;
		servedGUAMIList = NULL;
		relativeAmfCapacity = NULL;
		plmnSupportList = NULL;
		//criticalityDiagnostics = NULL;
	}
	NGSetupResponseMsg::~NGSetupResponseMsg(){}

	void NGSetupResponseMsg::setMessageType()
	{
		if(!ngSetupResponsePdu) ngSetupResponsePdu = (Ngap_NGAP_PDU_t*)calloc(1, sizeof(Ngap_NGAP_PDU_t));

		MessageType NgSetupResponseMessageTypeIE;
		NgSetupResponseMessageTypeIE.setProcedureCode(Ngap_ProcedureCode_id_NGSetup);
		NgSetupResponseMessageTypeIE.setTypeOfMessage(Ngap_NGAP_PDU_PR_successfulOutcome); 
		NgSetupResponseMessageTypeIE.setValuePresent(Ngap_SuccessfulOutcome__value_PR_NGSetupResponse);
		
		if(NgSetupResponseMessageTypeIE.getProcedureCode()==Ngap_ProcedureCode_id_NGSetup && NgSetupResponseMessageTypeIE.getTypeOfMessage()==Ngap_NGAP_PDU_PR_successfulOutcome)
		{
			NgSetupResponseMessageTypeIE.encode2pdu(ngSetupResponsePdu);
			ngSetupResponsIEs = &(ngSetupResponsePdu->choice.successfulOutcome->value.choice.NGSetupResponse);
		}
		else
		{
			cout<<"[warning] This information doesn't refer to NGSetupRespons Message!!!"<<endl;
		}
	}
	void NGSetupResponseMsg::setAMFName(const std::string name)
	{
		amfName = new AmfName();
		amfName->setValue(name);
	
		Ngap_NGSetupResponseIEs_t *ie = (Ngap_NGSetupResponseIEs_t *)calloc(1,sizeof(Ngap_NGSetupResponseIEs_t));
		ie->id = Ngap_ProtocolIE_ID_id_AMFName;
		ie->criticality = Ngap_Criticality_reject;
		ie->value.present = Ngap_NGSetupResponseIEs__value_PR_AMFName;
		
		int ret = amfName->encode2AmfName(&ie->value.choice.AMFName);
		if(!ret)
		{
			cout<<"encode AmfName IE error"<<endl;
			return ;
		}
		
		ret = ASN_SEQUENCE_ADD(&ngSetupResponsIEs->protocolIEs.list, ie);
    	if( ret != 0) cout<<"encode AmfName IE error"<<endl;
	}
	void NGSetupResponseMsg::setGUAMIList(std::vector<struct GuamiItem_s> list)
	{
		servedGUAMIList = new ServedGUAMIList();
		ServedGUAMIItem *servedGUAMIItem;
		servedGUAMIItem = new ServedGUAMIItem[list.size()]();
		for(int i=0;i<list.size();i++)
		{
			GUAMI *guami = new GUAMI();
			PlmnId *plmnid = new PlmnId();
			AMFRegionID *regionID = new AMFRegionID();
			AMFSetID *setID = new AMFSetID();
			AMFPointer *pointer = new AMFPointer();
			
			plmnid->setMccMnc(list[i].mcc,list[i].mnc);
			regionID->setAMFRegionID(list[i].regionID);
			setID->setAMFSetID(list[i].AmfSetID);
			pointer->setAMFPointer(list[i].AmfPointer);
			guami->setGUAMI(plmnid,regionID,setID,pointer);
			servedGUAMIItem[i].setGUAMI(guami);
			
			if(list[i].backupAMFName.size() > 0)
			{
				AmfName *backupamfname = new AmfName();
				backupamfname->setValue(list[i].backupAMFName);
				
				servedGUAMIItem[i].setBackupAMFName(backupamfname);
			}
			
		}
		servedGUAMIList->addServedGUAMIItems(servedGUAMIItem,list.size());
	
		Ngap_NGSetupResponseIEs_t *ie = (Ngap_NGSetupResponseIEs_t *)calloc(1,sizeof(Ngap_NGSetupResponseIEs_t));
		ie->id = Ngap_ProtocolIE_ID_id_ServedGUAMIList;
		ie->criticality = Ngap_Criticality_reject;
		ie->value.present = Ngap_NGSetupResponseIEs__value_PR_ServedGUAMIList;
		
		servedGUAMIList->encode2ServedGUAMIList(&ie->value.choice.ServedGUAMIList);

		int ret = ASN_SEQUENCE_ADD(&ngSetupResponsIEs->protocolIEs.list, ie);
    	if( ret != 0) cout<<"encode ServedGUAMIList IE error"<<endl;
	}
	void NGSetupResponseMsg::setRelativeAmfCapacity(long capacity)
	{
		relativeAmfCapacity = new RelativeAMFCapacity();
		relativeAmfCapacity->setValue(capacity);

		Ngap_NGSetupResponseIEs_t *ie = (Ngap_NGSetupResponseIEs_t *)calloc(1,sizeof(Ngap_NGSetupResponseIEs_t));
		ie->id = Ngap_ProtocolIE_ID_id_RelativeAMFCapacity;
		ie->criticality = Ngap_Criticality_ignore;
		ie->value.present = Ngap_NGSetupResponseIEs__value_PR_RelativeAMFCapacity;
		
		relativeAmfCapacity->encode2RelativeAMFCapacity(&ie->value.choice.RelativeAMFCapacity);

		int ret = ASN_SEQUENCE_ADD(&ngSetupResponsIEs->protocolIEs.list, ie);
    	if( ret != 0) cout<<"encode RelativeAMFCapacity IE error"<<endl;
	}
	void NGSetupResponseMsg::setPlmnSupportList(std::vector<PlmnSliceSupport_t> list)
	{
		plmnSupportList = new PLMNSupportList();
		PLMNSupportItem *plmnSupportItem = new PLMNSupportItem[list.size()]();
		for(int i=0;i<list.size();i++)
		{
			PlmnId *plmn = new PlmnId();
			plmn->setMccMnc(list[i].mcc,list[i].mnc);
			cout<<"mcc = "<<list[i].mcc<<"  mnc = "<<list[i].mnc<<endl;
			S_NSSAI *snssai = new S_NSSAI[list[i].slice_list.size()]();
			for(int j=0;j<list[i].slice_list.size();j++)
			{
				snssai[j].setSst(list[i].slice_list[j].sst);
				if(list[i].slice_list[j].sd.size())
				{
					snssai[j].setSd(list[i].slice_list[j].sd);
				}
			}
			plmnSupportItem[i].setPlmnSliceSupportList(plmn,snssai,list[i].slice_list.size());
		}
		plmnSupportList->addPLMNSupportItems(plmnSupportItem,list.size());

		Ngap_NGSetupResponseIEs_t *ie = (Ngap_NGSetupResponseIEs_t *)calloc(1,sizeof(Ngap_NGSetupResponseIEs_t));
		ie->id = Ngap_ProtocolIE_ID_id_PLMNSupportList;
		ie->criticality = Ngap_Criticality_reject;
		ie->value.present = Ngap_NGSetupResponseIEs__value_PR_PLMNSupportList;
		
		plmnSupportList->encode2PLMNSupportList(&ie->value.choice.PLMNSupportList);

		int ret = ASN_SEQUENCE_ADD(&ngSetupResponsIEs->protocolIEs.list, ie);
    	if( ret != 0) cout<<"encode PLMNSupportList IE error"<<endl;
		
	}
	int  NGSetupResponseMsg::encode2buffer(uint8_t *buf, int buf_size)
	{
		asn_fprint(stderr, &asn_DEF_Ngap_NGAP_PDU, ngSetupResponsePdu);
		asn_enc_rval_t er = aper_encode_to_buffer(&asn_DEF_Ngap_NGAP_PDU, NULL, ngSetupResponsePdu, buf, buf_size);
    	cout<<"er.encoded("<<er.encoded<<")"<<endl;
		return er.encoded;
	}
	//Decapsulation
	bool NGSetupResponseMsg::decodefrompdu(Ngap_NGAP_PDU_t *ngap_msg_pdu)
	{
		ngSetupResponsePdu = ngap_msg_pdu;
		
	    if(ngSetupResponsePdu->present == Ngap_NGAP_PDU_PR_successfulOutcome)
		{
			if(ngSetupResponsePdu->choice.successfulOutcome && ngSetupResponsePdu->choice.successfulOutcome->procedureCode == Ngap_ProcedureCode_id_NGSetup && ngSetupResponsePdu->choice.successfulOutcome->criticality == Ngap_Criticality_reject && ngSetupResponsePdu->choice.successfulOutcome->value.present == Ngap_SuccessfulOutcome__value_PR_NGSetupResponse)
			{
				ngSetupResponsIEs = &ngSetupResponsePdu->choice.successfulOutcome->value.choice.NGSetupResponse;
			}
			else
			{
				cout<<"Check NGSetupResponse message error!!!"<<endl;
                return false;
			}
		}
		else
		{
			cout<<"MessageType error!!!"<<endl;
			cout<<"ngSetupResponsePdu->present == "<<ngSetupResponsePdu->present<<endl;
            return false;
		}
		for(int i=0; i< ngSetupResponsIEs->protocolIEs.list.count; i++)
		{
			switch(ngSetupResponsIEs->protocolIEs.list.array[i]->id)
			{
				case Ngap_ProtocolIE_ID_id_AMFName:{
					if(ngSetupResponsIEs->protocolIEs.list.array[i]->criticality == Ngap_Criticality_reject && ngSetupResponsIEs->protocolIEs.list.array[i]->value.present == Ngap_NGSetupResponseIEs__value_PR_AMFName)
					{
						amfName = new AmfName();
						if(!amfName->decodefromAmfName(&ngSetupResponsIEs->protocolIEs.list.array[i]->value.choice.AMFName))
						{
							cout<<"decoded ngap AmfName IE error"<<endl;
							return false;
						}
					}
					else
					{
						cout<<"decoded ngap AmfName IE error"<<endl;
						return false;
					}
				}break;
				case Ngap_ProtocolIE_ID_id_ServedGUAMIList:{
					if(ngSetupResponsIEs->protocolIEs.list.array[i]->criticality == Ngap_Criticality_reject && ngSetupResponsIEs->protocolIEs.list.array[i]->value.present == Ngap_NGSetupResponseIEs__value_PR_ServedGUAMIList)
					{
						servedGUAMIList = new ServedGUAMIList();
						if(!servedGUAMIList->decodefromServedGUAMIList(&ngSetupResponsIEs->protocolIEs.list.array[i]->value.choice.ServedGUAMIList))
						{
							cout<<"decoded ngap ServedGUAMIList IE error!"<<endl;
							return false;
						}
					}
					else
					{
						cout<<"decoded ngap ServedGUAMIList IE error"<<endl;
						return false;
					}
				}break;
				case Ngap_ProtocolIE_ID_id_RelativeAMFCapacity:{
					if(ngSetupResponsIEs->protocolIEs.list.array[i]->criticality == Ngap_Criticality_ignore && ngSetupResponsIEs->protocolIEs.list.array[i]->value.present == Ngap_NGSetupResponseIEs__value_PR_RelativeAMFCapacity)
					{
						relativeAmfCapacity = new RelativeAMFCapacity();
						if(!relativeAmfCapacity->decodefromRelativeAMFCapacity(&ngSetupResponsIEs->protocolIEs.list.array[i]->value.choice.RelativeAMFCapacity))
						{
							cout<<"decoded ngap RelativeAMFCapacity IE error"<<endl;
							return false;
						}
					}
					else
					{
						cout<<"decoded ngap RelativeAMFCapacity IE error"<<endl;
						return false;
					}
				}break;
				case Ngap_ProtocolIE_ID_id_PLMNSupportList:{
					if(ngSetupResponsIEs->protocolIEs.list.array[i]->criticality == Ngap_Criticality_reject && ngSetupResponsIEs->protocolIEs.list.array[i]->value.present == Ngap_NGSetupResponseIEs__value_PR_PLMNSupportList)
					{
						plmnSupportList = new PLMNSupportList();
						if(!plmnSupportList->decodefromPLMNSupportList(&ngSetupResponsIEs->protocolIEs.list.array[i]->value.choice.PLMNSupportList))
						{
							cout<<"decoded ngap PLMNSupportList IE error"<<endl;
							return false;
						}
					}
					else
					{
						cout<<"decoded ngap PLMNSupportList IE error!"<<endl;
						return false;
					}
				}break;
				case Ngap_ProtocolIE_ID_id_CriticalityDiagnostics:{
					cout<<"decoded ngap: This is CriticalityDiagnostics IE"<<endl;
				}break;
				default:{
					cout<<"decoded ngap message pdu error"<<endl; 
					return false;
				}
			}
		}

		return true;
	}
	bool NGSetupResponseMsg::getAMFName(std::string &name)
	{
		if(!amfName) return false;
		amfName->getValue(name);
		return true;
	}
	bool NGSetupResponseMsg::getGUAMIList(std::vector<struct GuamiItem_s> &list)
	{
		if(!servedGUAMIList) return false;
		ServedGUAMIItem *servedGUAMIItem;
		int numOfItem = 0;
		servedGUAMIList->getServedGUAMIItems(servedGUAMIItem,numOfItem);
		for(int i=0;i<numOfItem;i++)
		{
			GuamiItem_t guamiitem_data;
			GUAMI *guami;
			servedGUAMIItem[i].getGUAMI(guami);
			PlmnId *plmnid;
			AMFRegionID *regionID;
			AMFSetID *setID;
			AMFPointer *pointer;
			guami->getGUAMI(plmnid,regionID,setID,pointer);
			plmnid->getMcc(guamiitem_data.mcc);
			plmnid->getMnc(guamiitem_data.mnc);
			regionID->getAMFRegionID(guamiitem_data.regionID);
			setID->getAMFSetID(guamiitem_data.AmfSetID);
			pointer->getAMFPointer(guamiitem_data.AmfPointer);

			AmfName *backupAMFName;
			if(servedGUAMIItem[i].getBackupAMFName(backupAMFName))
			{
				backupAMFName->getValue(guamiitem_data.backupAMFName);
			}
			else
			{
				guamiitem_data.backupAMFName = "None";
			}
			
			list.push_back(guamiitem_data);
		}

		
		return true;
	}
	long NGSetupResponseMsg::getRelativeAmfCapacity()
	{
		if(!relativeAmfCapacity) return -1;
		
		return relativeAmfCapacity->getValue();
	}
	bool NGSetupResponseMsg::getPlmnSupportList(std::vector<PlmnSliceSupport_t> &list)
	{
		if(!plmnSupportList) return false;

		PLMNSupportItem *plmnsupportItemItem;
		int numOfItem = 0;
		plmnSupportList->getPLMNSupportItems(plmnsupportItemItem,numOfItem);
		for(int i=0;i<numOfItem;i++)
		{
			PlmnSliceSupport_t plmnSupportItem_data;
			PlmnId *plmn;
			S_NSSAI *snssai;
			int numofsnssai = 0;
			plmnsupportItemItem[i].getPlmnSliceSupportList(plmn,snssai,numofsnssai);
			plmn->getMcc(plmnSupportItem_data.mcc);
			plmn->getMnc(plmnSupportItem_data.mnc);
			for(int j=0;j<numofsnssai;j++)
			{
				SliceSupportItem_t sliceSupportItem_data;
				snssai[j].getSst(sliceSupportItem_data.sst);
				snssai[j].getSd(sliceSupportItem_data.sd);
				plmnSupportItem_data.slice_list.push_back(sliceSupportItem_data);
			}
			
			list.push_back(plmnSupportItem_data);
		}
		
		return true;
	}

}

