// tDialDlg.cpp : implementation file
//

#include "stdafx.h"
#include "tDial.h"
#include "tDialDlg.h"
#include "AboutDlg.h"
#include "PhoneNoDlg.h" // CtPhoneNoDlg
#include "MyTapi.h"     // My TAPI wrappers

#include <stdarg.h>     // Variable number of arguments for LogStatus()
#include <ctype.h>      // isalpha() and toupper()

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define DIM(a) (sizeof(a)/sizeof(*a))

/////////////////////////////////////////////////////////////////////////////
// CtDialDlg dialog

CtDialDlg::CtDialDlg(CWnd* pParent /*=NULL*/)
:
    CDialog(CtDialDlg::IDD, pParent),
    m_hLine(0),
    m_hCall(0),
    m_nMakeCallRequest(0),
    m_nDropCallRequest(0),
    m_hLineApp(0),
    m_nLines(0),
    m_aApiVersions(0)
{
	//{{AFX_DATA_INIT(CtDialDlg)
	//}}AFX_DATA_INIT
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CtDialDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CtDialDlg)
	DDX_Control(pDX, IDC_HANG_UP, m_btnHangUp);
	DDX_Control(pDX, IDC_LOG, m_editLog);
	DDX_Control(pDX, IDC_DIAL, m_btnDial);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CtDialDlg, CDialog)
	//{{AFX_MSG_MAP(CtDialDlg)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_DIAL, OnDial)
	ON_BN_CLICKED(IDC_HANG_UP, OnHangUp)
	ON_WM_DESTROY()
	ON_BN_CLICKED(IDC_ABOUT, OnAbout)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CtDialDlg message handlers

BOOL CtDialDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
    if( InitializeLines(0x00010003, 0x00010004) < 0 )
    {
        ::AfxMessageBox(IDS_CANT_INIT_TAPI);
        EndDialog(IDABORT);
    }

    // Update controls
    UpdateStatus();

	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CtDialDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

HCURSOR CtDialDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CtDialDlg::OnAbout() 
{
	CAboutDlg().DoModal();
}

void CtDialDlg::OnDial() 
{
    ASSERT(!m_nMakeCallRequest);
    ASSERT(!m_hCall);

    m_editLog.SetWindowText("");
	if( UpdateData(TRUE) )
	{
        Dial();
	}
}

void CtDialDlg::OnHangUp() 
{
    // Can't hang up a call w/o a handle 'cuz
    // there's no way to cancel a pending TAPI request.
    ASSERT(m_hCall);

    LONG    tr = ::lineDrop(m_hCall, 0, 0);
    if( tr > 0 )
    {
        m_nDropCallRequest = tr;
        LogStatus("Call dropping...\r\n");
        UpdateStatus();
    }
    else
    {
        LogStatus("Can't drop call.\r\n");
    }

    UpdateStatus();
}

void CtDialDlg::UpdateStatus()
{
    if( m_hCall )
    {
        m_btnDial.EnableWindow(FALSE);
        m_btnHangUp.EnableWindow(TRUE);
    }
    else if( m_nMakeCallRequest || m_nDropCallRequest )
    {
        m_btnDial.EnableWindow(FALSE);
        m_btnHangUp.EnableWindow(FALSE);
    }
    else
    {
        m_btnDial.EnableWindow(TRUE);
        m_btnHangUp.EnableWindow(FALSE);
    }
}

void CtDialDlg::LogStatus(LPCSTR pszFormat, ...)
{
    char    szOutput[256];
    va_list argList;

    va_start(argList, pszFormat);
    ::wvsprintf(szOutput, pszFormat, argList);
    va_end(argList);

    m_editLog.SetSel(0xffffffff);
    m_editLog.ReplaceSel(szOutput);
}

void CtDialDlg::OnDestroy() 
{
    if( m_hCall )
    {
        ::lineDrop(m_hCall, 0, 0);
    }

    if( m_hLine )
    {
        ::lineClose(m_hLine);
        m_hLine = 0;
    }

    ::lineShutdown(m_hLineApp);
    m_hLineApp = 0;
    delete[] m_aApiVersions;

	CDialog::OnDestroy();
}

// Telephony handlers
LONG CtDialDlg::InitializeLines(
    DWORD dwLoVersion,
    DWORD dwHiVersion)
{
    LONG tr = lineInitialize(&m_hLineApp,
                             AfxGetInstanceHandle(),
                             MyLineCallback,
                             AfxGetAppName(),
                             &m_nLines);

    if( tr == 0 )
    {
        // Negotiate the API versions
        if( m_nLines &&
            (m_aApiVersions = new DWORD[m_nLines]) )
        {
            LINEEXTENSIONID extid;
            for( DWORD nLineID = 0; nLineID < m_nLines; nLineID++ )
            {
                tr = lineNegotiateAPIVersion(m_hLineApp,
                                         nLineID,
                                         dwLoVersion,
                                         dwHiVersion,
                                         m_aApiVersions +
                                             nLineID,
                                         &extid);
                if( tr < 0 )
                {
                    m_aApiVersions[nLineID] = 0;
                    tr = 0;
                }
            }
        }
    }

    return tr;
}

