/*
 * wsp_client_session_states.def - states for WSP session state machines
 *
 * Macro calls to generate rows of the state table. See the documentation for
 * guidance how to use and update these.
 *
 * Note that `NULL' state is renamed to `NULL_SESSION' because NULL is
 * reserved by C.
 *
 * Marcel Jansen
 */

STATE_NAME(NULL_SESSION)
STATE_NAME(CONNECTING)
STATE_NAME(CONNECTED)
STATE_NAME(SUSPENDED)
STATE_NAME(RESUMING)

ROW(NULL_SESSION,
	S_Connect_Req,
	1,
	{
        Octstr *opdu;
        opdu = make_connect_pdu(sm);
        send_invoke(sm, opdu, 0, PROVIDER_ACKNOWLEDGEMENT, TRANSACTION_CLASS_2);
	},
	CONNECTING)

ROW(CONNECTING,
	S_Disconnect_Req,
	1,
	{   
        if (sm->connect_handle >= 0) 
            /* TR-Abort.req(DISCONNECT) the Connect transaction */
            send_abort(WSP_ABORT_DISCONNECT, sm->connect_handle);

		/* Abort(DISCONNECT) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_DISCONNECT);

		/* S-Disconnect.ind(DISCONNECT) */
		indicate_disconnect(sm, WSP_ABORT_USERREQ);
	},
	NULL_SESSION)


ROW(CONNECTING,
	Disconnect_Event,
	1,
	{
        if (sm->connect_handle >= 0) 
            /* TR-Abort.req(DISCONNECT) the Connect transaction */
            send_abort(WSP_ABORT_DISCONNECT, sm->connect_handle);

		/* Abort(DISCONNECT) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_DISCONNECT);

		/* S-Disconnect.ind(DISCONNECT) */
		indicate_disconnect(sm, WSP_ABORT_DISCONNECT);
	},
	NULL_SESSION)

ROW(CONNECTING,
	S_MethodInvoke_Req,
	1,
	{
		WSPClientMethodMachine *msm;

        /* start a new method invoke with this event (see method state table) */
		msm = method_machine_create(sm, current_event->u.S_MethodInvoke_Req.client_transaction_id);

		/* Hand off the event to the new method machine */
		handle_method_event(sm, msm, current_event, pdu);
	},
	CONNECTING)

ROW(CONNECTING,
	S_MethodAbort_Req,
	1,
	{
		WSPClientMethodMachine *msm;
		/* See method state table  */
		msm = find_client_method_machine(sm, current_event->u.S_MethodAbort_Req.transaction_id);
		handle_method_event(sm, msm, current_event, pdu);
	},
	CONNECTING)

ROW(CONNECTING,
	Suspend_Event,
	1,
	{
        send_abort(WSP_ABORT_DISCONNECT, sm->connect_handle);

		/* Abort(DISCONNECT) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_DISCONNECT);

		/* S-Disconnect.ind(DISCONNECT) */
		indicate_disconnect(sm, WSP_ABORT_SUSPEND);
	},
	NULL_SESSION)

ROW(CONNECTING,
	TR_Invoke_Ind,
	current_event->u.TR_Invoke_Ind.tcl == 1 && pdu->type == ConfirmedPush,
	{
		/* TR-Abort.req(PROTOERR) the TR-Invoke */
		send_abort(WSP_ABORT_PROTOERR, e->handle);
	},
	CONNECTING)

ROW(CONNECTING,
	TR_Result_Ind,
	current_event->u.TR_Result_Ind.handle == sm->connect_handle && pdu->type == ConnectReply,
	{
        /* TR-Result.res */
		sendResultResponse(current_event->u.TR_Result_Ind.handle);

        /* Session_ID = SessionId from PDU */
        sm->server_session_id = pdu->u.ConnectReply.sessionid;
        
        /* S-Connect.cnf */
        confirm_connect(sm, wsp_headers_unpack(pdu->u.ConnectReply.headers, 1), wsp_cap_unpack_list(pdu->u.ConnectReply.capabilities));
	},
	CONNECTED)

