/* Outgoing -> (i2xx INVITE) -> Connected
*/
int c0000_Outgoing_2_Connected_X_i2xxINVITE(va_list *app)
{
	int ret;

	tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *);
	const tsip_response_t *r2xxINVITE = va_arg(*app, const tsip_response_t *);
	/* const tsip_action_t* action = */ va_arg(*app, const tsip_action_t *);

	/* Update the dialog state */
	if((ret = tsip_dialog_update(TSIP_DIALOG(self), r2xxINVITE))){
		return ret;
	}
	
	/* Process remote offer */
	if((ret = tsip_dialog_invite_process_ro(self, r2xxINVITE))){
		send_BYE(self);
		return ret;
	}
	else{
		/* send ACK */
		ret = send_ACK(self, r2xxINVITE);
	}
	
	/* Determine whether the remote party support UPDATE */
	self->support_update = tsip_message_allowed(r2xxINVITE, "UPDATE");

	/* Session Timers */
	if(self->stimers.timer.timeout){
		tsip_dialog_invite_stimers_handle(self, r2xxINVITE);
	}

	// starts ICE timers now that both parties received the "candidates"
	if(tsip_dialog_invite_ice_is_enabled(self)){
		tsip_dialog_invite_ice_timers_set(self, (self->required.ice ? -1 : TSIP_DIALOG_INVITE_ICE_CONNCHECK_TIMEOUT));
	}
	
	/* Alert the user (session) */
	ret = TSIP_DIALOG_INVITE_SIGNAL(self, tsip_ao_request, 
			TSIP_RESPONSE_CODE(r2xxINVITE), TSIP_RESPONSE_PHRASE(r2xxINVITE), r2xxINVITE);
		/* Alert the user (dialog) */
	ret = TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connected, "Dialog connected");
	
	if(self->is_transf){
		ret = tsip_dialog_invite_notify_parent(self, r2xxINVITE);
		self->is_transf = tsk_false;//final response -> no longer need to notify the parent
	}

	/* MSRP File Transfer */
	/*if(TSIP_DIALOG(self)->curr_action && ((TSIP_DIALOG(self)->curr_action->media.type & tmedia_msrp) == tmedia_msrp)){
		// FIXME
		tmedia_session_mgr_send_file(self->msession_mgr, "C:\\avatar.png", 
			TMEDIA_SESSION_SET_NULL());
	}*/
	
	return ret;
}
// Current -> (iINVITE) -> Current
static int x0500_Current_2_Current_X_iINVITE(va_list *app)
{
	int ret;
	tsip_dialog_invite_t *self;
	const tsip_action_t* action;
	const tsip_message_t *message;

	self = va_arg(*app, tsip_dialog_invite_t *);
	message = va_arg(*app, const tsip_message_t *);
	action = va_arg(*app, const tsip_action_t *);

	self->is_client = tsk_false;
	ret = tsip_dialog_invite_ice_save_action(self, _fsm_action_iINVITE, action, message);
	
	// Cancel without notifying ("silent mode") and perform the operation right now ("sync mode")
	tsip_dialog_invite_ice_cancel_silent_and_sync_ctx(self);

	// set remote candidates
	if(TSIP_MESSAGE_HAS_CONTENT(message)){
		if(tsk_striequals("application/sdp", TSIP_MESSAGE_CONTENT_TYPE(message))){
			tsdp_message_t* sdp_ro;
			if(!(sdp_ro = tsdp_message_parse(TSIP_MESSAGE_CONTENT_DATA(message), TSIP_MESSAGE_CONTENT_DATA_LENGTH(message)))){
				TSK_DEBUG_ERROR("Failed to parse remote sdp message");
				return -2;
			}
			// create ICE context
			if((ret = tsip_dialog_invite_ice_create_ctx(self, tmedia_type_from_sdp(sdp_ro)))){
				TSK_DEBUG_ERROR("tsip_dialog_invite_ice_create_ctx() failed");
				return ret;
			}
			ret = tsip_dialog_invite_ice_process_ro(self, sdp_ro, tsk_true);
			TSK_OBJECT_SAFE_FREE(sdp_ro);
		}
		else{
			TSK_DEBUG_ERROR("[%s] content-type is not supportted", TSIP_MESSAGE_CONTENT_TYPE(message));
			return -3;
		}
	}

	// For now disable ICE timers until we send the 2xx and receive the ACK
	ret = tsip_dialog_invite_ice_timers_set(self, -1);

	// Start ICE
	ret = tsip_dialog_invite_ice_start_ctx(self);

	return ret;
}
// Current -> (oINVITE) -> Current
static int x0500_Current_2_Current_X_oINVITE(va_list *app)
{
	int ret;
	tsip_dialog_invite_t *self;
	const tsip_action_t* action;
	const tsip_message_t *message;
	tmedia_type_t media_type;
	static const tsk_bool_t __force_restart_is_yes = tsk_true;

	self = va_arg(*app, tsip_dialog_invite_t *);
	message = va_arg(*app, const tsip_message_t *);
	action = va_arg(*app, const tsip_action_t *);

	media_type = (action && action->media.type != tmedia_none) ? action->media.type : TSIP_DIALOG_GET_SS(self)->media.type;
	self->is_client = tsk_true;
	tsip_dialog_invite_ice_save_action(self, _fsm_action_oINVITE, action, message);

	// Cancel without notifying ("silent mode") and perform the operation right now ("sync mode")
	tsip_dialog_invite_ice_cancel_silent_and_sync_ctx(self);

	// create ICE context
	if((ret = tsip_dialog_invite_ice_create_ctx(self, media_type))){
		TSK_DEBUG_ERROR("tsip_dialog_invite_ice_create_ctx() failed");
		return ret;
	}

	// For now disable ICE timers until we receive the 2xx
	ret = tsip_dialog_invite_ice_timers_set(self, -1);

	// Start ICE
	ret = tsip_dialog_invite_ice_start_ctx(self);

	// alert the user only if we are in initial state which means that it's not media update
	if(TSIP_DIALOG(self)->state == tsip_initial){
		TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting");
	}

	return ret;
}
static int tsip_dialog_invite_ice_create_ctx(tsip_dialog_invite_t * self, tmedia_type_t media_type)
{
	int32_t transport_idx;
	if(!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}
	transport_idx = TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default;
	if(!self->ice.ctx_audio && (media_type & tmedia_audio)){
		self->ice.ctx_audio = tnet_ice_ctx_create(self->ice.is_jingle, TNET_SOCKET_TYPE_IS_IPV6(TSIP_DIALOG_GET_STACK(self)->network.proxy_cscf_type[transport_idx]), 
					self->use_rtcp, tsk_false, tsip_dialog_invite_ice_audio_callback, self);
		if(!self->ice.ctx_audio){
			TSK_DEBUG_ERROR("Failed to create ICE audio context");
			return -2;
		}
		tnet_ice_ctx_set_stun(self->ice.ctx_audio, "stun.l.google.com", 19302, "Doubango", "*****@*****.**", "stun-password"); //FIXME
		tnet_ice_ctx_set_rtcpmux(self->ice.ctx_audio, self->use_rtcpmux);
	}
	if(!self->ice.ctx_video && (media_type & tmedia_video)){
		self->ice.ctx_video = tnet_ice_ctx_create(self->ice.is_jingle, TNET_SOCKET_TYPE_IS_IPV6(TSIP_DIALOG_GET_STACK(self)->network.proxy_cscf_type[transport_idx]), 
					self->use_rtcp, tsk_true, tsip_dialog_invite_ice_video_callback, self);
		if(!self->ice.ctx_video){
			TSK_DEBUG_ERROR("Failed to create ICE video context");
			return -2;
		}
		tnet_ice_ctx_set_stun(self->ice.ctx_video, "stun.l.google.com", 19302, "Doubango", "*****@*****.**", "stun-password"); // FIXME
		tnet_ice_ctx_set_rtcpmux(self->ice.ctx_video, self->use_rtcpmux);
	}

	// "none" comparison is used to exclude the "first call"
	if(self->ice.media_type != tmedia_none && self->ice.media_type != media_type){
		// cancels contexts associated to old medias
		if(self->ice.ctx_audio && !(media_type & tmedia_audio)){
			tnet_ice_ctx_cancel(self->ice.ctx_audio);
		}
		if(self->ice.ctx_video && !(media_type & tmedia_video)){
			tnet_ice_ctx_cancel(self->ice.ctx_video);
		}
		// cancels contexts associated to new medias (e.g. session "remove" then "add")
		// cancel() on newly created contexts don't have any effect
		if(self->ice.ctx_audio && (!(media_type & tmedia_audio) && (self->ice.media_type & tmedia_audio))){
			//tnet_ice_ctx_cancel(self->ice.ctx_audio);
		}
		if(self->ice.ctx_video && (!(media_type & tmedia_video) && (self->ice.media_type & tmedia_video))){
			//tnet_ice_ctx_cancel(self->ice.ctx_video);
		}
	}

	self->ice.media_type = media_type;
	

	// For now disable timers until both parties get candidates
	// (RECV ACK) or RECV (200 OK)
	tsip_dialog_invite_ice_timers_set(self, -1);

	// update session manager with the right ICE contexts
	if(self->msession_mgr){
		tmedia_session_mgr_set_ice_ctx(self->msession_mgr, self->ice.ctx_audio, self->ice.ctx_video);
	}

	return 0;
}