#include "./include/sip_ua_pub.h"
#include "./include/sip.h"
#include "./include/sip_ua_const.h"
#include "./include/sip_ua_struct.h"
#include "./include/sip_ua_ext.h"
#include "./include/sip_ua_dialog.h"
#include "./include/sip_ua.h"
#include "./include/sip_ext.h"
#include "./include/sip_debug.h"

extern int sip_ua_set_dialog_state(WORD dialogId, BYTE state);
extern int sip_ua_update_dialog_with_rsp(WORD dialogId, SIP_API_STRUCT * sipApiMsg, BYTE flag);

WORD sip_ua_find_rsp_method(BYTE method, WORD resp)
{

    switch(method)
    {
    case SIP_METHOD_INVITE:
        if (resp == 180)
            return SIP_METHOD_RESPONSE_INV_RINGING;
        else if ((resp > 100) && (resp < 200))
            return SIP_METHOD_RESPONSE_INV_PROGRESS;
        else if ((resp >= 200) && (resp < 300))
            return SIP_METHOD_RESPONSE_INV_CONFIRM;
        else if ((resp >= 300) && (resp < 400))
            return SIP_METHOD_RESPONSE_INV_REDIR;
        else
            return SIP_METHOD_RESPONSE_INV_REL;
        break;
    case SIP_METHOD_RE_INVITE:
        if (resp == 180)
            return SIP_METHOD_RESPONSE_RE_INV_RINGING;
        else if ((resp > 100) && (resp < 200))
            return SIP_METHOD_RESPONSE_RE_INV_PROGRESS;
        else if ((resp >= 200) && (resp < 300))
            return SIP_METHOD_RESPONSE_RE_INV_CONFIRM;
        else if (resp == 491)
            return SIP_METHOD_RESPONSE_RE_INV_RETRANSMIT;
        else if ((481 == resp) || (408 == resp) || (99 == resp))
            return SIP_METHOD_RESPONSE_RE_INV_RELEASE;
        else
            return SIP_METHOD_RESPONSE_RE_INV_FAILED;
        break;
    case SIP_METHOD_UPDATE:
        if ((resp >= 200) && (resp < 300))
            return SIP_METHOD_RESPONSE_UPDATE_CONFIRM;
        else
            return SIP_METHOD_RESPONSE_UPDATE_FAILED;
        break;
    case SIP_METHOD_CANCEL:
        return SIP_METHOD_RESPONSE_CANCEL;
        break;
    case SIP_METHOD_BYE:
        return SIP_METHOD_RESPONSE_BYE;
        break;
    default:
        if ((resp >= 200) && (resp < 300))
            return SIP_METHOD_RESPONSE_CONFIRM;
        else 
            return SIP_METHOD_RESPONSE_REL;
        break;
    }

    return 0;
}

void sip_ua_fsm(WORD port);

DWORD sip_ua_min(DWORD A, DWORD B)
{
    if (A <= B)
        return A;
        
    return B;
}

