// This code is a part of the Telephony Framework C++ Library.
// Copyright (C) 1997 Chris Sells. All rights reserved.
// tLine.cpp: CtLine class implementation

#include "stdafx.h"
#include "tLine.h"
#include "TfxUtil.h"
#include "tCall.h"
#include <algorithm>

using namespace std;

//typedef find< CPointerList::iterator, HCALL >  FindHCall;

#define LINEDEVSTATE_ALL        0x01FFFFFF
#define LINEADDRESSSTATE_ALL    0x000001FF

///////////////////////////////////////////////////////////////////
// Special Win95 Recovery

#if defined(_DEBUG) && TAPI_CURRENT_VERSION == 0x00010004
static LineTapiRecover  g_lineRecover;
#define RecoverPreInitialize() g_lineRecover.PreInitialize()
#define RecoverInitialize(happ) g_lineRecover.Initialize((void*)happ)
#define RecoverShutdown() g_lineRecover.Shutdown()
#else
#define RecoverPreInitialize()
#define RecoverInitialize(happ)
#define RecoverShutdown()
#endif

/////////////////////////////////////////////////////////////////////////////
// Line Operations

CtLine::CtLine()  // = 0
    :
    m_hLine(0),
    m_nLineID(DWORD(-1))
{
    // Add to static list of lines
    AddToLines(this);
}

CtLine::~CtLine()
{
    Close();

    // Remove from static list of lines
    RemoveFromLines(this);
}

HLINE CtLine::GetHandle() const
{
    return m_hLine;
}

DWORD CtLine::GetDeviceID() const
{
    return m_nLineID;
}

void CtLine::AddSink(CtLineSink* pSink)
{
    if( pSink )
    {
        int nEmpty = -1;

        for( int i = 0; i < m_rgSinks.size(); i++ )
        {
            // Found
            if( m_rgSinks[i] == pSink )
            {
                return;
            }
            // Found first empty spot
            else if( nEmpty == -1 && m_rgSinks[i] == 0 )
            {
                nEmpty = i;
            }
        }

        // Add it to the list in the empty spot
        if( nEmpty != -1 )
        {
            m_rgSinks[nEmpty] = pSink;
        }
        // Create a new spot and add it to the list
        else
        {
            m_rgSinks.push_back(pSink);
        }
    }
}

void CtLine::RemoveSink(CtLineSink* pSink)
{
    assert(pSink);

    // If it's in the list, remove it
    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] == pSink )
        {
            m_rgSinks[i] = 0;
            break;
        }
    }
}

BOOL CtLine::IsRequestPending(
    TREQUEST    nRequestID,             // = 0
    DWORD*      pnRequestType) const    // = 0
{
    return m_listRequests.IsRequestPending(nRequestID, pnRequestType);
}

BOOL CtLine::IsRequestTypePending(
    DWORD   nRequestType) const
{
    return m_listRequests.IsRequestTypePending(nRequestType);
}

/////////////////////////////////////////////////////////////////////////////
// Static Line Operations

void CtLine::AddToLines(
    CtLine* pLine)
{
    assert(pLine);
    s_listLines.push_back( pLine );  //.AddTail(pLine);
}

void CtLine::RemoveFromLines(
    CtLine* pLine)
{
    assert(pLine);
    s_listLines.remove( pLine );  //.RemoveAt(s_listLines.Find(pLine));
}

CtLine* CtLine::FromHandle(
    HLINE   hLine)
{
    assert(hLine);

    CtLine*     pLine = 0;
	CPointerList::iterator iter;
	for( iter = s_listLines.begin(); iter != s_listLines.end(); ++iter )
	{
		pLine = (CtLine*)(*iter);
		if( pLine->GetHandle() == hLine )
		{
			return pLine;
		}
	}

    return 0;
}

DWORD CtLine::GetNumDevs()
{
    return s_nNumDevs;
}

HLINEAPP CtLine::GetAppHandle()
{
    return s_hLineApp;
}

/////////////////////////////////////////////////////////////////////////////
// Static Line Wrappers

HLINEAPP    CtLine::s_hLineApp = 0;
DWORD       CtLine::s_dwLoVersion =  0x00010003;    // TAPI v1.3
DWORD       CtLine::s_dwHiVersion = TAPI_CURRENT_VERSION;
CPointerList    CtLine::s_listLines;
DWORD       CtLine::s_nNumDevs = 0;
CtAppSink*  CtLine::s_pAppSink = 0;
DWORD*      CtLine::s_aApiVersions = 0;

