// Compass.cpp : implementation file
//
// Copyright (C) 1998-1999 Apollocom
// All rights reserved.
//
// Apollocom licenses this source code for use within your application in
// object form. This source code is not to be distributed in any way without
// prior written permission from Apollocom.
//
// Visual Source Safe: $Revision: 5 $
///////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Compass.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CCompass

CCompass::CCompass()
{
	m_fHeading = 0.0;
	m_wTicAlign = COMPASS_TIC_TOP;
}

CCompass::~CCompass()
{
}


BEGIN_MESSAGE_MAP(CCompass, CStatic)
	//{{AFX_MSG_MAP(CCompass)
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCompass message handlers

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
BOOL CCompass::OnEraseBkgnd(CDC* pDC) 
{
	return TRUE;	// do not erase background
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void CCompass::OnPaint() 
{
	CPaintDC dc(this); // device context for painting

	CRect rect;
	GetClientRect(&rect);

	DrawCompass(&rect,&dc);
	
}

///////////////////////////////////////////////////////////////////////////////
// DrawCompass
// The DrawCompass method will draw a compas with the heading centered.
///////////////////////////////////////////////////////////////////////////////
#define DEGREES_IN_VIEW	45
void CCompass::DrawCompass(CRect *pRect, CDC *pDCa)
{
	//
	// Get DC if no DC is passed
	//
	CDC *pDC = pDCa;
	if(pDCa == NULL) pDC = GetDC();

	//
	// Create temporary work DC (memory DC) and bitmap
	//
	CDC dcMem;
	CBitmap bmTemp;
	dcMem.CreateCompatibleDC(pDC);
	bmTemp.CreateCompatibleBitmap( pDC,pRect->Width(),pRect->Height() );
	CBitmap *pbmOld = dcMem.SelectObject(&bmTemp);

	CRect rect = *pRect;

	//
	// Draw background rectangle
	//
	//Use our color...not windows
	//CBrush WorkSpaceBrush( GetSysColor( COLOR_BTNFACE ) );
	CBrush WorkSpaceBrush;
	WorkSpaceBrush.CreateSolidBrush(RGB(255,255,255));
	CBrush *pOldBrush = dcMem.SelectObject(&WorkSpaceBrush);
	CPen *pOldPen = (CPen *)dcMem.SelectStockObject( NULL_PEN );
	dcMem.Rectangle(&rect);
	dcMem.Draw3dRect(&rect,(COLORREF)GetSysColor(COLOR_3DSHADOW),
		(COLORREF)GetSysColor(COLOR_3DHILIGHT));
	dcMem.SelectStockObject( BLACK_PEN);


	//
	// Create rgn
	//
	rect.InflateRect(-1,0);
	CRgn rgnRect;
	rgnRect.CreateRectRgn(rect.left,rect.top,rect.right,rect.bottom);
	dcMem.SelectClipRgn(&rgnRect);
	rect = *pRect;
	rect.InflateRect(30,0);

	//
	// Calculate some ratios
	//
	float fDegreePerDeltaPixel = (float)rect.Width()/(float)DEGREES_IN_VIEW;
	if((int)fDegreePerDeltaPixel < 2) fDegreePerDeltaPixel = 2.0;

	//
	// Calculate most left degree point
	//
	int nStartDegree = (int)m_fHeading - rect.Width()/2 / (int)fDegreePerDeltaPixel;

	//
	// If nStartDegree is < 0 or > 360, adjust to reflect a valid compass heading
	//
	if( nStartDegree < 0 ) nStartDegree = 360 + (nStartDegree % 360);
	else if( nStartDegree > 360 ) nStartDegree %= 360;

	//
	// Create a font and set color
	//
	CFont cFont,cFont2,*cpOldFont;
	cFont.CreatePointFont(120,_T("Arial"));
	cFont2.CreatePointFont(90,_T("Arial"));
	cpOldFont = (CFont *)dcMem.SelectObject(&cFont);
	COLORREF crOldBkColor = dcMem.SetBkColor( RGB(255,255,255) );
	TEXTMETRIC tm;
	dcMem.GetTextMetrics(&tm);

	//
	// Draw compass
	//
	int nTextYPosition = 0;
	UINT y;
	for(int i = 0; i < rect.Width(); i += (int)fDegreePerDeltaPixel)
	{
		//
		// Calculate height of tick mark amd draw it. This is based on the
		// m_wTicAlign flag.

		//
		// Top Ticks
		//
		if( m_wTicAlign == COMPASS_TIC_TOP)
		{
			if( (nStartDegree % 10) == 0)
				y = rect.top + rect.Height()/3;
			else if( (nStartDegree % 5) == 0)
				y = rect.top + rect.Height()/5;
			else
				y = rect.top + rect.Height()/8;
			dcMem.MoveTo(rect.left+i,rect.top);
			dcMem.LineTo(rect.left+i,y);

			nTextYPosition = rect.Height()/2;
		}

		//
		// Bottom tics
		//
		else if( m_wTicAlign == COMPASS_TIC_BOTTOM)
		{
			if( (nStartDegree % 10) == 0)
				y = rect.bottom - rect.Height()/3;
			else if( (nStartDegree % 5) == 0)
				y = rect.bottom - rect.Height()/5;
			else
				y = rect.bottom - rect.Height()/8;
			dcMem.MoveTo(rect.left+i,rect.bottom-1);
			dcMem.LineTo(rect.left+i,y);
			nTextYPosition = rect.Height()/2 - tm.tmHeight;
		}

		//
		// Top and bottom tics
		//
		else if( m_wTicAlign == COMPASS_TIC_BOTH)
		{
			if( (nStartDegree % 10) == 0)
				y = rect.top + rect.Height()/3 - tm.tmHeight/3;
			else if( (nStartDegree % 5) == 0)
				y = rect.top + rect.Height()/5;
			else
				y = rect.top + rect.Height()/8;
			dcMem.MoveTo(rect.left+i,rect.top);
			dcMem.LineTo(rect.left+i,y);

			if( (nStartDegree % 10) == 0)
				y = rect.bottom - rect.Height()/3 + tm.tmHeight/3;
			else if( (nStartDegree % 5) == 0)
				y = rect.bottom - rect.Height()/5;
			else
				y = rect.bottom - rect.Height()/8;
			dcMem.MoveTo(rect.left+i,rect.bottom-1);
			dcMem.LineTo(rect.left+i,y);
			nTextYPosition = rect.Height()/2 - tm.tmHeight/2;
		}

		//
		// Draw text
		//
		if( (nStartDegree % 5) == 0)
		{
				CString str;
				dcMem.SelectObject(&cFont2);
			    str.Format("%d", nStartDegree);
				dcMem.ExtTextOut(
				rect.left+i-dcMem.GetTextExtent(str).cx/2,
				nTextYPosition,
				ETO_OPAQUE,
				NULL,
				str,
				NULL
				);
				dcMem.SelectObject(&cFont);
		}
		if( (nStartDegree % 15) == 0)
		{
			dcMem.SetTextColor(RGB(255,0,0));
			CString s;
			switch(nStartDegree)
			{
				case 0  : s = _T("0") ; break;
				case 45 : s = _T("45"); break;
				case 90 : s = _T("90") ; break;
				case 135: s = _T("135"); break;
				case 180: s = _T("180") ; break;
				case 225: s = _T("225"); break;
				case 270: s = _T("270") ; break;
				case 315: s = _T("315"); break;
				default: s.Format(_T("%d"),nStartDegree); dcMem.SetTextColor(RGB(0,0,0));break;
			}
			
			dcMem.ExtTextOut(
				rect.left+i-dcMem.GetTextExtent(s).cx/2,
				nTextYPosition,
				ETO_OPAQUE,
				NULL,
				s,
				NULL
				);
			dcMem.SetTextColor(RGB(0,0,0));
		}

		//
		// Wrap around
		//
		if(++nStartDegree >= 360) nStartDegree = 0; 
	}

	//
	// Draw center line
	//
	CPen MarkerPen( PS_SOLID, 2, RGB(0,0,255) );
	dcMem.SelectObject( &MarkerPen );
	int cx = rect.left+(rect.Width()/2 / (int)fDegreePerDeltaPixel) * (int)fDegreePerDeltaPixel;
	dcMem.MoveTo( cx,rect.top);
	dcMem.LineTo( cx,rect.bottom);

	//
	// finally draw to screen from temperary dc
	//
	pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,SRCCOPY);

	///////////////////////////////////////////////////////////////////////////
	pDC->SelectClipRgn(NULL);
	rgnRect.DeleteObject();
	dcMem.SelectObject(pOldBrush);
	WorkSpaceBrush.DeleteObject();
	dcMem.SetBkColor( crOldBkColor );
	dcMem.SelectObject(pbmOld);
	bmTemp.DeleteObject();
	dcMem.SelectObject(cpOldFont);
	cFont.DeleteObject();
	dcMem.SelectObject(pOldPen);

	dcMem.SelectObject(cpOldFont);

	///////////////////////////////////////////////////////////////////////////
	if(pDCa == NULL) ReleaseDC(pDC);
}

///////////////////////////////////////////////////////////////////////////////
// Name:		SetPos
// Description:	Sets heading of compas.
// Entry:		UINT nPos - 0 to 359 degrees
///////////////////////////////////////////////////////////////////////////////
UINT CCompass::SetPos(int nPos)
{
	UINT nPrev = (UINT)m_fHeading;

	m_fHeading = (float)nPos;
	CRect rect;
	GetClientRect(&rect);
	DrawCompass(&rect);

	return nPrev;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void CCompass::SetTicAlign(WORD wAlign)
{
	m_wTicAlign = wAlign;
	Invalidate();
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