int sip_ua_inv_req_proc(WORD port)
{
    WORD                rvCmd;
    WORD                rspMethod;
    WORD                sdCmd;
    SIP_UA_PORT         *sipUaPortPtr;
    BYTE                  cgId;//BYTE                sapIndex;
    WORD                usrPort;
    WORD                dialogId;
    WORD                sipTransId;
    BYTE                  sdMethod;
    SIP_API_STRUCT      *sipApiMsg;
    SIP_EVENTHANDLE_STRUCT *event_handle = NULL;

    if (port >= SIP_UA_MAX_NUM_OF_PORT)
    {
        return -1;
    }

    sipUaPortPtr = &sipUaPort[port];
    rvCmd = sipUaPortPtr->rcvCmd;
    sipTransId = sipUaPortPtr->llPort;
    sdCmd = sipUaPortPtr->sdCmd;
    cgId = sipUaPortPtr->cgId;//sapIndex = sipUaPortPtr->sapIndex;
    dialogId = sipUaPortPtr->dialogId;
    usrPort = sipUaPortPtr->usrPort;

    if ((event_handle = (SIP_EVENTHANDLE_STRUCT *)pal_get_handler(cgId)) == NULL)
    {
        sprintf((char *)sipAsciTempBuf, "sip_ua_inv_req_proc: can not find handle function!\r\n");
        sip_log_err(sipAsciTempBuf);
        return -1;
    }

    switch (sipUaPortPtr->state)
    {
    case SIP_UA_SEND_REQUEST:
        if (sdCmd != SIP_UA_SEND_COM_NULL)
        {
            sipUaPortPtr->sdCmd  = SIP_UA_SEND_COM_NULL;
            sipApiMsg = &sipUaPortPtr->sdSipMsg;
        
            if (sip_send_msg(sipTransId, port, SIP_TRANSACTION_LAYER, sipApiMsg) < 0)
                return 1;
        
            sipUaPortPtr->state = SIP_UA_REQ_WAIT_FOR_RSP;
            sipUaPortPtr->timer = SIP_UA_TIMER_WAIT_INV_RSP;
        }
        break;
    case SIP_UA_REQ_WAIT_FOR_RSP:
        if (rvCmd != SIP_UA_RECV_COM_NULL)
        {
            sdMethod = sipUaPortPtr->sdSipMsg.sipApiMsg.sipMsg.sipStartLine.sipMethod;

            sipUaPortPtr->rcvCmd = SIP_UA_RECV_COM_NULL;

/*            if (sapIndex >= SIP_UA_MAX_NUM_OF_SAP)
            {
                return 1;
            }*/

            rspMethod = sip_ua_find_rsp_method(sdMethod, rvCmd);

            if ((rvCmd >= 200) && (rvCmd < 300))
            {
                sip_ua_set_dialog_state(dialogId, SIP_UA_DIALOG_CONFIRMED);
                sip_ua_update_dialog_with_rsp(dialogId, &sipUaRecvMsg, 1);
                event_handle->recv_resp(usrPort, dialogId, rspMethod, &sipUaRecvMsg);
//                sipUaSaps[sapIndex].sipUaSap.recv_resp(usrPort, dialogId, rspMethod, &sipUaRecvMsg);
                return 1;
            }
            else if ((rvCmd >= 100) && (rvCmd < 200))
            {
                if (rvCmd != 100)
                {
                    sip_ua_set_dialog_state(dialogId, SIP_UA_DIALOG_EARLY);
                    sip_ua_update_dialog_with_rsp(dialogId, &sipUaRecvMsg, 1);
                    sipUaPortPtr->timer = SIP_UA_TIMER_WAIT_INV_200_RSP;
                    event_handle->recv_resp(usrPort, dialogId, rspMethod, &sipUaRecvMsg);
//                    sipUaSaps[sapIndex].sipUaSap.recv_resp(usrPort, dialogId, rspMethod, &sipUaRecvMsg);
                }

                return 0;
            }
            else
            {
                if ((sdMethod == SIP_METHOD_RE_INVITE) && (rspMethod != SIP_METHOD_RESPONSE_RE_INV_RELEASE))
                {
                    return 1;
                }

                sipUaPortPtr->state = SIP_UA_INV_REQ_TERMINATED;
                sipUaPortPtr->timer = SIP_UA_TIMER_T3;
                sip_ua_set_dialog_state(dialogId, SIP_UA_DIALOG_DESTROY);        
                event_handle->recv_resp(usrPort, dialogId, rspMethod, &sipUaRecvMsg);
                return 0;
            }
        }
        
        if (sipUaPortPtr->timer > 0)
        {
            if (--sipUaPortPtr->timer == 0)
            {
                event_handle->recv_resp(usrPort, dialogId, SIP_METHOD_RESPONSE_TIMEOUT, NULL);
//                sipUaSaps[sapIndex].sipUaSap.recv_resp(usrPort, dialogId, SIP_METHOD_RESPONSE_TIMEOUT, NULL);
                sip_ua_init_dialog(dialogId);
                return 1;
            }
        }
        break;
    case SIP_UA_INV_REQ_TERMINATED:
        if (sipUaPortPtr->timer > 0)
        {
            if (--sipUaPortPtr->timer == 0)
            {
                sip_ua_init_dialog(dialogId);
                return 1;
            }
        }
        break;
    default:
        return 1;
    }

    return 0;
}