void CtLine::NegotiateApiVersions()
{
    assert(!s_aApiVersions);

    if( s_nNumDevs &&
        (s_aApiVersions = new DWORD[s_nNumDevs]) )
    {
        LINEEXTENSIONID dummy;
        for( DWORD nLineID = 0; nLineID < s_nNumDevs; nLineID++ )
        {
            if( TFAILED(::TfxTapiFunc(::lineNegotiateAPIVersion(s_hLineApp,
                                                                nLineID,
                                                                s_dwLoVersion,
                                                                s_dwHiVersion,
                                                                s_aApiVersions + nLineID,
                                                                &dummy),
                                      TDBSTR("lineNegotiateAPIVersion"))) )
            {
                s_aApiVersions[nLineID] = 0;
            }
        }
    }
}

void CtLine::SetAppVersion(
    DWORD   dwLoVersion,
    DWORD   dwHiVersion)
{
    s_dwLoVersion = dwLoVersion;
    s_dwHiVersion = dwHiVersion;
}

DWORD CtLine::GetAppVersion()
{
	return s_dwHiVersion;
}

DWORD CtLine::GetApiVersion(
    DWORD   nLineID)
{
    assert(nLineID < GetNumDevs());

    if( s_aApiVersions )
    {
        return s_aApiVersions[nLineID];
    }
    else
    {
        return 0;
    }
}

TRESULT CtLine::Initialize(
    CtAppSink*  pAppSink,
    LPCSTR      szAppName,
    HINSTANCE   hInst)
{
    // Updated to support console apps, too
    // (as per Valery Arkhangorodsky [valerya@balisoft.com])
    //assert(hInst);
    if( !hInst ) hInst = GetModuleHandle(0);

    RecoverPreInitialize();

    if( !s_hLineApp )
    {
        HLINEAPP    hLineApp;
#if TAPI_CURRENT_VERSION < 0x00020000
        TRESULT tr = ::TfxTapiFunc(::lineInitialize(&hLineApp,
                                                    hInst,
                                                    TfxLineCallback,
                                                    szAppName,
                                                    &s_nNumDevs),
                                    TDBSTR("lineInitialize"));
#else
        DWORD   dwAPIVersion = s_dwHiVersion;
        LINEINITIALIZEEXPARAMS  params = { sizeof(LINEINITIALIZEEXPARAMS) };
        params.dwOptions = LINEINITIALIZEEXOPTION_USEHIDDENWINDOW;
        TRESULT tr = ::TfxTapiFunc(::lineInitializeEx(&hLineApp,
                                                    hInst,
                                                    TfxLineCallback,
                                                    szAppName,
                                                    &s_nNumDevs,
                                                    &dwAPIVersion,
                                                    &params),
                                    TDBSTR("lineInitializeEx"));
#endif

        if( TSUCCEEDED(tr) )
        {
            // Save the handle
            s_hLineApp = hLineApp;

            // Save the sink
            s_pAppSink = pAppSink;

            // Negotiate the API versions
            // (Necessary to get proper notifications)
            NegotiateApiVersions();

            RecoverInitialize(s_hLineApp);
        }
        return tr;
    }
    else
    {
		char buf[256] = {'\0'};
        sprintf( buf, "CtLine::Initialize()  Cannot re-initialize\n");
		OutputDebugString( buf );
        return LINEERR_OPERATIONFAILED;
    }
}

TRESULT CtLine::Shutdown()
{
    s_pAppSink = 0;
    delete [] s_aApiVersions;
    s_aApiVersions = 0;

    if( s_hLineApp )
    {
        TRESULT tr = ::TfxTapiFunc(::lineShutdown(s_hLineApp), TDBSTR("lineShutdown"));
        s_hLineApp = 0;

        RecoverShutdown();

        return tr;
    }
    else
    {
		char buf[256] = {'\0'};
        sprintf( buf, "CtLine::Shutdown()  Never initialized\n");
		OutputDebugString( buf );
        return LINEERR_OPERATIONFAILED;
    }
}

TRESULT CtLine::GetMakeCallRequest(
    LPLINEREQMAKECALL   plmc)
{
    assert(s_hLineApp);
    assert(plmc);

    return ::TfxTapiFunc(::lineGetRequest(s_hLineApp, LINEREQUESTMODE_MAKECALL, plmc),
                         TDBSTR("lineGetRequest"));
}