ROW(CONNECTING,
	TR_Result_Ind,
	current_event->u.TR_Result_Ind.handle == sm->connect_handle && pdu->type == Redirect,
	{
        /* TR-Result.res */
		sendResultResponse(current_event->u.TR_Result_Ind.handle);

        /* abort(CONNECTERR) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_CONNECTERR);
        
        /* TODO: support redirect properly S-disconnect.ind(Redirect parameters) */
        indicate_disconnect(sm, WSP_ABORT_SUSPEND);
	},
	NULL_SESSION)

ROW(CONNECTING,
	TR_Result_Ind,
	current_event->u.TR_Result_Ind.handle == sm->connect_handle && pdu->type == Reply,
	{
        /* TR-Result.res */
		sendResultResponse(current_event->u.TR_Result_Ind.handle);

        /* abort(CONNECTERR) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_CONNECTERR);
        
        /* TODO: how to fill in reply parameters S-disconnect.ind(Reply parameters) */
        indicate_disconnect(sm, WSP_ABORT_SUSPEND);        
	},
	NULL_SESSION)

ROW(CONNECTING,
	TR_Result_Ind,
    /* method transaction */
	current_event->u.TR_Result_Ind.handle != sm->connect_handle,
	{
		WSPClientMethodMachine *msm;
        WAPEvent *ab;
        
        /* abort(PROTOERR) the method transaction */		
        abort_method(sm, e->handle, WSP_ABORT_PROTOERR);
	},
	CONNECTING)



ROW(CONNECTING,
	TR_Invoke_Cnf,
	/* method transaction */
	current_event->u.TR_Invoke_Cnf.handle != sm->connect_handle,
	{
		WSPClientMethodMachine *msm;
        WAPEvent *ab;
        
        /* abort(PROTOERR) the method transaction */		
        abort_method(sm, e->handle, WSP_ABORT_PROTOERR);
	},
	CONNECTING)

ROW(CONNECTING,
	TR_Abort_Ind,
	current_event->u.TR_Abort_Ind.handle == sm->connect_handle,
	{
        /* abort(CONNECTERR) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_CONNECTERR);
        
        /* S-disconnect.ind(abort reason) */
		indicate_disconnect(sm, current_event->u.TR_Abort_Ind.abort_code);
	},
	NULL_SESSION)

ROW(CONNECTING,
	TR_Abort_Ind,
	current_event->u.TR_Abort_Ind.handle != sm->connect_handle,
	{
		WSPClientMethodMachine *msm;
		/* See method state table  */
		msm = find_client_method_machine(sm, e->handle);
		handle_method_event(sm, msm, current_event, pdu);
	},
	CONNECTING)


ROW(CONNECTED,
	S_Disconnect_Req,
	1,
	{
        Octstr *opdu;

        /* abort(DISCONNECT) all method and push transactions */
		abort_methods(sm, WSP_ABORT_DISCONNECT);
        
        /* TR-Invoke.req(Class 0, Disconnect) */
        opdu = make_disconnect_pdu(sm);
        send_invoke(sm, opdu, sm->connect_handle, USER_ACKNOWLEDGEMENT, TRANSACTION_CLASS_0);
        
        /* S-disconnect.ind(USERREQ) */
		indicate_disconnect(sm, WSP_ABORT_USERREQ);
	},
	NULL_SESSION)

ROW(CONNECTED,
	Disconnect_Event,
	1,
	{
        /* abort(DISCONNECT) all method and push transactions */
		abort_methods(sm, WSP_ABORT_DISCONNECT);

        /* S-disconnect.ind(DISCONNECT) */
		indicate_disconnect(sm, WSP_ABORT_DISCONNECT);
	},
	NULL_SESSION)