int sip_ua_non_inv_req_proc(WORD port)
{
    WORD                rvCmd;
    WORD                rspMethod;
    WORD                sdCmd;
    SIP_UA_PORT           *sipUaPortPtr;
    BYTE                  cgId;//BYTE                sapIndex;
    WORD                usrPort;
    WORD                dialogId;
    WORD                sipTransId;
    SIP_API_STRUCT  *sipApiMsg;
    BYTE                sdMethod;
    SIP_EVENTHANDLE_STRUCT *event_handle = NULL;

    if (port >= SIP_UA_MAX_NUM_OF_PORT)
    {
        return -1;
    }

    sipUaPortPtr = &sipUaPort[port];
    sdCmd = sipUaPortPtr->sdCmd;
    rvCmd = sipUaPortPtr->rcvCmd;
    sipTransId = sipUaPortPtr->llPort;
    cgId = sipUaPortPtr->cgId;//sapIndex = sipUaPortPtr->sapIndex;
    dialogId = sipUaPortPtr->dialogId;
    usrPort = sipUaPortPtr->usrPort;

    if ((event_handle = (SIP_EVENTHANDLE_STRUCT *)pal_get_handler(cgId)) == NULL)
    {
        sprintf((char *)sipAsciTempBuf, "sip_ua_non_inv_req_proc: can not find handle function!\r\n");
        sip_log_err(sipAsciTempBuf);
        return -1;
    }

    switch (sipUaPortPtr->state)
    {
    case SIP_UA_SEND_REQUEST:
        if (sdCmd != SIP_UA_SEND_COM_NULL)
        {
            sipApiMsg = &sipUaPortPtr->sdSipMsg;
            sipUaPortPtr->sdCmd = SIP_UA_SEND_COM_NULL;
        
            if (sip_send_msg(sipTransId, port, SIP_TRANSACTION_LAYER, sipApiMsg) < 0)
                return 1;

            if (sipApiMsg->sipApiMsg.sipMsg.sipStartLine.sipMethod == SIP_METHOD_ACK)
            {
                sipUaPortPtr->timer2 = SIP_UA_TIMER_T1;
            }

            sipUaPortPtr->state = SIP_UA_REQ_WAIT_FOR_RSP;
            sipUaPortPtr->timer = SIP_UA_TIMER_WAIT_NON_INV_RSP;
        }
        break;
    case SIP_UA_REQ_WAIT_FOR_RSP:
        sdMethod = sipUaPortPtr->sdSipMsg.sipApiMsg.sipMsg.sipStartLine.sipMethod;

        if (rvCmd != SIP_UA_RECV_COM_NULL)
        {
            sipUaPortPtr->rcvCmd = SIP_UA_RECV_COM_NULL;

/*            if (sapIndex >= SIP_UA_MAX_NUM_OF_SAP)
            {
                return 1;
            }*/

            rspMethod = sip_ua_find_rsp_method(sdMethod, rvCmd);

            if (sdMethod == SIP_METHOD_ACK)
            {
                if ((rvCmd == 200) && (sipUaRecvMsg.sipApiMsg.sipMsg.sipHdrCseq.method == SIP_METHOD_INVITE))
                {
                    sip_ua_update_dialog_with_rsp(dialogId, &sipUaRecvMsg, 1);//need forward
                    sip_ua_update_retransmit_para(dialogId, &sipUaPortPtr->sdSipMsg);
                    sip_send_msg(sipTransId, port, SIP_TRANSPORT_LAYER, &sipUaPortPtr->sdSipMsg);
                }
            }
            else if (sdMethod == SIP_METHOD_BYE)
            {
                event_handle->recv_resp(usrPort, dialogId, rspMethod, &sipUaRecvMsg);
//                sipUaSaps[sapIndex].sipUaSap.recv_resp(usrPort, dialogId, rspMethod, &sipUaRecvMsg);
                sip_ua_init_dialog(dialogId);
                return 1;
            }
            else if ((rvCmd >= 200) || (rvCmd <= SIP_TRANSACTION_TIMEOUT))
            {
                event_handle->recv_resp(usrPort, dialogId, rspMethod, &sipUaRecvMsg);
//                sipUaSaps[sapIndex].sipUaSap.recv_resp(usrPort, dialogId, rspMethod, &sipUaRecvMsg);
                return 1;
            }
        }

        if (sdMethod == SIP_METHOD_ACK)
        {
            if ((sipUaPortPtr->timer2 > 0))
            {
                if (--sipUaPortPtr->timer2 == 0)
                    return 1;
            }
                
            return 0;
        }

        if (sipUaPortPtr->timer > 0)
        {
            if (--sipUaPortPtr->timer == 0)
            {
                event_handle->recv_resp(usrPort, dialogId, SIP_METHOD_RESPONSE_TIMEOUT, NULL);
//                sipUaSaps[sapIndex].sipUaSap.recv_resp(usrPort, dialogId, SIP_METHOD_RESPONSE_TIMEOUT, NULL);

                if (sdMethod == SIP_METHOD_BYE)
                    sip_ua_init_dialog(dialogId);

                return 1;
            }
        }
        break;
    default:
        return 1;
    }

    return 0;
}

