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

#include "stdafx.h"
#include "tCall.h"
#include "TfxUtil.h"

/////////////////////////////////////////////////////////////////////////////
// Call Operations

// Constructor for call yet to be made
CtCall::CtCall(
    CtLine*     pLine)
    :
    m_pLine(pLine),
    m_hCall(0)
{
    ASSERT(pLine);

    // Append to static list of calls
    AddToCalls(this);
}

// Constructor for existing call
CtCall::CtCall(
    CtLine*     pLine,
    HCALL       hCall,
    CtCallSink* pInitialSink)  // = 0
    :
    m_pLine(pLine),
    m_hCall(hCall)
{
    ASSERT(pLine);
    ASSERT(hCall);

    // Append to static list of calls
    AddToCalls(this);

    // Cache the sink
    AddSink(pInitialSink);
}

CtCall::~CtCall()
{
    if( m_hCall )
    {
        // If deallocating the call fails 'cuz we're the sole owner,
        // we'll have to drop it first and deallocate it in CtLine::OnCallState()
        if( Deallocate() == LINEERR_INVALCALLSTATE )
        {
            Drop();
        }
    }

    // Remove all queued requests
    m_pLine->RemoveAllRequests(this);

    // Remove from static list of calls
    RemoveFromCalls(this);
}

CtLine* CtCall::GetLine() const
{
    return m_pLine;
}

HCALL CtCall::GetHandle() const
{
    return m_hCall;
}

HCALL CtCall::Attach(
    HCALL       hCall,
    CtCallSink* pInitialSink)  // = 0
{
    HCALL   hOldCall = hCall;
    m_hCall = hCall;

    //m_pSink = pSink;
    AddSink(pInitialSink);

    return hOldCall;
}

HCALL CtCall::Detach()
{
    // Remove all queued requests
    m_pLine->RemoveAllRequests(this);

    return Attach(0, 0);
}

// If it's not already in the list, add it
void CtCall::AddSink(CtCallSink* pSink)
{
    if( pSink )
    {
        int nEmpty = -1;

        for( int i = 0; i <= m_rgSinks.GetUpperBound(); 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.Add(pSink);
        }
    }
}