ROW(CONNECTED,
	S_MethodInvoke_Req,
	1,
	{
		WSPClientMethodMachine *msm;
        /* start a new method invoke with this event (see method state table */
		msm = method_machine_create(sm, current_event->u.S_MethodInvoke_Req.client_transaction_id);
		/* Hand off the event to the new method machine */
		handle_method_event(sm, msm, current_event, pdu);
	},
	CONNECTED)

ROW(CONNECTED,
	S_MethodResult_Res,
	1,
	{
		WSPClientMethodMachine *msm;
		/* See method state table  */
		msm = find_client_method_machine(sm, current_event->u.S_MethodResult_Res.client_transaction_id);
		handle_method_event(sm, msm, current_event, pdu);
	},
	CONNECTED)

ROW(CONNECTED,
	S_MethodAbort_Req,
	1,
	{
		WSPClientMethodMachine *msm;
		/* See method state table  */
		msm = find_client_method_machine(sm, current_event->u.S_MethodAbort_Req.transaction_id);
		handle_method_event(sm, msm, current_event, pdu);
	},
	CONNECTED)

ROW(CONNECTED,
    S_ConfirmedPush_Res,
    1,
    {
        /* See push state table*/
        WSPClientPushMachine *spm;
        spm = find_push_machine(sm, current_event->u.S_ConfirmedPush_Res.client_push_id);
        handle_push_event(sm, spm, current_event, pdu);
    },
    CONNECTED)

ROW(CONNECTED,
    S_PushAbort_Req,
    1,
    {
        /* See push state table*/
        WSPClientPushMachine *spm;
        spm = find_push_machine(sm, current_event->u.S_PushAbort_Req.push_id);
        handle_push_event(sm, spm, current_event, pdu);
    },
    CONNECTED)

ROW(CONNECTED,
	S_Suspend_Req,
	1,
	{
        Octstr *opdu;

        /* abort(DISCONNECT) all method and push transactions */
		abort_methods(sm, WSP_ABORT_DISCONNECT);
        
        /* TR-Invoke.req(Class 0, Suspend) */
        opdu = make_suspend_pdu(sm);
        send_invoke(sm, opdu, sm->connect_handle, USER_ACKNOWLEDGEMENT, TRANSACTION_CLASS_0);
        
        /* S-Suspend.ind(USERREQ) */
		indicate_suspend(sm, WSP_ABORT_USERREQ);
	},
	SUSPENDED)

ROW(CONNECTED,
	Suspend_Event,
	! resume_enabled,
	{
        /* abort(DISCONNECT) all method and push transactions */
		abort_methods(sm, WSP_ABORT_DISCONNECT);

        /* S-Disconnect.ind(SUSPEND) */
		indicate_disconnect(sm, WSP_ABORT_SUSPEND);
	},
	NULL_SESSION)

ROW(CONNECTED,
	Suspend_Event,
	resume_enabled,
	{
        /* abort(SUSPEND) all method and push transactions */
		abort_methods(sm, WSP_ABORT_SUSPEND);
        
        /* S-Suspend.ind(SUSPEND) */
		indicate_suspend(sm, WSP_ABORT_SUSPEND);
	},
	SUSPENDED)

ROW(CONNECTED,
	S_Resume_Req,
	1,
	{
        Octstr *opdu;

        /* abort(USERREQ) all method and push transactions */
		abort_methods(sm, WSP_ABORT_USERREQ);

        /* Bind session tot he new peer address quadruplet */
        sm->addr_tuple = wap_addr_tuple_duplicate(current_event->u.S_Resume_Req.addr_tuple);
        
        /* TR-Invoke(Class 2, Resume) */
        opdu = make_resume_pdu(NULL, current_event->u.S_Resume_Req.client_headers);
        send_invoke(sm, opdu, sm->connect_handle, USER_ACKNOWLEDGEMENT, TRANSACTION_CLASS_2);

        /* TODO check whether this is correct */
        sm->resume_handle = current_event->u.S_Resume_Req.session_handle;
	},
	RESUMING)

