/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// VisErrorReportDialog.cpp : implementation file
//

#include "StdAfx.H"
#include "LevelEdit.H"
#include "VisErrorReportDialog.H"
#include "Utils.H"
#include "VisLog.H"
#include "SceneEditor.H"
#include "CameraMgr.H"
#include "phys.h"
#include "rendobj.h"

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

/////////////////////////////////////////////////////////////////////////////
//
//	Constants
//
/////////////////////////////////////////////////////////////////////////////
static const int AXIS_BMP_WIDTH	= 65;
static const int AXIS_BMP_HEIGHT	= 65;


/////////////////////////////////////////////////////////////////////////////
//
//	Local Inlines
//
/////////////////////////////////////////////////////////////////////////////
static inline HBITMAP Load_Bitmap (UINT res_id)
{
	return (HBITMAP)::LoadImage (::AfxGetResourceHandle (), MAKEINTRESOURCE (res_id), IMAGE_BITMAP, 0, 0, LR_LOADTRANSPARENT);
}

/////////////////////////////////////////////////////////////////////////////
//
// VisErrorReportDialogClass
//
/////////////////////////////////////////////////////////////////////////////
VisErrorReportDialogClass::VisErrorReportDialogClass (CWnd *pParent)
	:	m_CurrentPoint (0),
		m_GraphArea (0, 0, 0, 0),
		m_AxisArea (0, 0, 0, 0),
		m_CurrentMinPoint (0),
		m_CurrentMaxPoint (0),
		m_TotalPoints (0),
		m_bTrackingSel (false),
		m_TrackPixel (0),
		m_SelStartIndex (0),
		m_SelEndIndex (0),
		m_AxisBMP (NULL),
		m_AxisSelBMP (NULL),
		m_AxisSel (0),
		CDialog(VisErrorReportDialogClass::IDD, pParent)
{
	//{{AFX_DATA_INIT(VisErrorReportDialogClass)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT

	m_PrevButton.normal		= Load_Bitmap (IDB_LT_ARROW);
	m_PrevButton.disabled	= Load_Bitmap (IDB_LT_ARROW_D);
	m_NextButton.normal		= Load_Bitmap (IDB_RT_ARROW);
	m_NextButton.disabled	= Load_Bitmap (IDB_RT_ARROW_D);
	m_AxisBMP					= Load_Bitmap (IDB_AXIS_BMP);
	m_AxisSelBMP				= Load_Bitmap (IDB_AXIS_SEL_BMP);

	m_AxisSelAreas[VIS_FORWARD].left		= 1;
	m_AxisSelAreas[VIS_FORWARD].top		= 38;
	m_AxisSelAreas[VIS_FORWARD].right	= m_AxisSelAreas[VIS_FORWARD].left + 20;
	m_AxisSelAreas[VIS_FORWARD].bottom	= m_AxisSelAreas[VIS_FORWARD].top + 16;

	m_AxisSelAreas[VIS_LEFT].left		= 1;
	m_AxisSelAreas[VIS_LEFT].top		= 12;
	m_AxisSelAreas[VIS_LEFT].right	= m_AxisSelAreas[VIS_LEFT].left + 20;
	m_AxisSelAreas[VIS_LEFT].bottom	= m_AxisSelAreas[VIS_LEFT].top + 16;

	m_AxisSelAreas[VIS_BACKWARDS].left		= 42;
	m_AxisSelAreas[VIS_BACKWARDS].top		= 13;
	m_AxisSelAreas[VIS_BACKWARDS].right		= m_AxisSelAreas[VIS_BACKWARDS].left + 20;
	m_AxisSelAreas[VIS_BACKWARDS].bottom	= m_AxisSelAreas[VIS_BACKWARDS].top + 16;
	
	m_AxisSelAreas[VIS_RIGHT].left	= 42;
	m_AxisSelAreas[VIS_RIGHT].top		= 39;
	m_AxisSelAreas[VIS_RIGHT].right	= m_AxisSelAreas[VIS_RIGHT].left + 20;
	m_AxisSelAreas[VIS_RIGHT].bottom	= m_AxisSelAreas[VIS_RIGHT].top + 16;

	m_AxisSelAreas[VIS_UP].left	= 21;
	m_AxisSelAreas[VIS_UP].top		= 1;
	m_AxisSelAreas[VIS_UP].right	= m_AxisSelAreas[VIS_UP].left + 20;
	m_AxisSelAreas[VIS_UP].bottom	= m_AxisSelAreas[VIS_UP].top + 16;

	m_AxisSelAreas[VIS_DOWN].left		= 22;
	m_AxisSelAreas[VIS_DOWN].top		= 47;
	m_AxisSelAreas[VIS_DOWN].right	= m_AxisSelAreas[VIS_DOWN].left + 20;
	m_AxisSelAreas[VIS_DOWN].bottom	= m_AxisSelAreas[VIS_DOWN].top + 16;
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// ~VisErrorReportDialogClass
//
/////////////////////////////////////////////////////////////////////////////
VisErrorReportDialogClass::~VisErrorReportDialogClass (void)
{
	//
	//	Free the BMPs
	//
	if (m_PrevButton.normal)	::DeleteObject (m_PrevButton.normal);
	if (m_PrevButton.disabled) ::DeleteObject (m_PrevButton.disabled);
	if (m_NextButton.normal)	::DeleteObject (m_NextButton.normal);
	if (m_NextButton.disabled) ::DeleteObject (m_NextButton.disabled);

	Free_Point_List ();
	return ;
}

/////////////////////////////////////////////////////////////////////////////
//
// DoDataExchange
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::DoDataExchange (CDataExchange *pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(VisErrorReportDialogClass)
	DDX_Control(pDX, IDC_ZOOM_SLIDER, m_ZoomSlider);
	//}}AFX_DATA_MAP
	return ;
}


BEGIN_MESSAGE_MAP(VisErrorReportDialogClass, CDialog)
	//{{AFX_MSG_MAP(VisErrorReportDialogClass)
	ON_WM_PAINT()
	ON_WM_VSCROLL()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_BN_CLICKED(IDC_PREV_ERROR, OnPrevError)
	ON_BN_CLICKED(IDC_NEXT_ERROR, OnNextError)
	ON_WM_DRAWITEM()
	ON_WM_NCHITTEST()
	ON_WM_SETCURSOR()
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
//
// OnInitDialog
//
/////////////////////////////////////////////////////////////////////////////
BOOL
VisErrorReportDialogClass::OnInitDialog (void)
{
	CDialog::OnInitDialog ();	
	
	//
	//	Register this window for timer-based screen refreshing
	//
	::SetProp (m_hWnd, "ALLOW_UPDATE", (HANDLE)1);

	//
	//	Set the dimensions of the graph area
	//
	::GetWindowRect (::GetDlgItem (m_hWnd, IDC_HISTOGRAM), &m_GraphArea);
	ScreenToClient (&m_GraphArea);
	m_GraphArea.left ++;
	m_GraphArea.right --;
	m_GraphArea.top ++;
	m_GraphArea.bottom --;
	m_Graph.Set_Dimensions (m_GraphArea.Width (), m_GraphArea.Height ());

	//
	//	Set the dimensions of the 'axis' area.
	//
	CRect rect;
	::GetWindowRect (::GetDlgItem (m_hWnd, IDC_AXIS_AREA), &rect);
	ScreenToClient (&rect);
	rect.left ++;
	rect.right --;
	rect.top ++;
	rect.bottom --;

	m_AxisArea.left	= rect.left + (rect.Width () >> 1) - (AXIS_BMP_WIDTH >> 1);
	m_AxisArea.top		= rect.top + (rect.Height () >> 1) - (AXIS_BMP_HEIGHT >> 1);
	m_AxisArea.right	= m_AxisArea.left + AXIS_BMP_WIDTH;
	m_AxisArea.bottom	= m_AxisArea.top + AXIS_BMP_HEIGHT;
	
	// Update the dialog with data from the scene
	Re_Sync_Data ();
	return TRUE;
}


/////////////////////////////////////////////////////////////////////////////
//
// Re_Sync_Data
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::Re_Sync_Data (void)
{
	//
	//	Get the error list from the scene
	//
	VisLogClass &vis_log				= ::Get_Scene_Editor ()->Get_Vis_Log ();	
	VIS_SAMPLE_LIST &error_list	= vis_log.Peek_Error_List ();	
	m_TotalPoints						= error_list.Count ();

	//
	//	Setup the 'zoom' slider
	//
	m_ZoomSlider.SetRange (2, m_TotalPoints >> 1);
	m_ZoomSlider.SetPos (m_TotalPoints >> 2);
	
	// Reset the range of values in the y-axis
	int max	= (m_TotalPoints >> 1) - (m_ZoomSlider.GetPos () - 2);
	m_Graph.Set_Y_Axis_Range (0, max);

	//
	//	Add the vis error points to the graph
	//	
	Free_Point_List ();
	int bad_points = 0;
	for (int index = 0; index < error_list.Count (); index ++) {
		VisSampleClass *sample = error_list[index];
		Add_Point (*sample);

		//
		//	Determining which direction resulted in the largest backface fraction
		//
		float fraction = sample->Get_Biggest_Fraction ();
		m_Graph.Add_Data_Point (fraction);

		//
		//	Determine if this point was bad or not
		//
		int dir_index = sample->Get_Biggest_Fraction_Index ();
		if (sample->Sample_Status ((VisDirType)dir_index) != VIS_STATUS_OK) {
			bad_points ++;
		}
	}

	//
	//	Set the dialog text fields
	//
	int points		= ::Get_Scene_Editor ()->Get_Total_Vis_Points ();
	SetDlgItemInt (IDC_VIS_POINTS, points);
	SetDlgItemInt (IDC_BAD_POINTS, bad_points);	
	SetDlgItemInt (IDC_MAX_POINTS, max);

	int bad_percent = (points > 0) ? ((bad_points * 100) / points) : 0;
	CString message;
	message.Format ("%d %%", bad_percent);
	SetDlgItemText (IDC_PERCENT_BAD, message);

	m_CurrentPoint = 0;
	m_CurrentMinPoint = 0;
	m_CurrentMaxPoint = 0;

	On_Graph_Sel_Changed ();
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Refresh_View
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::Refresh_View (void)
{
	if (m_hWnd != NULL) {
		InvalidateRect (&m_GraphArea, FALSE);
		UpdateWindow ();
	}

	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnPaint
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::OnPaint (void)
{
	CPaintDC dc(this);
	m_Graph.Render (dc, m_GraphArea.left, m_GraphArea.top);
	Paint_Axis_Area (dc);
	return ;	
}


/////////////////////////////////////////////////////////////////////////////
//
// OnVScroll
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::OnVScroll
(
	UINT			nSBCode,
	UINT			nPos,
	CScrollBar *pScrollBar
) 
{	
	// Reset the range of values in the y-axis
	int max	= m_TotalPoints - (m_ZoomSlider.GetPos () - 2);
	m_Graph.Set_Y_Axis_Range (0, max);
	SetDlgItemInt (IDC_MAX_POINTS, max);

	//
	//	Repaint the graph
	//
	Refresh_View ();	

	CDialog::OnVScroll (nSBCode, nPos, pScrollBar);
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnLButtonDown
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::OnLButtonDown (UINT nFlags, CPoint point) 
{
	if (m_GraphArea.PtInRect (point)) {
		 m_TrackPixel = point.x - m_GraphArea.left;
		 m_Graph.Set_Selection (m_TrackPixel, m_TrackPixel);
		 m_bTrackingSel = true;
		 SetCapture ();
		 Refresh_View ();
		 On_Graph_Sel_Changed ();
	} else if (m_AxisArea.PtInRect (point)) {
		Update_Axis_Area (true);
	}

	CDialog::OnLButtonDown (nFlags, point);
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnLButtonUp
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::OnLButtonUp (UINT nFlags, CPoint point) 
{
	if (m_bTrackingSel) {
		 m_bTrackingSel = false;
		 ReleaseCapture ();
	}

	CDialog::OnLButtonUp (nFlags, point);
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnMouseMove
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::OnMouseMove (UINT nFlags, CPoint point) 
{
	if (m_bTrackingSel) {		 
		 int new_pixel = point.x - m_GraphArea.left;
		 new_pixel = max (new_pixel, 0);
		 new_pixel = min (new_pixel, m_GraphArea.right-1);
		 m_Graph.Set_Selection (m_TrackPixel, new_pixel);
		 Refresh_View ();
		 On_Graph_Sel_Changed ();
	}

	CDialog::OnMouseMove (nFlags, point);
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Display_Point
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::Display_Point (const Matrix3D &transform, bool fly_to)
{
	if (fly_to) {
		::Get_Camera_Mgr ()->Fly_To_Transform (transform);	
	} else {
		::Get_Camera_Mgr ()->Set_Transform (transform);
	}

	::Refresh_Main_View ();
	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Free_Point_List
//
//////////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::Free_Point_List (void)
{
	//
	//	Delete all the vis samples
	//
	for (int index = 0; index < m_PointList.Count (); index ++) {
		VisSampleClass *vis_sample = m_PointList[index];
		SAFE_DELETE (vis_sample);
	}

	m_PointList.Delete_All ();
	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Add_Point
//
//////////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::Add_Point (const VisSampleClass &point)
{
	int start		= 0;
	int end			= m_PointList.Count ();
	end				= max (end, 0);
	float fraction = point.Get_Biggest_Fraction ();

	int index = end;
	while (end > start)
	{
		index						= start + ((end - start) >> 1);
		float curr_fraction	= m_PointList[index]->Get_Biggest_Fraction ();

		if (fraction > curr_fraction) {
			if (start == index) { index ++; break; }
			start = index;
		} else if (fraction < curr_fraction) {
			if (end == index) { index --; break; }
			end = index;
		} else {
			break;
		}

	}

	ASSERT (index >=0 );

	//
	//	Simple binary search to find the right place
	// to insert the new data point
	//
	/*while (end > start) {
		
		int index = start + ((end - start) >> 1);
		float curr_fraction = m_PointList[index]->BackfaceFraction;
		if ((index == start) || (fraction == curr_fraction)) {
			break;
		} else if (fraction > curr_fraction) {
			start = index;
		} else if (fraction < curr_fraction) {
			end = index;
		}
	}*/

	//
	//	Insert the new point into the list
	//
	m_PointList.Insert (index, new VisSampleClass (point));
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Find_Value_Index
//
/////////////////////////////////////////////////////////////////////////////
int
VisErrorReportDialogClass::Find_Value_Index (float fraction)
{
	/*int retval = -1;

	//
	//	Simple binary search to find the right place
	// to insert the new data point
	//
	int start	= 0;
	int end		= m_PointList.Count () - 1;
	while ((end > start) && (retval == -1)) {
		
		int index = start + ((end - start) >> 1);
		float curr_fraction = m_PointList[index]->BackfaceFraction;
		if (index == start) {
			retval = (fraction > curr_fraction) ? end : start;
			break;
		} else if (fraction > curr_fraction) {
			start = index;
		} else if (fraction < curr_fraction) {
			end = index;
		} else {
			retval = index;
		}
	}

	if (retval == -1) {
		retval = start;
	}*/

	int start		= 0;
	int end			= m_PointList.Count ();
	end				= max (end, 0);

	int index = end;
	while (end > start)
	{
		index						= start + ((end - start) >> 1);
		float curr_fraction	= m_PointList[index]->Get_Biggest_Fraction ();

		if (fraction > curr_fraction) {
			if (start == index) { index ++; break; }
			start = index;
		} else if (fraction < curr_fraction) {
			if (end == index) { index --; break; }
			end = index;
		} else {
			
			while ((curr_fraction == fraction) && (index > 0)) {
				curr_fraction = m_PointList[--index]->Get_Biggest_Fraction ();
			}

			index = (curr_fraction == fraction) ? index : index + 1;

			//m_ValueList[index].count ++;
			//found = true;
			break;
		}

	}

	ASSERT (index >=0 );

	return index;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	On_Graph_Sel_Changed
//
//////////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::On_Graph_Sel_Changed (void)
{
	float range_min = 0;
	float range_max = 0;
	m_Graph.Get_Selection (range_min, range_max);

	if (range_min >= 0 && range_max >= 0) {
		m_SelStartIndex	= Find_Value_Index (range_min);
		m_SelEndIndex		= Find_Value_Index (range_max);
	} else {
		m_SelStartIndex	= 0;
		m_SelEndIndex		= m_PointList.Count () - 1;
		m_SelEndIndex		= max (m_SelEndIndex, 0);
	}

	m_CurrentPoint = m_SelStartIndex - 1;

	//
	//	Update the dialog UI
	//
	Update_Current_Point_Text ();
	Update_Nav_Buttons ();
	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Update_Nav_Buttons
//
//////////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::Update_Nav_Buttons (void)
{
	bool enable_prev = m_CurrentPoint > m_SelStartIndex;
	bool enable_next = (m_CurrentPoint < m_SelEndIndex) && (m_PointList.Count () > 0);
	::EnableWindow (::GetDlgItem (m_hWnd, IDC_PREV_ERROR), enable_prev);
	::EnableWindow (::GetDlgItem (m_hWnd, IDC_NEXT_ERROR), enable_next);
	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	OnPrevError
//
//////////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::OnPrevError (void)
{
	if (m_CurrentPoint > m_SelEndIndex) {
		m_CurrentPoint = m_SelEndIndex;
	} else {
		m_CurrentPoint --;
		m_CurrentPoint = max (m_CurrentPoint, 0);
	}
	
	//
	//	Show the point
	//
	if (m_CurrentPoint < m_PointList.Count ()) {
		VisSampleClass *vis_sample	= m_PointList[m_CurrentPoint];
		int index						= vis_sample->Get_Biggest_Fraction_Index ();
		
		vis_sample->Set_Cur_Direction ((VisDirType)index);
		Display_Point (vis_sample->Get_Camera_Transform ((VisDirType)index), true);
	}

	//
	//	Update the UI
	//
	Update_Current_Point_Text ();
	Update_Nav_Buttons ();
	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	OnNextError
//
//////////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::OnNextError (void) 
{
	if (m_CurrentPoint < m_SelStartIndex) {
		m_CurrentPoint = m_SelStartIndex;
	} else {
		m_CurrentPoint ++;
		m_CurrentPoint = min (m_CurrentPoint, m_PointList.Count () - 1);
	}

	//
	//	Show the point
	//
	if (m_CurrentPoint < m_PointList.Count ()) {
		VisSampleClass *vis_sample	= m_PointList[m_CurrentPoint];
		int index						= vis_sample->Get_Biggest_Fraction_Index ();

		vis_sample->Set_Cur_Direction ((VisDirType)index);
		Display_Point (vis_sample->Get_Camera_Transform ((VisDirType)index), true);
	}

	//
	//	Update the UI
	//
	Update_Current_Point_Text ();
	Update_Nav_Buttons ();
	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Update_Current_Point_Text
//
//////////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::Update_Current_Point_Text (void)
{
	//
	//	Update the point-range static text control
	//
	int current_point = max ((m_CurrentPoint - m_SelStartIndex) + 1, 0);
	CString message;
	message.Format ("%d of %d", current_point, m_SelEndIndex - m_SelStartIndex);
	SetDlgItemText (IDC_CURRENT_POINT, message);

	if ((m_CurrentPoint >= 0) &&
		 (m_CurrentPoint < m_PointList.Count ()))
	{
		VisSampleClass *vis_sample	= m_PointList[m_CurrentPoint];
	
		//
		//	Update the backface percent for this sample
		//
		float fraction = vis_sample->Get_Biggest_Fraction ();
		message.Format ("%.2f %%", fraction * 100);
		SetDlgItemText (IDC_CURRENT_PERCENT, message);

		//
		//	Get the status string for the current direction
		//
		int dir_index = vis_sample->Get_Cur_Direction  ();
		switch (vis_sample->Sample_Status ((VisDirType)dir_index)) {
			
			case VIS_STATUS_OK:
				message = "Ok";
				break;

			case VIS_STATUS_NOT_TAKEN:
				message = "Not Taken";
				break;

			case VIS_STATUS_ERROR:
				message = "!!Error!!";
				break;

			case VIS_STATUS_BACKFACE_LEAK:
				message = "Leak";
				break;

			case VIS_STATUS_BACKFACE_OVERFLOW:
				message = "Overflow";
				break;
		}

		SetDlgItemText (IDC_CURRENT_POINT_STATUS, message);
	} else {
		SetDlgItemText (IDC_CURRENT_PERCENT, "");
		SetDlgItemText (IDC_CURRENT_POINT_STATUS, "");
	}

	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnDrawItem
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::OnDrawItem
(
	int nIDCtl,
	LPDRAWITEMSTRUCT lpDrawItemStruct
) 
{
	//
	// Determine what state to draw the button in (pushed or normal)
	//
	UINT state = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;
	POINT offset = { 0 };
	if (lpDrawItemStruct->itemState & ODS_SELECTED) {
		state |= DFCS_PUSHED;
		offset.x = 1;
		offset.y = 1;
	}

	// Draw the button's outline
	CRect rect = lpDrawItemStruct->rcItem;
	::DrawFrameControl (lpDrawItemStruct->hDC, rect, DFC_BUTTON, state);

	//
	// Determine which BMP to use
	//
	HBITMAP hbmp = NULL;
	if (nIDCtl == IDC_PREV_ERROR) {
		hbmp = ::IsWindowEnabled (lpDrawItemStruct->hwndItem) ? m_PrevButton.normal : m_PrevButton.disabled;
	} else if (nIDCtl == IDC_NEXT_ERROR) {
		hbmp = ::IsWindowEnabled (lpDrawItemStruct->hwndItem) ? m_NextButton.normal : m_NextButton.disabled;
	}

	HDC mem_dc			= ::CreateCompatibleDC (NULL);
	HBITMAP old_bmp	= (HBITMAP)::SelectObject (mem_dc, hbmp);

	//
	//	Draw the BMP centered in the button
	//
	const int BMP_WIDTH	= 25;
	const int BMP_HEIGHT = 28;
	int x_pos = rect.left + (rect.Width () >> 1) - (BMP_WIDTH >> 1) + offset.x;
	int y_pos = rect.top + (rect.Height () >> 1) - (BMP_HEIGHT >> 1) + offset.y;
	::BitBlt (lpDrawItemStruct->hDC, x_pos, y_pos, BMP_WIDTH, BMP_HEIGHT, mem_dc, 0, 0, SRCCOPY);

	::SelectObject (mem_dc, old_bmp);
	::DeleteDC (mem_dc);
		
	// Draw the focus rectangle if necessary
	if (lpDrawItemStruct->itemState & ODS_FOCUS) {
		::DrawFocusRect (lpDrawItemStruct->hDC, &rect);
	}
	
	// Allow the base class to process this message
	CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Paint_Axis_Area
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::Paint_Axis_Area (HDC hdc)
{
	HDC mem_dc			= ::CreateCompatibleDC (NULL);
	HBITMAP old_bmp	= (HBITMAP)::SelectObject (mem_dc, m_AxisBMP);

	//
	//	Draw the BMP centered in the button
	//
	const int BMP_WIDTH	= 65;
	const int BMP_HEIGHT = 65;
	//int x_pos = m_AxisArea.left + (m_AxisArea.Width () >> 1) - (BMP_WIDTH >> 1);
	//int y_pos = m_AxisArea.top + (m_AxisArea.Height () >> 1) - (BMP_HEIGHT >> 1);
	::BitBlt (hdc, m_AxisArea.left, m_AxisArea.top, BMP_WIDTH, BMP_HEIGHT, mem_dc, 0, 0, SRCCOPY);

	::SelectObject (mem_dc, old_bmp);
	old_bmp	= (HBITMAP)::SelectObject (mem_dc, m_AxisSelBMP);

	//
	//	Paint the selected areas of the axis
	//
	for (int index = 0; index < 6; index ++) {
		if (m_AxisSel & (1 << index)) {
			CRect &rect = m_AxisSelAreas[index];
			int x_pos = m_AxisArea.left + rect.left;
			int y_pos = m_AxisArea.top + rect.top;
			::BitBlt (hdc, x_pos, y_pos, rect.Width (), rect.Height (), mem_dc, rect.left, rect.top, SRCCOPY);
		}
	}


	::SelectObject (mem_dc, old_bmp);
	::DeleteDC (mem_dc);	
	return ;
}

UINT VisErrorReportDialogClass::OnNcHitTest(CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	
	return CDialog::OnNcHitTest(point);
}


/////////////////////////////////////////////////////////////////////////////
//
// OnSetCursor
//
/////////////////////////////////////////////////////////////////////////////
BOOL
VisErrorReportDialogClass::OnSetCursor
(
	CWnd *	pwnd,
	UINT		hit_test,
	UINT		message
)
{
	BOOL retval = FALSE;

	//
	//	Determine if the cursor is over a 'hot-spot' in the axis area.
	//
	if (Update_Axis_Area ()) {
		::SetCursor (::LoadCursor (::AfxGetResourceHandle (), MAKEINTRESOURCE (IDC_HAND_POINTER)));		
	} else {
		retval = CDialog::OnSetCursor (pwnd, hit_test, message);
	}

	return retval;
}


/////////////////////////////////////////////////////////////////////////////
//
// Update_Axis_Area
//
/////////////////////////////////////////////////////////////////////////////
bool
VisErrorReportDialogClass::Update_Axis_Area (bool do_view_change)
{
	CPoint point;
	::GetCursorPos (&point);
	ScreenToClient (&point);	

	point.x -= m_AxisArea.left;
	point.y -= m_AxisArea.top;	

	bool found			= false;	
	int old_sel			= m_AxisSel;
	m_AxisSel			= 0;

	//
	//	Only do the UI stuff if we have a current point.
	//
	if ((m_CurrentPoint >= 0) &&
		 (m_CurrentPoint < m_PointList.Count ()))
	{
		VisSampleClass *vis_sample	= m_PointList[m_CurrentPoint];

		if ((point.x >= 0) && (point.y >= 0)) {

			//
			//	Determine which direction we are over (if any)
			//
			for (int index = 0; index < 6 && !found; index ++) {
				if (m_AxisSelAreas[index].PtInRect (point)) {
					m_AxisSel	|= (1 << index);
					found			= true;
					
					//
					//	Update the camera if necessary
					//
					if (do_view_change) {
						vis_sample->Set_Cur_Direction ((VisDirType)index);
						Display_Point (vis_sample->Get_Camera_Transform ((VisDirType)index), false);
					}
				}
			}
		}
		
		//
		//	Hilight the direction that is currently displayed
		//		
		int direction	= vis_sample->Get_Cur_Direction ();
		int bit			= (1 << direction);
		m_AxisSel		|= bit;
	}
	
	if (old_sel != m_AxisSel) {

		//
		//	Repaint the axis area
		//
		HDC hdc = ::GetDC (m_hWnd);
		Paint_Axis_Area (hdc);
		::ReleaseDC (m_hWnd, hdc);
	}

	return found;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnDestroy
//
/////////////////////////////////////////////////////////////////////////////
void
VisErrorReportDialogClass::OnDestroy (void)
{
	CDialog::OnDestroy();
	::RemoveProp (m_hWnd, "ALLOW_UPDATE");
	return ;
}