TRESULT CtLine::GetMediaCallRequest(
    LPLINEREQMEDIACALL  plmc)
{
    assert(s_hLineApp);
    assert(plmc);

    return ::TfxTapiFunc(::lineGetRequest(s_hLineApp, LINEREQUESTMODE_MEDIACALL, plmc),
                         TDBSTR("lineGetRequest"));
}

TRESULT CtLine::RegisterRequestRecipient(
    DWORD   dwRequestMode,
    BOOL    bEnable)
{
    assert(s_hLineApp);
    assert(s_pAppSink || !bEnable);

    return ::TfxTapiFunc(::lineRegisterRequestRecipient(s_hLineApp,
                                                        DWORD(s_pAppSink),
                                                        dwRequestMode,
                                                        DWORD(bEnable)),
                         TDBSTR("lineRegisterRequestRecipient"));
}

TRESULT CtLine::TranslateDialog(
    DWORD   nLineID,
    HWND    hwndOwner,
    LPCSTR  szAddressIn)
{
    assert(s_hLineApp);
    assert(nLineID < GetNumDevs());
    //assert(hwndOwner);    // NULL HWND parent is legal

    return ::TfxTapiFunc(::lineTranslateDialog(s_hLineApp, nLineID, 
                                               s_aApiVersions[nLineID],
                                               hwndOwner, szAddressIn),
                         TDBSTR("lineTranslateDialog"));
}

TRESULT CtLine::ConfigDialog(
    DWORD   nLineID,
    HWND    hwndOwner,
    LPCSTR  pszDeviceClass)  // = 0
{
    assert(nLineID < GetNumDevs());
    //assert(hwndOwner);    // NULL HWND parent is legal

    return ::TfxTapiFunc(::lineConfigDialog(nLineID,
                                            hwndOwner,
                                            (pszDeviceClass && !*pszDeviceClass ? 0 : pszDeviceClass)),
                         TDBSTR("lineConfigDialog"));
}

TRESULT CtLine::GetIcon(
    DWORD   nLineID,
    LPHICON phicon,
    LPCSTR  pszDeviceClass) // = 0
{
    assert(nLineID < GetNumDevs());
    assert(phicon);

    return ::TfxTapiFunc(::lineGetIcon(nLineID, pszDeviceClass, phicon),
                         TDBSTR("lineGetIcon"));
}

TRESULT CtLine::SetCurrentLocation(
    DWORD   nLocationID)
{
    assert(s_hLineApp);

    return ::TfxTapiFunc(::lineSetCurrentLocation(s_hLineApp, nLocationID), 
                         TDBSTR("lineSetCurrentLocation"));
}

/////////////////////////////////////////////////////////////////////////////
// Line Wrappers

TRESULT CtLine::Open(
    DWORD       nLineID,
    CtLineSink* pInitialSink,   // = 0
    DWORD       dwPriviledges,  // = LINECALLPRIVILEGE_OWNER
    DWORD       dwMediaModes)   // = LINEMEDIAMODE_INTERACTIVEVOICE
{
    assert(s_hLineApp);
    assert(nLineID < GetNumDevs());
    assert(!m_hLine);

    TRESULT tr = ::TfxTapiFunc(::lineOpen(s_hLineApp, nLineID,
                                          &m_hLine, s_aApiVersions[nLineID],
                                          0, (DWORD)this, dwPriviledges,
                                          dwMediaModes, 0),
                                TDBSTR("lineOpen"));

    if( TSUCCEEDED(tr) )
    {
        // Cache the device ID and the initial sink
        m_nLineID = nLineID;
        AddSink(pInitialSink);

        assert(m_hLine);

        // Set all status messages
        ::TfxTapiFunc(::lineSetStatusMessages(m_hLine, LINEDEVSTATE_ALL, LINEADDRESSSTATE_ALL),
                      TDBSTR("lineSetStatusMessages"));
    }
    else
    {
        // NT sets the m_hLine even if we fail!
        m_hLine = 0;
    }

    return tr;
}

