#include "stdafx.h"
#include "cbridge.h"

// SOURCE_Q931_INFO methods

/* virtual */
SOURCE_Q931_INFO::~SOURCE_Q931_INFO(
    )
{
}


// this should never get called, but needs to be supported
// as the base class implementation is pure virtual
// virtual
HRESULT SOURCE_Q931_INFO::AcceptCallback (
    IN	DWORD			Status,
    IN	SOCKET			Socket,
	IN	SOCKADDR_IN *	LocalAddress,
	IN	SOCKADDR_IN *	RemoteAddress)
{
    // we should never receive an accept call back for the 
    // Q931 source instance
    _ASSERTE(FALSE);

    return E_UNEXPECTED;
}


// This function is called by the event manager.
// The caller will free the PDU. This function may modify
// some of the fields of the PDU.

// this is called when an async receive operation completes
// virtual
HRESULT SOURCE_Q931_INFO::ReceiveCallback (
    IN      Q931_MESSAGE             *pQ931Message,
    IN      H323_UserInformation     *pH323UserInfo
    )
{
    HRESULT HResult;
    
    // we must have valid decoded PDUs 
    _ASSERTE(NULL != pQ931Message);

    // The ASN.1 part is not present in the case of some PDUs
    //_ASSERTE(NULL != pH323UserInfo);

    // if RELEASE COMPLETE PDU
    if (pH323UserInfo != NULL &&
        releaseComplete_chosen ==
            pH323UserInfo->h323_uu_pdu.h323_message_body.choice)
    {
        DebugF (_T("Q931: 0x%x caller sent 'Release Complete'.\n"), &GetCallBridge ());
        HResult = HandleReleaseCompletePDU(
                    pQ931Message,
                    pH323UserInfo
                    );

		return HResult;
    }

    // check current state and handle the incoming PDU
    switch(m_Q931SourceState)
    {
    case Q931_SOURCE_STATE_CON_ESTD:
        {
            // processes PDUs when in Q931_SOURCE_STATE_CON_ESTD state
            HResult = HandleStateSrcConEstd(
                        pQ931Message,
                        pH323UserInfo
                        );
        }
        break;

    case Q931_SOURCE_STATE_SETUP_RCVD:
        {
            // Pass on the PDU to the Q931 destination instance which
            // passes it on after due modifications
            HResult = GetDestQ931Info().ProcessSourcePDU(
                        pQ931Message,
                        pH323UserInfo
                        );
        }
        break;

    case Q931_SOURCE_STATE_INIT:
    case Q931_SOURCE_STATE_REL_COMP_RCVD:
    default:
        {
            // we can't be in Q931_SOURCE_STATE_INIT as we wouldn't have
            // queued an async receive by then

            // we can't be in Q931_SOURCE_STATE_REL_COMP_RCVD as we not have
            // queued this receive

          // I.K. 0819999  _ASSERTE(FALSE);
            HResult = E_UNEXPECTED;
        }
        break;
    };

    // if there is an error
    if (FAILED(HResult))
    {
        goto shutdown;
    }

    // we must queue an async receive irrespective of whether
    // the PDU was dropped (IPTEL_E_INVALID_PDU == HResult)
    // queue an async receive
    HResult = QueueReceive();
    if (FAILED(HResult))
    {
        goto shutdown;
    }

    return HResult;

shutdown:

    // initiate shutdown
    GetCallBridge().Terminate ();

    return HResult;
}


// handles RELEASE_COMPLETE PDUs
HRESULT 
SOURCE_Q931_INFO::HandleReleaseCompletePDU(
    IN      Q931_MESSAGE             *pQ931Message,
    IN      H323_UserInformation     *pH323UserInfo 
    )
{
    // it must be a release complete PDU
    _ASSERTE(releaseComplete_chosen == \
                pH323UserInfo->h323_uu_pdu.h323_message_body.choice);

    // we can handle a RELEASE COMPLETE PDU in any state except the following
    _ASSERTE(Q931_SOURCE_STATE_INIT             != m_Q931SourceState);
    _ASSERTE(Q931_SOURCE_STATE_REL_COMP_RCVD    != m_Q931SourceState);

    // pass on the pdu to the Q931 source instance
    // ignore return error code, if any
    GetDestQ931Info().ProcessSourcePDU(
        pQ931Message,
        pH323UserInfo
        );

    // state transition to Q931_SOURCE_STATE_REL_COMP_RCVD
    m_Q931SourceState = Q931_SOURCE_STATE_REL_COMP_RCVD;

    // initiate shutdown - this cancels the timers, but doesn't close
	// the sockets. the sockets are closed when the send callback is made
    GetCallBridge().TerminateCallOnReleaseComplete();

	GetSocketInfo ().Clear (TRUE);

    return S_OK;
}