void CtDialDlg::Dial(LPCSTR pszPhoneNo)
{
    // Get valid lines
    BOOL*   rgbValidLines = new BOOL[m_nLines];
    if( rgbValidLines )
    {
        LINEDEVCAPS*    pldc = 0;

        for( DWORD nLineID = 0; nLineID < m_nLines; nLineID++ )
        {
            if( (::MyGetLineDevCaps(m_hLineApp, m_aApiVersions[nLineID], nLineID, &pldc) == 0) &&
                (pldc->dwBearerModes & LINEBEARERMODE_VOICE) &&
                (pldc->dwMediaModes & LINEMEDIAMODE_INTERACTIVEVOICE) &&
                (pldc->dwLineFeatures & LINEFEATURE_MAKECALL) )
            {
                rgbValidLines[nLineID] = TRUE;
            }
            else
            {
                rgbValidLines[nLineID] = FALSE;
            }
        }

        ::free(pldc);
    }

    // Show the dialog
    CPhoneNoDlg dlg;
    dlg.m_pno.SetWholePhoneNo(pszPhoneNo);
    dlg.m_rgbShouldShowLines = rgbValidLines;
    dlg.m_hLineApp = m_hLineApp;
    dlg.m_nLines = m_nLines;
    dlg.m_aApiVersions = m_aApiVersions;

    if( dlg.DoModal() == IDOK )
    {
        // Open the line for an outgoing call
        if( ::lineOpen(m_hLineApp, dlg.m_nLineID, &m_hLine, m_aApiVersions[dlg.m_nLineID],
                       0, (DWORD)this, LINECALLPRIVILEGE_NONE,
                       LINEMEDIAMODE_INTERACTIVEVOICE, 0) == 0 )
        {
            LONG    tr = ::lineMakeCall(m_hLine, &m_hCall, dlg.m_sDialable,
                                         dlg.m_pno.GetCountryCodeNum(), 0);
            if( tr > 0 )
            {
                m_nMakeCallRequest = tr;
                LogStatus("Placing a call to '%s'...\r\n", dlg.m_pno.GetDisplayable());
                UpdateStatus();
            }
            else
            {
                ::AfxMessageBox(IDS_CANT_MAKE_CALL);
            }
        }
        else
        {
            ::AfxMessageBox(IDS_CANT_OPEN_LINE);
        }
    }

    delete[] rgbValidLines;
}

void CALLBACK
MyLineCallback(
    DWORD   dwDevice,
    DWORD   nMsg,
    DWORD   dwCallbackInstance,
    DWORD   dwParam1,
    DWORD   dwParam2,
    DWORD   dwParam3)
{
    // dwCallbackInstance set in call to lineOpen()
    CtDialDlg*	pThis = (CtDialDlg*)dwCallbackInstance;

    switch( nMsg )
    {
    case LINE_REPLY:
        TRACE0("LINE_REPLY\n");
        pThis->OnReply(dwParam1, dwParam2);
    break;

    case LINE_CALLSTATE:
        TRACE0("LINE_CALLSTATE\n");
        pThis->OnCallState((HCALL)dwDevice, dwParam1, dwParam2, dwParam3);
    break;
    }
}

void CtDialDlg::OnReply(
    DWORD   nRequestID,
    DWORD   nResult)
{
    if( nRequestID == m_nMakeCallRequest )
    {
        m_nMakeCallRequest = 0;

        if( nResult == 0 )
        {
            LogStatus("Call placed.\r\n");
        }
        else
        {
            m_hCall = 0;
            VERIFY(::lineClose(m_hLine) == 0);
            m_hLine = 0;

            LogStatus("Call cannot be placed.\r\n");
        }
    }
    else if( nRequestID == m_nDropCallRequest )
    {
        m_nDropCallRequest = 0;

        LogStatus("Call dropped.\r\n");
    }

    UpdateStatus();
}

void CtDialDlg::OnCallState(
    HCALL   hCall,
    DWORD   nCallState,
    DWORD   dwParam2,
    DWORD   nCallPriviledge)
{
    struct FlagMap { DWORD nFlag; LPCSTR szFlag; };
    static FlagMap aFlags[] =
    {
        {LINECALLSTATE_IDLE,        "idle"},
        {LINECALLSTATE_ACCEPTED,    "accepted"},
        {LINECALLSTATE_DIALTONE,    "dial tone detected"},
        {LINECALLSTATE_DIALING,     "dialing"},
        {LINECALLSTATE_RINGBACK,    "ring-back detected"},
        {LINECALLSTATE_BUSY,        "busy detected"},
        {LINECALLSTATE_SPECIALINFO, "error detected"},
        {LINECALLSTATE_CONNECTED,   "connected"},
        {LINECALLSTATE_PROCEEDING,  "proceeding"},
        {LINECALLSTATE_DISCONNECTED,"disconnected"},
    };

    for( int i = 0; i < DIM(aFlags); i++ )
    {
        if( aFlags[i].nFlag == nCallState )
        {
            LogStatus("Call %s.\r\n", aFlags[i].szFlag);
            break;
        }
    }

    switch( nCallState )
    {
    case LINECALLSTATE_DISCONNECTED:
        OnHangUp();
    break;

    case LINECALLSTATE_IDLE:
        VERIFY(::lineDeallocateCall(m_hCall) == 0);
        m_hCall = 0;

        VERIFY(::lineClose(m_hLine) == 0);
        m_hLine = 0;
    break;
    }

    UpdateStatus();
}