int sip_ua_inv_rsp_proc(WORD port)
{
    WORD                rvCmd;
    WORD                sdCmd;
    SIP_UA_PORT           *sipUaPortPtr;
    BYTE                  cgId;//BYTE                sapIndex;
    WORD                usrPort;
    WORD                dialogId;
    WORD                sipTransId;
    BYTE                  sdMethod;
    SIP_API_STRUCT  *sipApiMsg;
    SIP_EVENTHANDLE_STRUCT *event_handle = NULL;

    if (port >= SIP_UA_MAX_NUM_OF_PORT)
    {
        return -1;
    }

    sipUaPortPtr = &sipUaPort[port];
    cgId = sipUaPortPtr->cgId;//sapIndex = sipUaPortPtr->sapIndex;
    usrPort = sipUaPortPtr->usrPort;
    rvCmd = sipUaPortPtr->rcvCmd;
    sdCmd = sipUaPortPtr->sdCmd;
    sipTransId = sipUaPortPtr->llPort;
    sipApiMsg = &sipUaPortPtr->sdSipMsg;
    dialogId = sipUaPortPtr->dialogId;

    if ((event_handle = (SIP_EVENTHANDLE_STRUCT *)pal_get_handler(cgId)) == NULL)
    {
        sprintf((char *)sipAsciTempBuf, "sip_ua_non_inv_req_proc: can not find handle function!\r\n");
        sip_log_err(sipAsciTempBuf);
        return -1;
    }

    switch (sipUaPortPtr->state)
    {
    case SIP_UA_SEND_RESPONSE:
         if (sdCmd != SIP_UA_SEND_COM_NULL)
         {
             sipUaPortPtr->sdCmd = SIP_UA_SEND_COM_NULL;
             sdMethod = sipApiMsg->sipApiMsg.sipMsg.sipStartLine.sipMethod;

             if (sip_send_msg(sipTransId, port, SIP_TRANSACTION_LAYER, sipApiMsg) < 0)
                 return 1;

             if ((sdCmd > 100) && (sdCmd < 200))
             {
                 if (sdMethod == SIP_METHOD_INVITE)
                 {
                     sip_ua_set_dialog_state(dialogId, SIP_UA_DIALOG_EARLY);
                 }
             }
             else if ((sdCmd >= 200) && (sdCmd < 300))
             {
                 if (sdMethod == SIP_METHOD_INVITE)
                 {
                     sip_ua_set_dialog_state(dialogId, SIP_UA_DIALOG_CONFIRMED);
                 }
                 sipUaPortPtr->state = SIP_UA_INV_200_RSP_WAIT_FOR_ACK;

                 sipUaPortPtr->retrans200Times = 0;
                 sipUaPortPtr->timer = SIP_UA_TIMER_WAIT_ACK;
                 sipUaPortPtr->waitAckTimer = SIP_UA_TIMER_RETRANSMIT_200;
             }
             else if (sdCmd >= 300)
             {
                 if ((sdMethod == SIP_METHOD_RE_INVITE) && (sdCmd != 481) && (sdCmd != 408))
                 {
                     return 1;
                 }
                 else
                 {
                     sipUaPortPtr->state = SIP_UA_INV_RSP_TERMINATED;
                     sipUaPortPtr->timer = SIP_UA_TIMER_T2;
                     sip_ua_set_dialog_state(dialogId, SIP_UA_DIALOG_DESTROY);
                 }
             }
         }

         if ((sipUaPortPtr->state == SIP_UA_SEND_RESPONSE) && (sipUaPortPtr->timer > 0))
         {
            if (--sipUaPortPtr->timer == 0)
            {
                sip_ua_init_dialog(dialogId);
                return 1;
            }
         }
         break;
    case SIP_UA_INV_200_RSP_WAIT_FOR_ACK:
        if (rvCmd != SIP_UA_RECV_COM_NULL)
        {
            sipUaPortPtr->rcvCmd= SIP_UA_RECV_COM_NULL;
            
/*            if (sapIndex >= SIP_UA_MAX_NUM_OF_SAP)
            {
                return -1;
            }*/

            if (rvCmd == SIP_METHOD_ACK)
            {
                event_handle->recv_req(port, usrPort, dialogId, rvCmd, &sipUaRecvMsg, NULL);
//                sipUaSaps[sapIndex].sipUaSap.recv_req(port, usrPort, dialogId, rvCmd, &sipUaRecvMsg, NULL);
                return 1;
            }
        }

        if (sipUaPortPtr->waitAckTimer > 0)
        {
            if (--sipUaPortPtr->waitAckTimer == 0)
            {
                sip_send_msg(sipTransId, port, SIP_TRANSPORT_LAYER, sipApiMsg);
                sipUaPortPtr->retrans200Times++;
                sipUaPortPtr->waitAckTimer = sip_ua_min((1 << sipUaPortPtr->retrans200Times) * SIP_UA_TIMER_RETRANSMIT_200, SIP_UA_TIMER_T1);
            }
        }

        if (sipUaPortPtr->timer > 0)
        {
            if (--sipUaPortPtr->timer == 0)
            {
                //need forward develop  send bye
                event_handle->recv_resp(usrPort, dialogId, SIP_METHOD_RESPONSE_TIMEOUT, NULL);
//                sipUaSaps[sapIndex].sipUaSap.recv_resp(usrPort, dialogId, SIP_METHOD_RESPONSE_TIMEOUT, NULL);
                return 1;
            }
        }
        break;
    case SIP_UA_INV_RSP_TERMINATED:
        if (sipUaPortPtr->timer > 0)
        {
            if (--sipUaPortPtr->timer == 0)
            {
                sip_ua_init_dialog(dialogId);
                return 1;
            }
        }
        break;
    default:
        return 1;
    }

    return 0;

}

