// tDialDlg.cpp : implementation file
//

#include "stdafx.h"
#include "tDial.h"
#include "tDialDlg.h"
#include "AboutDlg.h"
#include "PhoneNoDlg.h" // CtPhoneNoDlg
#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_call(&m_line),
    m_rgbValidLines(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)
	ON_WM_SYSCOMMAND()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

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

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

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here

    // Cache valid lines
    CacheValidLines();

    // 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::OnSysCommand(UINT nID, LPARAM lParam) 
{
	if( (nID & 0xFFF0) == IDM_ABOUTBOX )
	{
		OnAbout();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

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

void CtDialDlg::CacheValidLines()
{
    ASSERT(m_rgbValidLines == 0);

    // Get valid lines
    DWORD   nLines = ::TfxGetNumLines();
    ASSERT(nLines);

    DWORD   nValidLines = 0;
    m_rgbValidLines = new BOOL[nLines];
    if( m_rgbValidLines )
    {
        CtLineDevCaps   ldc;
        for( DWORD nLineID = 0; nLineID < nLines; nLineID++ )
        {
            if( TSUCCEEDED(ldc.GetDevCaps(nLineID)) &&
                (ldc.GetBearerModes() & LINEBEARERMODE_VOICE) &&
                (ldc.GetMediaModes() & LINEMEDIAMODE_INTERACTIVEVOICE) &&
                (ldc.GetLineFeatures() & LINEFEATURE_MAKECALL) )
            {
                m_rgbValidLines[nLineID] = TRUE;
                nValidLines++;
            }
            else
            {
                m_rgbValidLines[nLineID] = FALSE;
                LogStatus("Line %d doesn't support voice calls.\r\n", nLineID);
            }
        }
    }

    if( !nValidLines )
    {
        delete[] m_rgbValidLines;
        m_rgbValidLines = 0;
        LogStatus("No lines support voice calls.\r\n");
    }
}

void CtDialDlg::OnDial() 
{
    ASSERT(!m_call.IsRequestTypePending(CALLREQUEST_MAKECALL));
    ASSERT(!m_call.GetHandle());

    m_editLog.SetWindowText("");
    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_call.GetHandle());

    if( TPENDING(m_call.Drop()) )
    {
        LogStatus("Call dropping...\r\n");
    }
    else
    {
        LogStatus("Can't drop call.\r\n");
    }

    UpdateStatus();
}

void CtDialDlg::UpdateStatus()
{
    if( m_call.GetHandle() )
    {
        m_btnDial.EnableWindow(FALSE);
        m_btnHangUp.EnableWindow(TRUE);
    }
    else if( m_call.IsRequestPending() )
    {
        m_btnDial.EnableWindow(FALSE);
        m_btnHangUp.EnableWindow(FALSE);
    }
    else
    {
        m_btnDial.EnableWindow(m_rgbValidLines ? TRUE : FALSE);
        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() 
{
    delete[] m_rgbValidLines;
    m_rgbValidLines = 0;

    if( m_call.GetHandle() )
    {
        m_call.Drop();
    }

    if( m_line.GetHandle() )
    {
        m_line.Close();
    }

	CDialog::OnDestroy();
}

// Telephony handlers
void CtDialDlg::Dial(LPCSTR pszPhoneNo)
{
    // Show the dialog
    CPhoneNoDlg dlg;
    dlg.m_pno.SetWholePhoneNo(pszPhoneNo);
    dlg.m_rgbShouldShowLines = m_rgbValidLines;

    if( dlg.DoModal() == IDOK )
    {
        // Open the line for an outgoing call
        if( TSUCCEEDED(m_line.Open(dlg.m_nLineID, this)) )
        {
            TRESULT tr = m_call.MakeCall(dlg.m_sDialable,
                                         dlg.m_pno.GetCountryCodeNum(),
                                         this);
            if( TPENDING(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);
        }
    }
}

void CtDialDlg::OnCallState(CtCall* pCall, DWORD nCallState, DWORD dwParam2, DWORD nCallPriviledge)
{
    struct FlagMap { DWORD nFlag; LPCSTR pszFlag; };
    static FlagMap rgFlags[] =
    {
        {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(rgFlags); i++ )
    {
        if( rgFlags[i].nFlag == nCallState )
        {
            LogStatus("Call %s.\r\n", rgFlags[i].pszFlag);
            break;
        }
    }

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

    case LINECALLSTATE_IDLE:
        VERIFY(TSUCCEEDED(m_call.Deallocate()));
        VERIFY(TSUCCEEDED(m_line.Close()));
    break;
    }

    UpdateStatus();
}

void CtDialDlg::OnCallReply(CtCall* pCall, TREQUEST nRequestID, TRESULT nResult, DWORD nRequestType)
{
    switch( nRequestType )
    {
    case CALLREQUEST_MAKECALL:
        if( TSUCCEEDED(nResult) )
        {
            LogStatus("Call placed.\r\n");
        }
        else
        {
            //m_call.Deallocate(); // No call to deallocate
            VERIFY(TSUCCEEDED(m_line.Close()));
            LogStatus("Call cannot be placed.\r\n");
        }
    break;

    case CALLREQUEST_DROP:
        if( TSUCCEEDED(nResult) )
        {
            LogStatus("Call dropped.\r\n");
        }
        else
        {
            LogStatus("Call cannot be dropped.\r\n");
        }
    break;
    }

    UpdateStatus();
}

void CtDialDlg::OnLineClose(CtLine* pLine)
{
    ASSERT(pLine->GetHandle() == m_line.GetHandle());

    LogStatus("Line closed.\n");

    if( m_call.GetHandle() )
    {
        m_call.Drop();
        m_call.Deallocate();
    }

    if( m_line.GetHandle() )
    {
        m_line.Close();
    }
}
