#include "../cdp_avp/cdp_avp_mod.h"
#include "../../lib/ims/ims_getters.h"

#include "ccr.h"
#include "Ro_data.h"
#include "ro_avp.h"
#include "ims_charging_mod.h"

extern cdp_avp_bind_t *cdp_avp;
extern struct cdp_binds cdpb;

int Ro_write_event_type_avps(AAA_AVP_LIST *avp_list, event_type_t *x)
{
	AAA_AVP_LIST aList = {0, 0};

	LM_DBG("write event type AVPs\n");
	if(x->sip_method) {
		if(!cdp_avp->epcapp.add_SIP_Method(
				   &aList, *(x->sip_method), AVP_DUPLICATE_DATA))
			goto error;
	}

	if(x->event)
		if(!cdp_avp->epcapp.add_Event(&aList, *(x->event), 0))
			goto error;

	if(x->expires)
		if(!cdp_avp->epcapp.add_Expires(&aList, *(x->expires)))
			goto error;

	if(!cdp_avp->epcapp.add_Event_Type(
			   avp_list, &aList, AVP_FREE_DATA)) //TODO: used to be DON'T FREE
		goto error;

	return 1;
error:
	cdp_avp->cdp->AAAFreeAVPList(&aList);
	LM_ERR("error while adding event type avps\n");
	return 0;
}

int Ro_write_time_stamps_avps(AAA_AVP_LIST *avp_list, time_stamps_t *x)
{
	AAA_AVP_LIST aList = {0, 0};

	LM_DBG("write timestamp AVPs\n");
	if(x->sip_request_timestamp)
		if(!cdp_avp->epcapp.add_SIP_Request_Timestamp(
				   &aList, *(x->sip_request_timestamp)))
			goto error;

	if(x->sip_request_timestamp_fraction)
		if(!cdp_avp->epcapp.add_SIP_Request_Timestamp_Fraction(
				   &aList, *(x->sip_request_timestamp_fraction)))
			goto error;

	if(x->sip_response_timestamp)
		if(!cdp_avp->epcapp.add_SIP_Response_Timestamp(
				   &aList, *(x->sip_response_timestamp)))
			goto error;

	if(x->sip_response_timestamp_fraction)
		if(!cdp_avp->epcapp.add_SIP_Response_Timestamp_Fraction(
				   &aList, *(x->sip_response_timestamp_fraction)))
			goto error;

	if(!cdp_avp->epcapp.add_Time_Stamps(
			   avp_list, &aList, AVP_FREE_DATA)) //used to be DON'T FREE
		goto error;


	return 1;
error:
	cdp_avp->cdp->AAAFreeAVPList(&aList);
	LM_ERR("error while adding time stamps avps\n");

	return 0;
}