int sip_ua_non_inv_rsp_proc(WORD port)
{
    WORD                rvCmd;
    WORD                sdCmd;
    SIP_UA_PORT           *sipUaPortPtr;
    WORD                sipTransId;
    SIP_API_STRUCT  *sipApiMsg;

    if (port >= SIP_UA_MAX_NUM_OF_PORT)
    {
        return -1;
    }

    sipUaPortPtr = &sipUaPort[port];
    rvCmd = sipUaPortPtr->rcvCmd;
    sdCmd = sipUaPortPtr->sdCmd;
    sipTransId = sipUaPortPtr->llPort;
    sipApiMsg = &sipUaPortPtr->sdSipMsg;

    switch (sipUaPortPtr->state)
    {
    case SIP_UA_SEND_RESPONSE:
        if (sdCmd != SIP_UA_SEND_COM_NULL)
        {
            sipUaPortPtr->sdCmd = SIP_UA_SEND_COM_NULL;

            sip_send_msg(sipTransId, port, SIP_TRANSACTION_LAYER, sipApiMsg);

            return 1;
        }
        break;
    default:
        return 1;
    }
    return 0;
}

void sip_ua_fsm_request(WORD port)
{
        switch (sipUaPort[port].mainState)
        {
        case SIP_UA_MAIN_STATE_INVITE_REQ:
            switch (sip_ua_inv_req_proc(port))
            {
            case 0:
                break;
            case 1:
                sipUaPort[port].mainState = SIP_UA_MAIN_STATE_INIT;
                sip_ua_fsm(port);
                break;
            default:
                break;
            }
            break;
        case SIP_UA_MAIN_STATE_NON_INVITE_REQ:
            switch (sip_ua_non_inv_req_proc(port))
            {
            case 0:
                break;
            case 1:
                sipUaPort[port].mainState = SIP_UA_MAIN_STATE_INIT;
                sip_ua_fsm(port);
                break;
            default:
                break;
            }
            break;
        case SIP_UA_MAIN_STATE_INIT:
            sip_ua_init_port(port);
            break;
        }
}