ROW(CONNECTED,
	TR_Invoke_Ind,
	current_event->u.TR_Invoke_Ind.tcl == 0 && pdu->type == Disconnect,
	{
        /* abort(DISCONNECT) all outstanding method transactions */        
		abort_methods(sm, WSP_ABORT_DISCONNECT);
        
        /* S-disconnect.ind(DISCONNECT) */
		indicate_disconnect(sm, WSP_ABORT_DISCONNECT);
	},
	NULL_SESSION)

ROW(CONNECTED,
	TR_Invoke_Ind,
	current_event->u.TR_Invoke_Ind.tcl == 0 && pdu->type == Push,
	{
        /* S-Push.ind */
        indicate_push(sm, pdu);
	},
	CONNECTED)

ROW(CONNECTED,
	TR_Invoke_Ind,
	current_event->u.TR_Invoke_Ind.tcl == 1 && pdu->type == ConfirmedPush,
	{
       /* Start new push transaction*/
        WSPClientPushMachine *spm;
        spm = client_push_machine_create(sm);
        handle_push_event(sm, spm, current_event, pdu);
	},
	CONNECTED)

ROW(CONNECTED,
	TR_Result_Ind,
	current_event->u.TR_Result_Ind.handle != sm->connect_handle,
	{
		WSPClientMethodMachine *msm;
		/* See method state table  */
		msm = find_client_method_machine(sm, e->handle);
		handle_method_event(sm, msm, current_event, pdu);
	},
	CONNECTED)

ROW(CONNECTED,
	TR_Invoke_Cnf,
	current_event->u.TR_Invoke_Cnf.handle != sm->connect_handle,
	{
		WSPClientMethodMachine *msm;
		/* See method state table  */
		msm = find_client_method_machine(sm, e->handle);
		handle_method_event(sm, msm, current_event, pdu);
	},
	CONNECTED)

/* 
 * Event TR-Abort.ind has a separate flag telling is the indicator the 
 * initiator or the responder.
 */
ROW(CONNECTED,
	TR_Abort_Ind,
	e->handle != sm->connect_handle  && e->ir_flag == RESPONDER_INDICATION,
	{
		WSPClientMethodMachine *msm;

		/* See method state table */
		msm = find_client_method_machine(sm, e->handle);
		handle_method_event(sm, msm, current_event, pdu);
	},
	CONNECTED)

ROW(CONNECTED,
	TR_Abort_Ind,
	e->handle != sm->connect_handle && e->ir_flag == INITIATOR_INDICATION,
	{
		WSPClientPushMachine *m;

		/* See push state table */
		m = find_push_machine(sm, e->handle);
		handle_push_event(sm, m, current_event, pdu);
	},
	CONNECTED)


ROW(SUSPENDED,
	S_Disconnect_Req,
	1,
	{
        /* S-Disconnect.ind(USERREQ) */
        indicate_disconnect(sm, WSP_ABORT_USERREQ);
	},
	NULL_SESSION)

ROW(SUSPENDED,
	Disconnect_Event,
	1,
	{
        /* S-Disconnect.ind(DISCONNECT) */
        indicate_disconnect(sm, WSP_ABORT_DISCONNECT);
	},
	NULL_SESSION)

ROW(SUSPENDED,
	S_Resume_Req,
	1,
	{
        Octstr *opdu;

        /* TR-Invoke.req(Class 2, Resume) */
        opdu = make_resume_pdu(NULL, current_event->u.S_Resume_Req.client_headers);
        send_invoke(sm, opdu, sm->connect_handle, USER_ACKNOWLEDGEMENT, TRANSACTION_CLASS_2);

        /* TODO check whether this is correct */
        sm->resume_handle = current_event->u.S_Resume_Req.session_handle;

	},
	RESUMING)