int Ro_write_ims_information_avps(AAA_AVP_LIST *avp_list, ims_information_t *x)
{
	str_list_slot_t *sl = 0;
	AAA_AVP_LIST aList = {0, 0};
	AAA_AVP_LIST aList2 = {0, 0};
	service_specific_info_list_element_t *info = 0;
	ioi_list_element_t *ioi_elem = 0;

	LM_DBG("write IMS information AVPs\n");

	if(x->event_type)
		if(!Ro_write_event_type_avps(&aList2, x->event_type))
			goto error;
	if(x->role_of_node)
		if(!cdp_avp->epcapp.add_Role_Of_Node(&aList2, *(x->role_of_node)))
			goto error;

	if(!cdp_avp->epcapp.add_Node_Functionality(&aList2, x->node_functionality))
		goto error;

	if(x->user_session_id)
		if(!cdp_avp->epcapp.add_User_Session_Id(
				   &aList2, *(x->user_session_id), 0))
			goto error;

	for(sl = x->calling_party_address.head; sl; sl = sl->next) {
		if(!cdp_avp->epcapp.add_Calling_Party_Address(&aList2, sl->data, 0))
			goto error;
		LM_DBG("added calling party address %.*s\n", sl->data.len, sl->data.s);
	}

	if(x->called_party_address) {
		if(!cdp_avp->epcapp.add_Called_Party_Address(
				   &aList2, *(x->called_party_address), 0))
			goto error;
		LM_DBG("added called party address %.*s\n",
				x->called_party_address->len, x->called_party_address->s);
	}

	if(x->incoming_trunk_id && (x->incoming_trunk_id->len > 0)
			&& x->outgoing_trunk_id && (x->outgoing_trunk_id->len > 0)) {
		if(!cdp_avp->epcapp.add_Outgoing_Trunk_Group_Id(
				   &aList, *(x->outgoing_trunk_id), 0))
			goto error;

		if(!cdp_avp->epcapp.add_Incoming_Trunk_Group_Id(
				   &aList, *(x->incoming_trunk_id), 0))
			goto error;

		if(!cdp_avp->epcapp.add_Trunk_Group_Id(&aList2, &aList, 0))
			goto error;
		cdp_avp->cdp->AAAFreeAVPList(&aList);
		aList.head = aList.tail = 0;
	}

	if(x->access_network_info) {
		cdp_avp->imsapp.add_Access_Network_Information(
				&aList2, *(x->access_network_info), 0);
	}

	for(sl = x->called_asserted_identity.head; sl; sl = sl->next) {
		if(!cdp_avp->epcapp.add_Called_Asserted_Identity(&aList2, sl->data, 0))
			goto error;
	}

	if(x->requested_party_address)
		if(!cdp_avp->epcapp.add_Requested_Party_Address(
				   &aList2, *(x->requested_party_address), 0))
			goto error;
	if(x->time_stamps)
		if(!Ro_write_time_stamps_avps(&aList2, x->time_stamps))
			goto error;

	for(ioi_elem = x->ioi.head; ioi_elem; ioi_elem = ioi_elem->next) {

		if(ioi_elem->info.originating_ioi)
			if(!cdp_avp->epcapp.add_Originating_IOI(
					   &aList, *(ioi_elem->info.originating_ioi), 0))
				goto error;

		if(ioi_elem->info.terminating_ioi)
			if(!cdp_avp->epcapp.add_Terminating_IOI(
					   &aList, *(ioi_elem->info.terminating_ioi), 0))
				goto error;

		if(!cdp_avp->epcapp.add_Inter_Operator_Identifier(&aList2, &aList, 0))
			goto error;
		cdp_avp->cdp->AAAFreeAVPList(&aList);
		aList.head = aList.tail = 0;
	}

	if(x->icid)
		if(!cdp_avp->epcapp.add_IMS_Charging_Identifier(&aList2, *(x->icid), 0))
			goto error;

	if(x->service_id)
		if(!cdp_avp->epcapp.add_Service_ID(&aList2, *(x->service_id), 0))
			goto error;

	for(info = x->service_specific_info.head; info; info = info->next) {

		if(info->info.data)
			if(!cdp_avp->epcapp.add_Service_Specific_Data(
					   &aList, *(info->info.data), 0))
				goto error;
		if(info->info.type)
			if(!cdp_avp->epcapp.add_Service_Specific_Type(
					   &aList, *(info->info.type)))
				goto error;

		if(!cdp_avp->epcapp.add_Service_Specific_Info(&aList2, &aList, 0))
			goto error;
		cdp_avp->cdp->AAAFreeAVPList(&aList);
		aList.head = aList.tail = 0;
	}

	if(x->cause_code)
		if(!cdp_avp->epcapp.add_Cause_Code(&aList2, *(x->cause_code)))
			goto error;

	if(x->app_provided_party) {
		if(!cdp_avp->epcapp.add_Application_Provided_Called_Party_Address(
				   &aList, *(x->app_provided_party), 0))
			goto error;
		if(!cdp_avp->epcapp.add_Application_Server_Information(
				   &aList2, &aList, 0))
			goto error;
		cdp_avp->cdp->AAAFreeAVPList(&aList);
		aList.head = aList.tail = 0;
	}

	if(!cdp_avp->epcapp.add_IMS_Information(avp_list, &aList2,
			   AVP_FREE_DATA)) //TODO check why not DON'T FREE DATA
		goto error;

	return 1;
error:
	/*free aList*/
	cdp_avp->cdp->AAAFreeAVPList(&aList);
	cdp_avp->cdp->AAAFreeAVPList(&aList2);
	LM_ERR("could not add ims information AVPs\n");
	return 0;
}

int Ro_write_service_information_avps(
		AAA_AVP_LIST *avp_list, service_information_t *x)
{
	subscription_id_list_element_t *elem = 0;
	AAA_AVP_LIST aList = {0, 0};

	LM_DBG("write service information\n");
	for(elem = x->subscription_id.head; elem; elem = elem->next) {

		if(!cdp_avp->ccapp.add_Subscription_Id_Group(
				   &aList, elem->s.type, elem->s.id, 0))
			goto error;
	}

	if(x->ims_information)
		if(!Ro_write_ims_information_avps(&aList, x->ims_information))
			goto error;

	if(!cdp_avp->epcapp.add_Service_Information(
			   avp_list, &aList, AVP_FREE_DATA)) //TODO: use to be don't free
		goto error;

	return 1;
error:
	cdp_avp->cdp->AAAFreeAVPList(&aList);
	return 0;
}