TRESULT CtLine::Close()
{
    TRESULT tr = 0;

    if( m_hLine )
    {
        tr = ::TfxTapiFunc(::lineClose(m_hLine), TDBSTR("lineClose"));
        RemoveAllRequests();

        // Deallocate all unhandled calls
		HCALL   hCall = NULL;
		CPointerList::iterator iter;
		for( iter = m_listUnhandledCalls.begin(); iter != m_listUnhandledCalls.end(); ++iter )
        {
            hCall = (HCALL)(*iter);
            ::TfxTapiFunc(::lineDeallocateCall(hCall), TDBSTR("lineDeallocate"));
        }

        m_hLine = 0;
        m_nLineID = DWORD(-1);
    }
    return tr;
}

TRESULT CtLine::ForwardAll(
    LPLINEFORWARDLIST const plfl,
    DWORD                   nRings)
{
    assert(m_hLine);
    TRESULT tr = ::TfxTapiFunc(::lineForward(m_hLine, TRUE, 0, plfl, nRings, 0, 0),
                               TDBSTR("lineForward"));
    if( TPENDING(tr) )
    {
        AddRequest(tr, this, LINEREQUEST_FORWARD);
    }

    return tr;
}

TRESULT CtLine::ForwardAddress(
    DWORD                   nAddressID,
    LPLINEFORWARDLIST const plfl,
    DWORD                   nRings)
{
    assert(m_hLine);
    TRESULT tr = ::TfxTapiFunc(::lineForward(m_hLine, FALSE, nAddressID, plfl, nRings, 0, 0),
                               TDBSTR("lineForward"));

    if( TPENDING(tr) )
    {
        AddRequest(tr, this, LINEREQUEST_FORWARD);
    }

    return tr;
}

TRESULT CtLine::GetAddressID(
    LPDWORD pdwAddressID, 
    DWORD   nAddressMode, 
    LPCSTR  pszAddress, 
    DWORD   nSize) const
{
    assert(m_hLine);
    assert(pdwAddressID);
    assert(pszAddress);
    assert(nSize);

    return ::TfxTapiFunc(::lineGetAddressID(m_hLine, pdwAddressID, nAddressMode, pszAddress, nSize),
                         TDBSTR("lineGetAddressID"));
}

TRESULT CtLine::GetNumRings(
    DWORD   nAddressID,
    DWORD*  pnRings)
{
    assert(m_hLine);
    assert(pnRings);

    return ::TfxTapiFunc(::lineGetNumRings(m_hLine, nAddressID, pnRings),
                         TDBSTR("lineGetNumRings"));
}

TRESULT CtLine::SetNumRings(
    DWORD   nAddressID,
    DWORD   nRings)
{
    assert(m_hLine);

    return ::TfxTapiFunc(::lineSetNumRings(m_hLine, nAddressID, nRings),
                         TDBSTR("lineSetNumRings"));
}

/////////////////////////////////////////////////////////////////////////////
// Line Event Handling

void CALLBACK
TfxLineCallback(
    DWORD   dwDevice,
    DWORD   nMsg,
    DWORD   dwInstance,
    DWORD   dwParam1,
    DWORD   dwParam2,
    DWORD   dwParam3)
{
    TETRACE(dwDevice, nMsg, dwParam1, dwParam2, dwParam3);

    switch( nMsg )
    {
    case LINE_CREATE:
        //TRACE0( TEXT("LINE_CREATE\n"));
        CtLine::OnCreate(dwParam1);
    break;

    case LINE_REQUEST:
        //TRACE0(TEXT("LINE_REQUEST\n"));
        CtLine::OnRequest(dwParam1, HWND(dwParam2), LOWORD(dwParam3));
    break;

    default:
        CtLine*   pLine = (CtLine*)dwInstance;
        if( pLine )
        {
            pLine->OnEvent(dwDevice, nMsg, dwParam1, dwParam2, dwParam3);
        }
        else
        {
			char buf[256] = {'\0'};
            sprintf( buf, __TEXT("Unhandled line event: %luL  %lu, %lu, %lu\n"), nMsg,
                  dwParam1, dwParam2, dwParam3);
			OutputDebugString( buf );
        }
    break;
    }
}

void CtLine::OnCreate(
    DWORD   dwDeviceID)
{
    if( s_pAppSink ) s_pAppSink->OnLineCreate(dwDeviceID);
}

void CtLine::OnRequest(
    DWORD       nRequestMode,
    HWND        hRequestWnd,
    TREQUEST    nRequestID)
{
    if( s_pAppSink ) s_pAppSink->OnLineRequest(nRequestMode, hRequestWnd, nRequestID);
}