ROW(SUSPENDED,
	TR_Invoke_Ind,
	current_event->u.TR_Invoke_Ind.tcl == 0 && pdu->type == Disconnect,
	{
        /* S-disconnect.ind(DISCONNECT) */
		indicate_disconnect(sm, WSP_ABORT_DISCONNECT);
	},
	NULL_SESSION)

ROW(SUSPENDED,
	TR_Invoke_Ind,
	current_event->u.TR_Invoke_Ind.tcl == 1 && pdu->type == ConfirmedPush,
	{
        /* TR-Abort.req(SUSPEND) the TR-Invoke */
		send_abort(WSP_ABORT_SUSPEND, current_event->u.TR_Invoke_Ind.handle);
	},
	SUSPENDED)

ROW(RESUMING,
	S_Disconnect_Req,
	1,
	{
        /* TR-Abort,req(DISCONNECT) the Resume */
		send_abort(WSP_ABORT_DISCONNECT, sm->resume_handle);

        /* abort(DISCONNECT) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_DISCONNECT);
        
        /* S-disconnect.ind(USERREQ) */
		indicate_disconnect(sm, WSP_ABORT_USERREQ);
    },
	NULL_SESSION)

ROW(RESUMING,
	Disconnect_Event,
	1,
	{
        /* TR-Abort,req(DISCONNECT) the Resume */
		send_abort(WSP_ABORT_DISCONNECT, sm->resume_handle);

        /* abort(DISCONNECT) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_DISCONNECT);

        /* S-disconnect.ind(DISCONNECT) */
		indicate_disconnect(sm, WSP_ABORT_DISCONNECT);
	},
	NULL_SESSION)

ROW(RESUMING,
	S_MethodInvoke_Req,
	1,
	{
		WSPClientMethodMachine *msm;
        /* start a new method invoke with this event (see method state table */
		msm = method_machine_create(sm, current_event->u.S_MethodInvoke_Req.client_transaction_id);
		/* Hand off the event to the new method machine */
		handle_method_event(sm, msm, current_event, pdu);
	},
	RESUMING)


ROW(RESUMING,
	S_MethodAbort_Req,
	1,
	{
		WSPClientMethodMachine *msm;
		/* See method state table  */
		msm = find_client_method_machine(sm, current_event->u.S_MethodAbort_Req.transaction_id);
		handle_method_event(sm, msm, current_event, pdu);
	},
	RESUMING)


ROW(RESUMING,
	S_Suspend_Req,
	1,
	{
        Octstr *opdu;

        /* TR-Abort,req(SUSPEND) the Resume */
		send_abort(WSP_ABORT_SUSPEND, sm->resume_handle);

        /* abort(SUSPEND) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_SUSPEND);

        /* TR-Invoke.req(Class 0, Suspend) */
        opdu = make_suspend_pdu(sm);
        send_invoke(sm, opdu, sm->connect_handle, USER_ACKNOWLEDGEMENT, TRANSACTION_CLASS_0);
        
        /* S-Suspend.ind(USERREQ) */
		indicate_suspend(sm, WSP_ABORT_USERREQ);
	},
	SUSPENDED)

ROW(RESUMING,
	Suspend_Event,
	1,
	{
        /* TR-Abort,req(SUSPEND) the Resume */
		send_abort(WSP_ABORT_SUSPEND, sm->resume_handle);

        /* abort(SUSPEND) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_SUSPEND);

        /* S-Suspend.ind(SUSPEND) */
        indicate_suspend(sm, WSP_ABORT_SUSPEND);
	},
	SUSPENDED)

ROW(RESUMING,
	TR_Invoke_Ind,
	current_event->u.TR_Invoke_Ind.tcl == 0 && pdu->type == Disconnect,
	{
        /* TR-Abort,req(DISCONNECT) the Resume */
		send_abort(WSP_ABORT_DISCONNECT, sm->resume_handle);
        
        /* abort(DISCONNECT) all outstanding method transactions */        
		abort_methods(sm, WSP_ABORT_DISCONNECT);
        
        /* S-disconnect.ind(DISCONNECT) */
		indicate_disconnect(sm, WSP_ABORT_DISCONNECT);
	},
	NULL_SESSION)