AAAMessage *Ro_write_CCR_avps(AAAMessage *ccr, Ro_CCR_t *x)
{

	if(!ccr)
		return 0;
	LM_DBG("write all CCR AVPs\n");

	if(x->origin_host.s && x->origin_host.len > 0) {
		if(!cdp_avp->base.add_Origin_Host(&(ccr->avpList), x->origin_host, 0))
			goto error;
	}

	if(x->origin_realm.s && x->origin_realm.len > 0) {
		if(!cdp_avp->base.add_Origin_Realm(&(ccr->avpList), x->origin_realm, 0))
			goto error;
	}

	if(x->destination_host.s && x->destination_host.len > 0) {
		if(!cdp_avp->base.add_Destination_Host(
				   &(ccr->avpList), x->destination_host, 0))
			goto error;
	}

	if(x->destination_realm.s && x->destination_realm.len > 0) {
		if(!ro_add_destination_realm_avp(ccr, x->destination_realm))
			goto error;
	}

	if(!cdp_avp->base.add_Accounting_Record_Type(
			   &(ccr->avpList), x->acct_record_type))
		goto error;
	if(!cdp_avp->base.add_Accounting_Record_Number(
			   &(ccr->avpList), x->acct_record_number))
		goto error;

	if(x->user_name)
		if(!cdp_avp->base.add_User_Name(
				   &(ccr->avpList), *(x->user_name), AVP_DUPLICATE_DATA))
			goto error;

	if(x->acct_interim_interval)
		if(!cdp_avp->base.add_Acct_Interim_Interval(
				   &(ccr->avpList), *(x->acct_interim_interval)))
			goto error;

	if(x->origin_state_id)
		if(!cdp_avp->base.add_Origin_State_Id(
				   &(ccr->avpList), *(x->origin_state_id)))
			goto error;

	if(x->event_timestamp)
		if(!cdp_avp->base.add_Event_Timestamp(
				   &(ccr->avpList), *(x->event_timestamp)))
			goto error;

	if(x->service_context_id)
		if(!cdp_avp->ccapp.add_Service_Context_Id(
				   &(ccr->avpList), *(x->service_context_id), 0))
			goto error;

	if(x->service_information)
		if(!Ro_write_service_information_avps(
				   &(ccr->avpList), x->service_information))
			goto error;
	return ccr;
error:
	cdp_avp->cdp->AAAFreeMessage(&ccr);
	return 0;
}

AAAMessage *Ro_new_ccr(AAASession *session, Ro_CCR_t *ro_ccr_data)
{

	AAAMessage *ccr = 0;
	LM_DBG("create a new CCR\n");
	ccr = cdp_avp->cdp->AAACreateRequest(
			IMS_Ro, Diameter_CCR, Flag_Proxyable, session);
	if(!ccr) {
		LM_ERR("could not create CCR\n");
		return 0;
	}

	ccr = Ro_write_CCR_avps(ccr, ro_ccr_data);

	return ccr;
}