void CtLine::OnEvent(
    DWORD   dwDevice,
    DWORD   nMsg,
    DWORD   dwParam1,
    DWORD   dwParam2,
    DWORD   dwParam3)
{
    switch( nMsg )
    {
    case LINE_ADDRESSSTATE:
        //TRACE0(TEXT("LINE_ADDRESSSTATE\n"));
        assert(m_hLine == (HLINE)dwDevice);
        OnAddressState(dwParam1, dwParam2);
    break;

    case LINE_CALLINFO:
        //TRACE0(TEXT("LINE_CALLINFO\n"));
        OnCallInfo((HCALL)dwDevice, dwParam1);
    break;

    case LINE_CALLSTATE:
        //TRACE0(TEXT("LINE_CALLSTATE\n"));
        OnCallState((HCALL)dwDevice, dwParam1, dwParam2, dwParam3);
    break;

    case LINE_CLOSE:
        //TRACE0(TEXT("LINE_CLOSE\n"));
        assert(m_hLine == (HLINE)dwDevice);
        OnClose();
    break;

    case LINE_DEVSPECIFIC:
        //TRACE0(TEXT("LINE_DEVSPECIFIC\n"));
        assert(m_hLine == (HLINE)dwDevice);
        OnDevSpecific(dwDevice, dwParam1, dwParam2, dwParam3);
    break;

    case LINE_DEVSPECIFICFEATURE:
        //TRACE0(TEXT("LINE_DEVSPECIFICFEATURE\n"));
        assert(m_hLine == (HLINE)dwDevice);
        OnDevSpecificFeature(dwDevice, dwParam1, dwParam2, dwParam3);
    break;

    case LINE_GATHERDIGITS:
        //TRACE0(TEXT("LINE_GATHERDIGITS\n"));
        OnGatherDigits((HCALL)dwDevice, dwParam1);
    break;

    case LINE_GENERATE:
        //TRACE0(TEXT("LINE_GENERATE\n"));
        OnGenerate((HCALL)dwDevice, dwParam1);
    break;

    case LINE_LINEDEVSTATE:
        //TRACE0(TEXT("LINE_LINEDEVSTATE\n"));
        assert(m_hLine == (HLINE)dwDevice);
        OnDevState(dwParam1, dwParam2, dwParam3);
    break;

    case LINE_MONITORDIGITS:
        //TRACE0(TEXT("LINE_MONITORDIGITS\n"));
        OnMonitorDigits((HCALL)dwDevice, (char)LOBYTE(LOWORD(dwParam1)), dwParam2);
    break;

    case LINE_MONITORMEDIA:
        //TRACE0(TEXT("LINE_MONITORMEDIA\n"));
        OnMonitorMedia((HCALL)dwDevice, dwParam1);
    break;

    case LINE_MONITORTONE:
        //TRACE0(TEXT("LINE_MONITORTONE\n"));
        OnMonitorTone((HCALL)dwDevice, dwParam1);
    break;

    case LINE_REPLY:
    {
        //TRACE0(TEXT("LINE_REPLY: "));
        // If a reply target can be found for this request,
        // let the target handle the reply
        CtReplyTarget*  pTarget = 0;
        DWORD           dwRequest;
        if( m_listRequests.RemoveRequest(dwParam1, &pTarget, &dwRequest) )
        {
            assert(pTarget);
            assert(dwRequest != LINEREQUEST_UNKNOWN);
            pTarget->OnReply(dwParam1, dwParam2, dwRequest);
        }
        // If a reply target can't be found for this request,
        // let the line handle it as an unhandled reply.
        // This allows app-specific reply handling for calls made
        // directly to TAPI via the handle.
        else
        {
            OnReply(dwParam1, dwParam2, LINEREQUEST_UNKNOWN);
        }
    }
    break;

#if (TAPI_CURRENT_VERSION >= 0x00020000)
    case LINE_AGENTSPECIFIC: // TAPI v2.0
        OnAgentSpecific(dwParam1, dwParam2, dwParam3);
    break;
    
    case LINE_AGENTSTATUS: // TAPI v2.0
        OnAgentStatus(dwParam1, dwParam2, dwParam3);
    break;

    case LINE_APPNEWCALL: // TAPI v2.0
    {
        HCALL   hCall = (HCALL)dwParam2;
        OnNewCall(dwParam1, hCall, dwParam3);

        // If the call is unhandled, add it to the list
        // of calls we know about but are explicitly unhandled
        if( !CtCall::FromHandle(hCall) )
        {
            m_listUnhandledCalls.push_back((void*)hCall);  //.AddTail((void*)hCall);
        }
    }
    break;

    case LINE_PROXYREQUEST: // TAPI v2.0
        OnProxyRequest((LINEPROXYREQUEST*)dwParam1);
    break;

    case LINE_REMOVE: // TAPI v2.0
        OnLineRemove(dwParam1);
    break;
#endif

    default:
		char buf[256] = {'\0'};
        sprintf( buf, __TEXT("CtLine::OnEvent()  Unhandled nMsg= %d\n"), nMsg);
		OutputDebugString( buf );
        assert(FALSE);
    break;
    }
}