void sip_ua_fsm_indicate(WORD port)
{
        switch (sipUaPort[port].mainState)
        {
        case SIP_UA_MAIN_STATE_IDLE:
            if (sipUaPort[port].sdCmd != 0)
            {
                if ((sipUaPort[port].rcvCmd == SIP_METHOD_INVITE) || (sipUaPort[port].rcvCmd == SIP_METHOD_RE_INVITE))
                {
                    sipUaPort[port].rcvCmd = SIP_UA_RECV_COM_NULL;
                    sipUaPort[port].mainState = SIP_UA_MAIN_STATE_INVITE_RSP;
                }
                else
                {
                    sipUaPort[port].rcvCmd = SIP_UA_RECV_COM_NULL;
                    sipUaPort[port].mainState = SIP_UA_MAIN_STATE_NON_INVITE_RSP;
                }
                sip_ua_fsm(port);
                break;
            }
            
            if (sipUaPort[port].sipuafTimer > 0)
            {
                if (--sipUaPort[port].sipuafTimer == 0)
                {
                    sipUaPort[port].mainState = SIP_UA_MAIN_STATE_INIT;
                    sip_ua_fsm(port);
                }
            }
            break;
        case SIP_UA_MAIN_STATE_INVITE_RSP:
            switch (sip_ua_inv_rsp_proc(port))
            {
            case 0:
                break;
            case 1:
                sipUaPort[port].mainState = SIP_UA_MAIN_STATE_INIT;
                sip_ua_fsm(port);
                break;
            default:
                break;
            }
            break;
        case SIP_UA_MAIN_STATE_NON_INVITE_RSP:
            switch (sip_ua_non_inv_rsp_proc(port))
            {
            case 0:
                break;
            case 1:
                sipUaPort[port].mainState = SIP_UA_MAIN_STATE_INIT;
                sip_ua_fsm(port);
                break;
            default:
                break;
            }
            break;
        case SIP_UA_MAIN_STATE_INIT:
            sip_ua_init_port(port);
            break;
        }
}

void sip_ua_fsm(WORD port)
{
    if (port >= SIP_UA_MAX_NUM_OF_PORT)
    {
        return;
    }

    switch(sipUaPort[port].sipuafState)
    {
    case SIP_UA_FSM_STATE_REQUEST:
     sip_ua_fsm_request(port);
        break;
    case SIP_UA_FSM_STATE_INDICATE:
        sip_ua_fsm_indicate(port);
        break;
    default:
        sip_ua_init_port(port);
        break;
    }
}