void CtCall::RemoveSink(CtCallSink* pSink)
{
    ASSERT(pSink);

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

BOOL CtCall::IsRequestPending(
    TREQUEST    nRequestID,             // = 0
    DWORD*      pnRequestType) const    // = 0
{
    return m_pLine->IsCallRequestPending(nRequestID, pnRequestType);
}

BOOL CtCall::IsRequestTypePending(
    DWORD   nRequestType) const
{
    return m_pLine->IsCallRequestTypePending(this, nRequestType);
}

/////////////////////////////////////////////////////////////////////////////
// Static Call Operations

CPtrList    CtCall::s_listCalls;

void CtCall::AddToCalls(
    CtCall* pCall)
{
    ASSERT(pCall);
    s_listCalls.AddTail(pCall);
}

void CtCall::RemoveFromCalls(
    CtCall*   pCall)
{
    ASSERT(pCall);
    s_listCalls.RemoveAt(s_listCalls.Find(pCall));
}

CtCall* CtCall::FromHandle(
    HCALL   hCall)
{
    ASSERT(hCall);

    POSITION    pos = s_listCalls.GetHeadPosition();
    CtCall*     pCall = 0;
    while( pos &&
           (pCall = (CtCall*)s_listCalls.GetNext(pos)) )
    {
        if( pCall->GetHandle() == hCall )
        {
            return pCall;
        }
    }
    return 0;
}

/////////////////////////////////////////////////////////////////////////////
// Call Wrappers

TRESULT CtCall::Accept(
    LPCSTR  psUserUserInfo, // = 0
    DWORD   nSize)          // = 0
{
    ASSERT(m_hCall);
    TREQUEST    tr = ::TfxTapiFunc(::lineAccept(m_hCall, psUserUserInfo, nSize),
                                   TDBSTR("lineAccept"));

    if( TPENDING(tr) )
    {
        m_pLine->AddRequest(tr, this, CALLREQUEST_ACCEPT);
    }

    return tr;
}

TRESULT CtCall::Answer(
    LPCSTR  psUserUserInfo, // = 0
    DWORD   nSize)          // = 0
{
    ASSERT(m_hCall);
    TREQUEST    tr = ::TfxTapiFunc(::lineAnswer(m_hCall, psUserUserInfo, nSize),
                                   TDBSTR("lineAnswer"));

    if( TPENDING(tr) )
    {
        m_pLine->AddRequest(tr, this, CALLREQUEST_ANSWER);
    }

    return tr;
}

TRESULT CtCall::Dial(
    LPCSTR  szDestAddress,
    DWORD   dwCountryCode)  // = 0
{
    ASSERT(m_hCall);
    TREQUEST    tr = ::TfxTapiFunc(::lineDial(m_hCall, szDestAddress, dwCountryCode),
                                   TDBSTR("lineDial"));
    if( TPENDING(tr) )
    {
        m_pLine->AddRequest(tr, this, CALLREQUEST_DIAL);
    }

    return tr;
}

TRESULT CtCall::Deallocate()
{
    ASSERT(m_hCall);
    TRESULT tr = ::TfxTapiFunc(::lineDeallocateCall(m_hCall),
                               TDBSTR("lineDeallocateCall"));
    if( TSUCCEEDED(tr) )
    {
        m_pLine->RemoveAllRequests(this);
    }

    m_hCall = 0;
    return tr;
}

TRESULT CtCall::Drop(
    LPCSTR  psUserUserInfo, // = 0
    DWORD   nSize)          // = 0
{
    ASSERT(m_hCall);
    TREQUEST    tr = ::TfxTapiFunc(::lineDrop(m_hCall, psUserUserInfo, nSize),
                                   TDBSTR("lineDrop"));
    if( TPENDING(tr) )
    {
        m_pLine->AddRequest(tr, this, CALLREQUEST_DROP);
    }

    return tr;
}

TRESULT CtCall::GatherDigits(
    LPSTR   pszDigits,
    DWORD   nDigits,
    LPCSTR  pszTerminationDigits,   // = 0
    DWORD   nFirstDigitTimeout,     // = 5000
    DWORD   nInterDigitTimeout,     // = 5000
    DWORD   nDigitMode)             // = LINEDIGITMODE_DTMF
{
    ASSERT(m_hCall);
    ASSERT(pszDigits || !nDigits);

    return ::TfxTapiFunc(::lineGatherDigits(m_hCall, nDigitMode, pszDigits, nDigits,
                                            pszTerminationDigits, nFirstDigitTimeout,
                                            nInterDigitTimeout),
                         TDBSTR("lineGatherDigits"));
}

TRESULT CtCall::GenerateDigits(
    LPCSTR  szDigits,
    DWORD   nDuration,  // = 0
    DWORD   nDigitMode) // = LINEDIGITMODE_DTMF
{
    ASSERT(szDigits && *szDigits);
    ASSERT(m_hCall);
    TREQUEST    tr = ::TfxTapiFunc(::lineGenerateDigits(m_hCall, nDigitMode, szDigits, nDuration),
                                   TDBSTR("lineGenerateDigits"));
    if( TPENDING(tr) )
    {
        m_pLine->AddRequest(tr, this, CALLREQUEST_GENERATEDIGITS);
    }

    return tr;
}

TRESULT CtCall::GenerateTone(
    DWORD   nToneMode,
    DWORD   nDuration,
    DWORD   nCustomTones,   // = 0
    LINEGENERATETONE*   pCustomTones)   // = 0
{
    ASSERT(nToneMode != LINETONEMODE_CUSTOM ||
           (nCustomTones && pCustomTones));
    ASSERT(m_hCall);

    return ::TfxTapiFunc(::lineGenerateTone(m_hCall, nToneMode, nDuration,
                                            nCustomTones, pCustomTones),
                         TDBSTR("lineGenerateTone"));
}

TRESULT CtCall::Handoff(
    LPCSTR  szFileName)
{
    ASSERT(m_hCall);
    return ::TfxTapiFunc(::lineHandoff(m_hCall, szFileName, 0), TDBSTR("lineHandoff"));
}

TRESULT CtCall::Handoff(
    DWORD   nMediaMode)
{
    ASSERT(m_hCall);
    return ::TfxTapiFunc(::lineHandoff(m_hCall, 0, nMediaMode), TDBSTR("lineHandoff"));
}

TRESULT CtCall::MakeCall(
    LPCSTR          szDestAddress,  // = 0
    DWORD           nCountryCode,   // = 0
    CtCallSink*     pInitialSink,   // = 0
    LINECALLPARAMS* pCallParams)    // = 0
{
    ASSERT(m_pLine);
    ASSERT(m_pLine->GetHandle());
    ASSERT(!m_hCall);

    // If there are no call params, make one
    bool    bOwnCallParams = !pCallParams && szDestAddress && *szDestAddress;
    if( bOwnCallParams )
    {
        size_t  cbDestAddress = strlen(szDestAddress) + 1;
        size_t  cbCallParams = sizeof(LINECALLPARAMS) + cbDestAddress;
        pCallParams = (LINECALLPARAMS*)(new BYTE[cbCallParams]);
        if( pCallParams )
        {
            ZeroMemory(pCallParams, cbCallParams);
            pCallParams->dwTotalSize = cbCallParams;
            pCallParams->dwBearerMode = LINEBEARERMODE_VOICE;
            //pCallParams->dwMinRate;   // (3.1kHz?)
            //pCallParams->dwMaxRate;   // (3.1kHz?)
            pCallParams->dwMediaMode = LINEMEDIAMODE_INTERACTIVEVOICE;
            pCallParams->dwAddressMode = LINEADDRESSMODE_ADDRESSID;
            //pCallParams->dwAddressID; // (any available?)
            pCallParams->dwDisplayableAddressSize = cbDestAddress;
            pCallParams->dwDisplayableAddressOffset = sizeof(LINECALLPARAMS);
            char*   pszDisplayableAddress = (char*)((BYTE*)pCallParams + sizeof(LINECALLPARAMS));
            strcpy(pszDisplayableAddress, szDestAddress);
        }
    }

    TRESULT tr = ::TfxTapiFunc(::lineMakeCall(m_pLine->GetHandle(), &m_hCall,
                                              szDestAddress, nCountryCode, pCallParams),
                               TDBSTR("lineMakeCall"));
    if( bOwnCallParams )
    {
        delete[] pCallParams;
    }

    if( TPENDING(tr) )
    {
        AddSink(pInitialSink);
        m_pLine->AddRequest(tr, this, CALLREQUEST_MAKECALL);
    }

    return tr;
}

TRESULT CtCall::MonitorDigits(
    DWORD   dwDigitModes)
{
    ASSERT(m_hCall);
    return ::TfxTapiFunc(::lineMonitorDigits(m_hCall, dwDigitModes),
                         TDBSTR("lineMonitorDigits"));
}

/////////////////////////////////////////////////////////////////////////////
// Call Events

void CtCall::OnInfo(
    DWORD   nCallInfo)
{
    for( int i = 0; i <= m_rgSinks.GetUpperBound(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtCallSink*)m_rgSinks[i])->OnCallInfo(this, nCallInfo);
        }
    }
}