void CtLine::OnAddressState(
    DWORD nAddressID,
    DWORD nAddressState)
{
    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtLineSink*)m_rgSinks[i])->OnLineAddressState(this, nAddressID, nAddressState);
        }
    }
}

void CtLine::OnCallInfo(
    HCALL   hCall,
    DWORD   nCallInfo)
{
    CtCall*   pCall = CtCall::FromHandle(hCall);
    if( pCall ) pCall->OnInfo(nCallInfo);
}

void CtLine::OnCallState(
    HCALL   hCall,
    DWORD   nCallState,
    DWORD   dwParam2,
    DWORD   nCallPriviledge)
{
    CtCall*     pCall = CtCall::FromHandle(hCall);
	CPointerList::iterator FindIter;
	FindIter = find( m_listUnhandledCalls.begin(), m_listUnhandledCalls.end(), (void*)hCall );
//    POSITION    posUnhandled = m_listUnhandledCalls.Find((void*)hCall);

    // If this is a new call, notify the app
    if( !pCall && (FindIter == m_listUnhandledCalls.end()) )
    {
        DWORD   nAddressID = 0;

        // Get the address ID
        LINECALLINFO    lci;
        lci.dwTotalSize = sizeof(lci);
        long            tr = lineGetCallInfo(hCall, &lci);
        if( TSUCCEEDED(tr) || tr == LINEERR_STRUCTURETOOSMALL )
        {
            nAddressID = lci.dwAddressID;
        }

        // Fake LINE_APPNEWCALL for TAPI 1.x
        OnNewCall(nAddressID, hCall, nCallPriviledge);
        pCall = CtCall::FromHandle(hCall);
    }

    // If the app is handling this call, send the notification
    if( pCall )
    {
        pCall->OnState(nCallState, dwParam2, nCallPriviledge);
    }
    // If the call was dropped in the CtCall destructor or
    // if the app decides not to handle the call at all,
    // we'll get an idle notification for a call w/o an object
    else if( nCallState == LINECALLSTATE_IDLE )
    {
        ::TfxTapiFunc(::lineDeallocateCall(hCall), TDBSTR("lineDeallocate"));

        // Remove it from the list of unhandled calls as
        // there will be no more notifications.
        if( FindIter != m_listUnhandledCalls.end() )
        {
            m_listUnhandledCalls.remove( (void*)hCall );  //.RemoveAt(posUnhandled);
        }
    }
    // If the call is unhandled, add it to the list
    // of calls we know about but are explicitly unhandled
    else if( FindIter == m_listUnhandledCalls.end())
    {
        m_listUnhandledCalls.push_back((void*)hCall);  //.AddTail((void*)hCall);
    }
}

void CtLine::OnClose()
{
    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtLineSink*)m_rgSinks[i])->OnLineClose(this);
        }
    }
}

void CtLine::OnDevSpecific(
    DWORD   dwDevice,
    DWORD   dwParam1,
    DWORD   dwParam2,
    DWORD   dwParam3)
{
    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtLineSink*)m_rgSinks[i])->OnLineDevSpecific(this, dwDevice, dwParam1, dwParam2, dwParam3);
        }
    }
}

void CtLine::OnDevSpecificFeature(
    DWORD   dwDevice,
    DWORD   dwParam1,
    DWORD   dwParam2,
    DWORD   dwParam3)
{
    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtLineSink*)m_rgSinks[i])->OnLineDevSpecific(this, dwDevice, dwParam1, dwParam2, dwParam3);
        }
    }
}

void CtLine::OnGatherDigits(
    HCALL   hCall,
    DWORD   nGatherTerm)
{
    CtCall*   pCall = CtCall::FromHandle(hCall);
    if( pCall ) pCall->OnGatherDigits(nGatherTerm);
}

