// calcdriv.cpp : Defines the class behaviors for the application.
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include "calcdriv.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CCalcDrivApp

BEGIN_MESSAGE_MAP(CCalcDrivApp, CWinApp)
	//{{AFX_MSG_MAP(CCalcDrivApp)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG_MAP
	// Standard file based document commands
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCalcDrivApp construction

CCalcDrivApp::CCalcDrivApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CCalcDrivApp object

CCalcDrivApp NEAR theApp;

/////////////////////////////////////////////////////////////////////////////
// CCalcDrivApp initialization

BOOL CCalcDrivApp::InitInstance()
{   
#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT) //__WATCOMC__
    #ifndef _MAC
	    // turn on extra memory tracking
    	afxMemDF |= checkAlwaysMemDF;
    #endif
#endif

	// Initialize OLE 2.0 libraries
	if (!AfxOleInit())
	{
		AfxMessageBox(IDP_OLE_INIT_FAILED);
		return FALSE;
	}

	// Standard initialization
	// If you are not using these features and wish to reduce the size
	//  of your final executable, you should remove from the following
	//  the specific initialization routines you do not need.

	Enable3dControls();    // Use 3d controls in dialogs

	// Simple application that simply invokes a dialog
	CDriverDlg dlg;
	m_pMainWnd = &dlg;
	dlg.DoModal();

	return FALSE;   // don't run after the dialog is done
}

/////////////////////////////////////////////////////////////////////////////
// CDriverDlg dialog

CDriverDlg::CDriverDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CDriverDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CDriverDlg)
	//}}AFX_DATA_INIT
}

CDriverDlg::~CDriverDlg()
{
	TRY
	{
		// shut down the calculator
		//  (since calculator shows its user-interface, it would stay active
		//  if we didn't shut it down with a call to its Quit method)
		m_calc.Close();
	}
	END_TRY
}

void CDriverDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CDriverDlg)
	DDX_Control(pDX, IDD_LAST_OPERATOR, m_stcOperator);
	DDX_Control(pDX, IDD_LAST_OPERAND, m_stcOperand);
	DDX_Control(pDX, IDC_LAST_ACCUM, m_stcAccum);
	DDX_Control(pDX, IDC_EXPRESSION, m_editExpression);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CDriverDlg, CDialog)
	//{{AFX_MSG_MAP(CDriverDlg)
	ON_BN_CLICKED(IDC_GO, OnGo)
	ON_BN_CLICKED(IDC_SINGLE_STEP, OnSingleStep)
	ON_BN_CLICKED(IDC_REFRESH, OnRefresh)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CDriverDlg message handlers

void CDriverDlg::OnGo()
{
	// get current expression from the edit control
	CString strExpression;
	m_editExpression.GetWindowText(strExpression);
	LPCTSTR psz = strExpression;

	// send all characters in the edit control from start to end
	int nLen = strExpression.GetLength();
	int nStart, nEnd;
	m_editExpression.GetSel(nStart, nEnd);
	if (nStart == nLen)
		nStart = 0;
	psz += nStart;
	while (*psz != '\0')
	{
		TCHAR szTemp[2];
		szTemp[0] = *psz;
		szTemp[1] = '\0';
		m_calc.Button(szTemp);
		++psz;
	}
	m_editExpression.SetSel(nLen, nLen);

	OnRefresh();    // refresh after all button commands sent
	m_calc.Evaluate();
	m_calc.Display();
}

void CDriverDlg::OnSingleStep()
{
	// get current expression from the edit control
	CString strExpression;
	m_editExpression.GetWindowText(strExpression);
	LPCTSTR psz = strExpression;

	// send first character in selection, then move selection to next
	int nStart, nEnd;
	m_editExpression.GetSel(nStart, nEnd);
	psz += nStart;
	if (*psz != '\0')
	{
		TCHAR szTemp[2];
		szTemp[0] = *psz;
		szTemp[1] = '\0';
		m_calc.Button(szTemp);

		OnRefresh();        // refresh after each step

		// move to next character for next single-step
		m_editExpression.SetSel(nStart+1,
			min(strExpression.GetLength(), nStart+2));
	}
	else
	{
		m_calc.Evaluate();
		m_calc.Display();
		// stepping from end will start at beginning next time
		m_editExpression.SetSel(0, min(strExpression.GetLength(), 1));
	}
	OnRefresh();    // refresh after all button commands sent
}

void CDriverDlg::OnRefresh()
{
	TCHAR buf[64];
	long lResult = m_calc.GetOperand();
	wsprintf(buf, _T("%ld"), lResult);
	m_stcOperand.SetWindowText(buf);

	long lAccum = m_calc.GetAccum();
	wsprintf(buf, _T("%ld"), lAccum);
	m_stcAccum.SetWindowText(buf);

	short nOp = m_calc.GetOperation();
	static TCHAR operators[5] = { '?', '+', '-', '*', '/'};
	if (nOp < 0 || nOp > 4)
		nOp = 0;
	wsprintf(buf, _T("%c"), operators[nOp]);
	m_stcOperator.SetWindowText(buf);
}

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

	// create the calculator object that we'll drive through OLE automation
	COleException e;
	CLSID clsid;
	if (CLSIDFromProgID(OLESTR("mfccalc.calculator"), &clsid) != NOERROR)
	{
		AfxMessageBox(IDP_UNABLE_TO_CREATE);
		EndDialog(IDABORT);
		return FALSE;
	}

	// try to get the active calculator before creating a new one
	LPUNKNOWN lpUnk;
	LPDISPATCH lpDispatch;
	if (GetActiveObject(clsid, NULL, &lpUnk) == NOERROR)
	{
		HRESULT hr = lpUnk->QueryInterface(IID_IDispatch, 
			(LPVOID*)&lpDispatch);
		lpUnk->Release();
		if (hr == NOERROR)
			m_calc.AttachDispatch(lpDispatch, TRUE);
	}

	// if not dispatch ptr attached yet, need to create one
	if (m_calc.m_lpDispatch == NULL && 
		!m_calc.CreateDispatch(clsid, &e))
	{
		AfxMessageBox(IDP_UNABLE_TO_CREATE);
		EndDialog(IDABORT);
		return FALSE;
	}

	// attempt to make it visible
	m_calc.SetVisible(TRUE);
	if (!m_calc.GetVisible())
	{
		AfxMessageBox(IDP_UNABLE_TO_SHOW);
		EndDialog(IDABORT);
		return FALSE;
	}

	// refresh display to contents of the automation calculator
	OnRefresh();

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