void CtCall::OnState(
    DWORD   nCallState,
    DWORD   dwParam2,
    DWORD   nCallPriviledge)
{
    for( int i = 0; i <= m_rgSinks.GetUpperBound(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtCallSink*)m_rgSinks[i])->OnCallState(this, nCallState, dwParam2, nCallPriviledge);
        }
    }
}

void CtCall::OnGatherDigits(
    DWORD   nGatherTerm)
{
    for( int i = 0; i <= m_rgSinks.GetUpperBound(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtCallSink*)m_rgSinks[i])->OnCallGatherDigits(this, nGatherTerm);
        }
    }
}

void CtCall::OnGenerate(
    DWORD   nGenerateTerm)
{
    for( int i = 0; i <= m_rgSinks.GetUpperBound(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtCallSink*)m_rgSinks[i])->OnCallGenerate(this, nGenerateTerm);
        }
    }
}

void CtCall::OnMonitorDigits(
    char    cDigit,
    DWORD   nDigitMode)
{
    for( int i = 0; i <= m_rgSinks.GetUpperBound(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtCallSink*)m_rgSinks[i])->OnCallMonitorDigits(this, cDigit, nDigitMode);
        }
    }
}

void CtCall::OnMonitorMedia(
    DWORD   nMediaMode)
{
    for( int i = 0; i <= m_rgSinks.GetUpperBound(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtCallSink*)m_rgSinks[i])->OnCallMonitorMedia(this, nMediaMode);
        }
    }
}

void CtCall::OnMonitorTone(
    DWORD   dwAppSpecific)
{
    for( int i = 0; i <= m_rgSinks.GetUpperBound(); i++ )
    {
        if( m_rgSinks[i] )
        {
            ((CtCallSink*)m_rgSinks[i])->OnCallMonitorTone(this, dwAppSpecific);
        }
    }
}

void CtCall::OnReply(
    TREQUEST    nRequestID,
    TRESULT     nResult,
    DWORD       nRequestType)
{
#ifdef _DEBUG
    static LPCSTR   aszReplyName[] = {  "CALLREQUEST_UNKNOWN",
                                        "CALLREQUEST_ACCEPT",
                                        "CALLREQUEST_ANSWER",
                                        "CALLREQUEST_DIAL",
                                        "CALLREQUEST_DROP",
                                        "CALLREQUEST_GENERATEDIGITS",
                                        "CALLREQUEST_MAKECALL", };
    ASSERT(nRequestType <= DIM(aszReplyName));
    ::TfxTapiReply(nRequestID, nResult, aszReplyName[nRequestType]);
#endif

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