void CtLine::OnGenerate(
    HCALL   hCall,
    DWORD   nGenerateTerm)
{
    CtCall*   pCall = CtCall::FromHandle(hCall);
    if( pCall ) pCall->OnGenerate(nGenerateTerm);
}

void CtLine::OnDevState(
    DWORD   nDevState,
    DWORD   dwParam2,
    DWORD   dwParam3)
{
    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtLineSink*)m_rgSinks[i])->OnLineDevState(this, nDevState, dwParam2, dwParam3);
        }
    }
}

void CtLine::OnMonitorDigits(
    HCALL   hCall,
    char    cDigit,
    DWORD   nDigitMode)
{
    CtCall*   pCall = CtCall::FromHandle(hCall);
    if( pCall ) pCall->OnMonitorDigits(cDigit, nDigitMode);
}

void CtLine::OnMonitorMedia(
    HCALL   hCall,
    DWORD   nMediaMode)
{
    CtCall*   pCall = CtCall::FromHandle(hCall);
    if( pCall ) pCall->OnMonitorMedia(nMediaMode);
}

void CtLine::OnMonitorTone(
    HCALL   hCall,
    DWORD   dwAppSpecific)
{
    CtCall*   pCall = CtCall::FromHandle(hCall);
    if( pCall ) pCall->OnMonitorTone(dwAppSpecific);
}

void CtLine::OnReply(
    TREQUEST    nRequestID,
    TRESULT     nResult,
    DWORD       nRequestType)
{
#ifdef _DEBUG
    static LPCSTR   aszReplyName[] = {  "LINEREQUEST_UNKNOWN",
                                        "LINEREQUEST_FORWARD", };
    assert(nRequestType <= DIM(aszReplyName));
    ::TfxTapiReply(nRequestID, nResult, aszReplyName[nRequestType]);
#endif

    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtLineSink*)m_rgSinks[i])->OnLineReply(this, nRequestID, nResult, nRequestType);
        }
    }
}

void CtLine::AddRequest(
    TREQUEST        nRequestID,
    CtReplyTarget*  pTarget,
    DWORD           dwRequestType)
{
    m_listRequests.AddRequest(nRequestID, pTarget, dwRequestType);
}

void CtLine::RemoveAllRequests(
    CtReplyTarget*  pTarget)
{
    m_listRequests.RemoveAllRequests(pTarget);
}

BOOL CtLine::IsCallRequestPending(
    TREQUEST    nRequestID,
    DWORD*      pnRequestType) const
{
    return m_listRequests.IsRequestPending(nRequestID, pnRequestType);
}

BOOL CtLine::IsCallRequestTypePending(
    const CtCall*   pCall,
    DWORD           nRequestType) const
{
    return m_listRequests.IsRequestTypePending(nRequestType, pCall);
}

// TAPI 2.0 events
void CtLine::OnNewCall(
    DWORD   nAddressID,
    HCALL   hCall,
    DWORD   nCallPriviledge)
{
    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtLineSink*)m_rgSinks[i])->OnLineNewCall(this, hCall, nAddressID, nCallPriviledge);
        }
    }
}

#if (TAPI_CURRENT_VERSION >= 0x00020000)

void CtLine::OnAgentSpecific(
    DWORD   dwAgentExtensionIDIndex,
    DWORD   dwHandlerSpecific1,
    DWORD   dwHandlerSpecific2)
{
    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtLineSink*)m_rgSinks[i])->OnLineAgentSpecific(this, dwAgentExtensionIDIndex, dwHandlerSpecific1, dwHandlerSpecific2);
        }
    }
}

void CtLine::OnAgentStatus(
    DWORD   nAddressID,
    DWORD   nAgentStatus,
    DWORD   dwAgentStatusDetail)
{
    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtLineSink*)m_rgSinks[i])->OnLineAgentStatus(this, nAddressID, nAgentStatus, dwAgentStatusDetail);
        }
    }
}

void CtLine::OnProxyRequest(
    LINEPROXYREQUEST*   pProxyRequest)
{
    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtLineSink*)m_rgSinks[i])->OnLineProxyRequest(this, pProxyRequest);
        }
    }
}

void CtLine::OnLineRemove(
    DWORD   dwDeviceID)
{
    for( int i = 0; i < m_rgSinks.size(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtLineSink*)m_rgSinks[i])->OnLineRemove(this, dwDeviceID);
        }
    }
}

#endif