// processes PDUs when in Q931_SOURCE_STATE_CON_EST state
HRESULT
SOURCE_Q931_INFO::HandleStateSrcConEstd(
    IN      Q931_MESSAGE            *pQ931Message,
    IN      H323_UserInformation    *pH323UserInfo 
    )
{
	if (!pH323UserInfo) {
		DebugF(_T("SOURCE_Q931_INFO::HandleStateSrcConEstd: no UUIE data!  ignoring message.\n"));
		return E_INVALIDARG;
	}

    // we can only handle a setup PDU in this state
    // all other PDUs are THROWN AWAY (as we don't know 
    // whom to pass it to)

    if (setup_chosen != pH323UserInfo->h323_uu_pdu.h323_message_body.choice)
    {
		DebugF(
			_T("SOURCE_Q931_INFO::HandleStateSrcConEstd: received a pdu other than Setup before receiving a Setup, pdu cannot be processed\n"));
        return E_INVALIDARG;
    }

	// save the caller's call reference value now as we may reuse the
	// PDU structure in ProcessSourcePDU
    // The Setup PDU is sent by the originator and so the call reference flag
    // should not be set.
	// -XXX- this should not be an assert!!!! FIX THIS! -- arlied
    _ASSERTE(!(pQ931Message->CallReferenceValue & CALL_REF_FLAG));
	m_CallRefVal = pQ931Message->CallReferenceValue | CALL_REF_FLAG;

    // pass on the setup pdu to the Q931 destination instance
    HRESULT HResult = GetDestQ931Info().ProcessSourcePDU(
                        pQ931Message,
                        pH323UserInfo
                        );

    if (FAILED (HResult))
    {
        return HResult;
    }
    
    // state transition to Q931_SOURCE_STATE_SETUP_RCVD
    m_Q931SourceState = Q931_SOURCE_STATE_SETUP_RCVD;


	// try to create a CALL PROCEEDING PDU
	// if we fail, don't try to recover
	// Q.931 requires that gateways in the call path must identify 
	// themselves to the callee
	Q931_MESSAGE CallProcQ931Message;
	H323_UserInformation CallProcH323UserInfo;
	HResult = Q931EncodeCallProceedingMessage(
					m_CallRefVal,
				    &CallProcQ931Message,
				    &CallProcH323UserInfo
					);

	// try to send a CALL PROCEEDING PDU to the caller
	// if we fail, don't try to recover

    HResult = QueueSend(
				&CallProcQ931Message, 
				&CallProcH323UserInfo);

    return HResult;
}


// TimerValue contains the timer value in seconds, for a timer event
// to be created when a queued send completes
HRESULT 
SOURCE_Q931_INFO::ProcessDestPDU(
    IN      Q931_MESSAGE            *pQ931Message,
    IN      H323_UserInformation    *pH323UserInfo
    )
{
    HRESULT HResult = E_FAIL;

    // handle PDU from the source Q931 instance
    switch(m_Q931SourceState)
    {
    case Q931_SOURCE_STATE_SETUP_RCVD:
        {
			if (connect_chosen == 
				pH323UserInfo->h323_uu_pdu.h323_message_body.choice)
			{
                DebugF (_T("Q931: 0x%x forwarding 'Connect' to caller.\n"), &GetCallBridge ());
				HResult = ProcessConnectPDU(
							pQ931Message, 
							pH323UserInfo
							);
                if (FAILED(HResult))
                {
	                DebugF(_T("SOURCE_Q931_INFO::ProcessDestPDU: ProcessConnectPDU failed, returning %x\n"),
                        HResult);
                    return HResult;
                }
			}
        }
        break;

    case Q931_SOURCE_STATE_INIT:
    case Q931_SOURCE_STATE_CON_ESTD:
    case Q931_SOURCE_STATE_REL_COMP_RCVD:
    default:
        {
			DebugF( _T("SOURCE_Q931_INFO::ProcessDestPDU: bogus state, returning E_UNEXPECTED\n"));
            return E_UNEXPECTED;
        }
        break;
    };

	// Q931 Header - CallReferenceValue
	// pQ931Message->CallReferenceValue = GetCallRefVal();

    // queue async send for the PDU
    HResult = QueueSend(pQ931Message, pH323UserInfo);
    if (HResult != S_OK) {
	    DebugF( _T("SOURCE_Q931_INFO::ProcessDestPDU: failed to queue sendreturning %x\n"), HResult);
		return HResult;
    }
   
    return HResult;
}


// NOTE: CRV modification is handled in ProcessDestPDU
HRESULT 
SOURCE_Q931_INFO::ProcessConnectPDU(
    IN      Q931_MESSAGE             *pQ931Message,
    IN      H323_UserInformation     *pH323UserInfo 
    )
{
	Connect_UUIE *	Connect;
	HRESULT			Result;
	SOCKADDR_IN		H245ListenAddress;

	// it must be a CONNECT PDU
	_ASSERTE(connect_chosen == pH323UserInfo->h323_uu_pdu.h323_message_body.choice);

	Connect = &pH323UserInfo -> h323_uu_pdu.h323_message_body.u.connect;

	// we must have already checked to see if an h245 transport
	// address was specified by the callee in the dest instance
	_ASSERTE(Connect_UUIE_h245Address_present & Connect -> bit_mask);
	_ASSERTE(ipAddress_chosen & Connect -> h245Address.choice);

	// queue an overlapped accept, get ready to accept an incoming
	// connection on the local address/port

    H245ListenAddress.sin_addr.s_addr = htonl (GetCallBridge (). GetSourceInterfaceAddress ());
    H245ListenAddress.sin_port = htons (0);

	Result = GetSourceH245Info().ListenForCaller (&H245ListenAddress);
	if (FAILED (Result))
	{
		DebugF (_T("H245: 0x%x failed to listen for caller.\n"), &GetCallBridge ());

		return Result;
	}
    //_ASSERTE(S_FALSE != HResult);

	// replace the h245 address/port in the connect PDU
	FillTransportAddress (H245ListenAddress, Connect -> h245Address);

    DebugF (_T("H245: 0x%x listens for H.245 connection from caller on %08X:%04X.\n"),
                &GetCallBridge (),
                SOCKADDR_IN_PRINTF (&H245ListenAddress));
            
	return S_OK;
}