Ro_CCA_t *Ro_parse_CCA_avps(AAAMessage *cca)
{
	if(!cca)
		return 0;

	LM_DBG("parse CCA AVPs\n");
	Ro_CCA_t *ro_cca_data = 0;
	mem_new(ro_cca_data, sizeof(Ro_CCR_t), pkg);
	multiple_services_credit_control_t *mscc = 0;
	mem_new(mscc, sizeof(multiple_services_credit_control_t), pkg);
	granted_services_unit_t *gsu = 0;
	mem_new(gsu, sizeof(granted_services_unit_t), pkg);
	final_unit_indication_t *fui = 0;
	mem_new(fui, sizeof(final_unit_indication_t), pkg);
	mscc->granted_service_unit = gsu;
	mscc->final_unit_action = fui;

	mscc->final_unit_action->action = -1;
	mscc->final_unit_action->redirect_server = 0;

	AAA_AVP_LIST *avp_list = &cca->avpList;
	AAA_AVP_LIST mscc_avp_list;
	AAA_AVP_LIST *mscc_avp_list_ptr;

	AAA_AVP *avp = avp_list->head;
	unsigned int x;
	while(avp != NULL) {
		switch(avp->code) {
			case AVP_CC_Request_Type:
				x = get_4bytes(avp->data.s);
				ro_cca_data->cc_request_type = x;
				break;
			case AVP_CC_Request_Number:
				x = get_4bytes(avp->data.s);
				ro_cca_data->cc_request_number = x;
				break;
			case AVP_Multiple_Services_Credit_Control:
				mscc_avp_list = cdp_avp->cdp->AAAUngroupAVPS(avp->data);
				mscc_avp_list_ptr = &mscc_avp_list;
				AAA_AVP *mscc_avp = mscc_avp_list_ptr->head;
				while(mscc_avp != NULL) {
					LM_DBG("MSCC AVP code is [%i] and data length is [%i]\n",
							mscc_avp->code, mscc_avp->data.len);
					switch(mscc_avp->code) {
						AAA_AVP_LIST y;
						AAA_AVP *z;
						case AVP_Granted_Service_Unit:
							y = cdp_avp->cdp->AAAUngroupAVPS(mscc_avp->data);
							z = y.head;
							while(z) {
								switch(z->code) {
									case AVP_CC_Time:
										mscc->granted_service_unit->cc_time =
												get_4bytes(z->data.s);
										break;
									default:
										LM_ERR("Unsupported Granted Service "
											   "Unit with code:[%d]\n",
												z->code);
								}
								z = z->next;
							}
							cdp_avp->cdp->AAAFreeAVPList(&y);
							break;
						case AVP_Validity_Time:
							mscc->validity_time = get_4bytes(mscc_avp->data.s);
							break;
						case AVP_Time_Quota_Threshold:
							mscc->time_quota_threshold =
									get_4bytes(mscc_avp->data.s);
							break;
						case AVP_Result_Code:
							mscc->resultcode = get_4bytes(mscc_avp->data.s);
							break;
						case AVP_Final_Unit_Indication:
							y = cdp_avp->cdp->AAAUngroupAVPS(mscc_avp->data);
							z = y.head;
							while(z) {
								switch(z->code) {
									case AVP_Final_Unit_Action:
										mscc->final_unit_action->action =
												get_4bytes(z->data.s);
										break;
									case AVP_Redirect_Server:
										LM_DBG("Received redirect server\n");
										redirect_server_t
												*redirect_server_info = 0;
										mem_new(redirect_server_info,
												sizeof(redirect_server_t), pkg);
										mscc->final_unit_action
												->redirect_server =
												redirect_server_info;

										AAA_AVP_LIST yy;
										AAA_AVP *zz;
										yy = cdp_avp->cdp->AAAUngroupAVPS(
												z->data);
										zz = yy.head;
										while(zz) {
											switch(zz->code) {
												case AVP_Redirect_Address_Type:
													LM_DBG("Received redirect "
														   "address type\n");
													mscc->final_unit_action
															->redirect_server
															->address_type =
															get_4bytes(
																	zz->data.s);
													break;
												case AVP_Redirect_Server_Address:
													LM_DBG("Received redirect "
														   "server address of "
														   "[%.*s]\n",
															zz->data.len,
															zz->data.s);
													str_dup_ptr(
															redirect_server_info
																	->server_address,
															zz->data, pkg);
													break;
												default:
													LM_ERR("Unsupported "
														   "Redirect Server "
														   "AVP with "
														   "code:[%d]\n",
															zz->code);
											}
											zz = zz->next;
										}
										cdp_avp->cdp->AAAFreeAVPList(&yy);
										break;
									default:
										LM_ERR("Unsupported Final Unit "
											   "Indication AVP.\n");
								}
								z = z->next;
							}
							cdp_avp->cdp->AAAFreeAVPList(&y);
					}
					mscc_avp = mscc_avp->next;
				}
				cdp_avp->cdp->AAAFreeAVPList(mscc_avp_list_ptr);
				break;
			case AVP_Result_Code:
				x = get_4bytes(avp->data.s);
				ro_cca_data->resultcode = x;
				break;
			case AVP_Origin_Host:
				ims_str_dup(ro_cca_data->origin_host, avp->data, pkg);
				break;
		}
		avp = avp->next;
	}
	ro_cca_data->mscc = mscc;
	return ro_cca_data;

out_of_memory:
	LM_ERR("out of pkg memory\n");
	Ro_free_CCA(ro_cca_data);
	return 0;
}