ROW(RESUMING,
	TR_Invoke_Ind,
	current_event->u.TR_Invoke_Ind.tcl == 1 && pdu->type == ConfirmedPush,
	{
        /* TR-Abort.req(SUSPEND) the TR-Invoke */
		send_abort(WSP_ABORT_SUSPEND, current_event->u.TR_Invoke_Ind.handle);
	},
	RESUMING)

ROW(RESUMING,
	TR_Result_Ind,
	current_event->u.TR_Result_Ind.handle == sm->resume_handle && pdu->type == Reply && pdu->u.Reply.status == 0x20 /* (status==ok) */,
	{
        /* TR-Result.res */
		sendResultResponse(current_event->u.TR_Result_Ind.handle);

        /* S-Resume.cnf */
        confirm_resume(sm, wsp_headers_unpack(pdu->u.Reply.headers, 1)); 
	},
	CONNECTED)

ROW(RESUMING,
	TR_Result_Ind,
	current_event->u.TR_Result_Ind.handle == sm->resume_handle && pdu->type == Reply && pdu->u.Reply.status != 0x20 /* (status!=ok) */,
	{
        /* TR-Result.res */
		sendResultResponse(current_event->u.TR_Result_Ind.handle);

        /* abort(DISCONNECT) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_DISCONNECT);
        
        /* TODO: how to fill in reply parameters S-disconnect.ind(Reply parameters) */        
        /* S-Disconnect.ind(Reply parameters) */
		indicate_disconnect(sm, WSP_ABORT_DISCONNECT);
	},
	NULL_SESSION)

ROW(RESUMING,
	TR_Result_Ind,
	current_event->u.TR_Result_Ind.handle != sm->connect_handle,
	{
        /* abort (PROTOERR) the method transaction */
        abort_method(sm, current_event->u.TR_Result_Ind.handle, WSP_ABORT_PROTOERR);
	},
	RESUMING)

ROW(RESUMING,
	TR_Invoke_Cnf,
	current_event->u.TR_Invoke_Cnf.handle != sm->connect_handle,
	{
        /* abort (PROTOERR) the method transaction */
        abort_method(sm, current_event->u.TR_Result_Ind.handle, WSP_ABORT_PROTOERR);
	},
	RESUMING)

ROW(RESUMING,
	TR_Abort_Ind,
	current_event->u.TR_Abort_Ind.handle == sm->resume_handle && current_event->u.TR_Abort_Ind.abort_code == WSP_ABORT_DISCONNECT,
	{
        /* abort(DISCONNECT) all outstanding method transactions */
		abort_methods(sm, WSP_ABORT_DISCONNECT);
        
        /* S-disconnect.ind(DISCONNECT) */
		indicate_disconnect(sm, WSP_ABORT_DISCONNECT);
	},
	NULL_SESSION)

ROW(RESUMING,
	TR_Abort_Ind,
	current_event->u.TR_Abort_Ind.handle == sm->resume_handle && current_event->u.TR_Abort_Ind.abort_code != WSP_ABORT_DISCONNECT,
	{
        /* abort(SUSPEND) all outstanding method transactions */        
		abort_methods(sm, WSP_ABORT_SUSPEND);

        /* S-Suspend.ind(abort reason) */
        indicate_suspend(sm, current_event->u.TR_Abort_Ind.abort_code);
	},
	SUSPENDED)

ROW(RESUMING,
	TR_Abort_Ind,
	e->handle != sm->connect_handle,
	{
		WSPClientMethodMachine *msm;

		/* See method state table */
		msm = find_client_method_machine(sm, e->handle);
		handle_method_event(sm, msm, current_event, pdu);
	},
    RESUMING)



#undef ROW
#undef STATE_NAME




                \
