// Started -> (oINVITE) -> Started
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;

	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);

	// 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;
	}

	// 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;
}
/* 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;
}
/* Any -> (hangup) -> InProgress
*/
int tsip_dialog_register_Any_2_InProgress_X_hangup(va_list *app)
{
	tsip_dialog_register_t *self;

	self = va_arg(*app, tsip_dialog_register_t *);

	/* Alert the user */
	TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog");

	self->unregistering = tsk_true;
	return tsip_dialog_register_send_REGISTER(self, tsk_true);
}
/* Started -> (SUBSCRIBE) -> Trying
*/
int tsip_dialog_subscribe_Started_2_Trying_X_subscribe(va_list *app)
{
	tsip_dialog_subscribe_t *self;

	self = va_arg(*app, tsip_dialog_subscribe_t *);

	TSIP_DIALOG(self)->running = tsk_true;

	/* alert the user */
	TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting");

	return send_SUBSCRIBE(self);
}
/* Started -> (sendOPTIONS) -> Sending
*/
int tsip_dialog_options_Started_2_Sending_X_sendOPTIONS(va_list *app)
{
	tsip_dialog_options_t *self;
	const tsip_action_t* action;

	self = va_arg(*app, tsip_dialog_options_t *);
	/*tsip_request_t *request =*/ va_arg(*app, tsip_request_t *);
	action = va_arg(*app, const tsip_action_t *);

	TSIP_DIALOG(self)->running = tsk_true;
	tsip_dialog_set_curr_action(TSIP_DIALOG(self), action);

	/* alert the user */
	TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting");

	return send_OPTIONS(self);
}
// 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;
}
/* Started -> (oINVITE) -> Outgoing
*/
int c0000_Started_2_Outgoing_X_oINVITE(va_list *app)
{
	int ret;
	tsip_dialog_invite_t *self;
	const tsip_action_t* action;

	self = va_arg(*app, tsip_dialog_invite_t *);
	va_arg(*app, const tsip_message_t *);
	action = va_arg(*app, const tsip_action_t *);
	
	/* This is the first FSM transaction when you try to make an audio/video/msrp call */
	if(!self->msession_mgr){
		int32_t transport_idx = TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default;
		self->msession_mgr = tmedia_session_mgr_create(action ? action->media.type : tmedia_all,
			TSIP_DIALOG_GET_STACK(self)->network.local_ip[transport_idx], TNET_SOCKET_TYPE_IS_IPV6(TSIP_DIALOG_GET_STACK(self)->network.proxy_cscf_type[transport_idx]), tsk_true);
		if(TSIP_DIALOG_GET_STACK(self)->natt.ctx){
			ret = tmedia_session_mgr_set_natt_ctx(self->msession_mgr, TSIP_DIALOG_GET_STACK(self)->natt.ctx, TSIP_DIALOG_GET_STACK(self)->network.aor.ip[transport_idx]);
		}
		
		ret = tmedia_session_mgr_set_ice_ctx(self->msession_mgr, self->ice.ctx_audio, self->ice.ctx_video);
		ret = tsip_dialog_invite_msession_configure(self);
	}

	/* We are the client */
	self->is_client = tsk_true;
	/* Whether it's a client dialog for call transfer */
	self->is_transf = (TSIP_DIALOG_GET_SS(self)->id_parent != TSIP_SSESSION_INVALID_ID);

	/* Get Media type from the action */
	TSIP_DIALOG_GET_SS(self)->media.type = action->media.type;
	/* Appy media params received from the user */
	if(!TSK_LIST_IS_EMPTY(action->media.params)){
		tmedia_session_mgr_set_3(self->msession_mgr, action->media.params);
	}

	/*  RFC 4028 - 7.1. Generating an Initial Session Refresh Request

		A UAC MAY include a Session-Expires header field in an initial
		session refresh request if it wants a session timer applied to the
		session.  The value of this header field indicates the session
		interval desired by the UAC.  If a Min-SE header is included in the
		initial session refresh request, the value of the Session-Expires
		MUST be greater than or equal to the value in Min-SE.

		The UAC MAY include the refresher parameter with value 'uac' if it
		wants to perform the refreshes.  However, it is RECOMMENDED that the
		parameter be omitted so that it can be selected by the negotiation
		mechanisms described below.
	*/
	if(TSIP_DIALOG_GET_SS(self)->media.timers.timeout){
		self->stimers.timer.timeout = TSIP_DIALOG_GET_SS(self)->media.timers.timeout;
		tsk_strupdate(&self->stimers.refresher, TSIP_DIALOG_GET_SS(self)->media.timers.refresher);
		self->stimers.is_refresher = tsk_striequals(self->stimers.refresher, "uac");
		self->supported.timer = tsk_true;
	}
	
	/* QoS
	* One Voice Profile - 5.4.1 SIP Precondition Considerations
	* The UE shall use the Supported header, and not the Require header, to indicate the support of precondition in
	* accordance with Section 5.1.3.1 of 3GPP TS 24.229.
	*/
	self->qos.type = TSIP_DIALOG_GET_SS(self)->media.qos.type;
	self->qos.strength = TSIP_DIALOG_GET_SS(self)->media.qos.strength;
	tmedia_session_mgr_set_qos(self->msession_mgr, self->qos.type, self->qos.strength);
	self->supported.precondition = (self->qos.strength == tmedia_qos_strength_optional);
	self->required.precondition = (self->qos.strength == tmedia_qos_strength_mandatory);

	/* send the request */
	ret = send_INVITE(self, tsk_false);

	/* alert the user */
	TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting");

	return ret;
}