// DepthCtrl.cpp: implementation of the CDepthCtrl class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "DepthCtrl.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CDepthCtrl::CDepthCtrl()
{
	//DEFAULT ATTRIBUTES
	m_maxDQSize = DEFAULT_DQ_SIZE;
	m_dMaxDepth = DEFAULT_MAX_DEPTH;
	m_dMinDepth	= DEFAULT_MIN_DEPTH;
	EMarkerType = ROV;
	EUnitSystem = Metric;

	//DEFAULT COLORS
	crWater		= RGB(150,200,255);		//blue water
	crAxes		= RGB(255,0,0);			//red axes
	crShip		= RGB(199,198,130);		//black ship marker
	crShipFill  = RGB(200,100,55);		//Orange like color
	crData		= RGB(255,250,250);		//white data line
	crBottom    = RGB(88,71,8);			//brown
	crGrid		= RGB(55,55,255);		//light blue

	bkColorSet  = FALSE;

	m_PolyLine		= NULL;
	m_nPoints		= 0;
	m_nVertices		= POLYGON_VERTICES;	
	m_nLastDepth	= 0;
	m_fLastAlt		= 0;
	m_nCurrentFrame = 0;
	m_nBottomDepth  = 3000; //Thats deep enough for Init
}

CDepthCtrl::~CDepthCtrl()
{

}

//////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS 
//////////////////////////////////////////////////////////////////////

BOOL CDepthCtrl::InitCtrl(CWnd *pWnd, CRect area)
{
	//save the parent DC & bounding area
	m_pParentWnd = pWnd;
	m_Rect		 = area;

	CDC	*pDC = pWnd->GetDC();

	//create our own DC to work with
	if(!m_DC1.CreateCompatibleDC(pDC))
	{
		pWnd->ReleaseDC(pDC);
		return FALSE;
	}
	pWnd->ReleaseDC(pDC);

	//Calculate ship marker offset
	m_posX		= int(floor((float)area.Width()-120));
	
	//Set beginning depth range
	m_dMinDepth	= 0;
	m_dMaxDepth	= area.Height();

	//Lots of stuff goes in here...
	InitGDI();

	//Now update the client area
	Refresh();

	return TRUE;
}

void CDepthCtrl::Cleanup()
{
	m_DC1.DeleteDC();
	
	m_bmpMain.DeleteObject();
	m_brWater.DeleteObject();
	m_brShip.DeleteObject();
	m_penData.DeleteObject();
	m_penShip.DeleteObject();
	m_penAxis.DeleteObject();
	m_penAxisMarkers.DeleteObject();
	m_ftAxis.DeleteObject();
	m_ftDepth.DeleteObject();
	
	if(m_PolyLine){
		delete [] m_PolyLine;
		m_PolyLine = NULL;
	}
}



void CDepthCtrl::AddDepth(double value, double altitude)
{
	//Pop the last entry if at max
	if(m_qHistory.size() == m_maxDQSize)
		m_qHistory.pop_back();
		
	//Add new depth value
	m_qHistory.push_front((float)value);

	m_fLastAlt	 = (float)altitude;
	m_nLastDepth = value;

	//Check for frame color shift
	//int frame = m_nLastDepth/FRAME_SIZE;
	//if(frame > m_nCurrentFrame)
	//{	m_nCurrentFrame = frame;
	//	ShiftFrameColor();
	//}
	
	//Check for range out of bounds
	if(value > m_dMaxDepth)
	    ShiftRangeDown(value);
	else if(value < m_dMinDepth)
		ShiftRangeUp(value);

	//Redraw
	Refresh();
}

void CDepthCtrl::SetBottomDepth(int value)
{
	//Or maybe ASSERT(value)
	if(value == 0)
		return;

	m_nBottomDepth = value;
}

void CDepthCtrl::Resize(CRect rect)
{
	m_Rect		= rect;
	m_posX		= int(floor((float)rect.Width()-120));
	
	//Reset Beginning depth range
	m_dMaxDepth	= m_dMinDepth + rect.Height();

	

	Refresh();

}

void CDepthCtrl::Refresh()
{
	CreatePlane();
	DrawAxes();	
	DrawShip(m_nLastDepth - m_dMinDepth);
	DrawDepthLine();
	Render();
}

void CDepthCtrl::SetWindowOffsets(int X, int Y)
{
	if((X<0) || (Y<0))
		return;

	offsetX = X;
	offsetY = Y;

	//redraw
}

void CDepthCtrl::SetScaleUnits(int type)
{
	if(type > 1)
		return;

	if(type == 1)
		EUnitSystem  = Metric;
	else
		EUnitSystem  = English;

	//redraw
	Refresh();
}

void CDepthCtrl::SetWaterColor(COLORREF water)
{
	crWater = water;

	bkColorSet = TRUE;

	//redraw
	Refresh();
}

void CDepthCtrl::SetShipMarker(COLORREF crFill)
{
	if(crFill)
		crShip = crFill;
	
	m_brShip.DeleteObject();
	m_brShip.CreateSolidBrush(crShip);

	Refresh();

}

void CDepthCtrl::SetAxisColor(COLORREF Axis)
{
	crAxes = Axis;
	m_penAxis.DeleteObject();
	m_penAxis.CreatePen(PS_SOLID,5,crAxes);
	m_penAxisMarkers.DeleteObject();
	m_penAxisMarkers.CreatePen(PS_SOLID,1,crAxes);
	//redraw 
	Refresh();
}

void CDepthCtrl::SetHistoryColor(COLORREF crHistory)
{
	crData = crHistory;
	
	m_penData.DeleteObject();
	m_penData.CreatePen(PS_SOLID,2,crData);
	//redraw 
	Refresh();

}

void CDepthCtrl::SetDepthTextColor(COLORREF crText)
{
	m_DC1.SetTextColor(crText);
	Refresh();
}

void CDepthCtrl::SetCrossLineColor(COLORREF crLines)
{
	crGrid = crLines;
	m_penGrid.DeleteObject();
	m_penGrid.CreatePen(PS_DOT,1,crGrid);

	Refresh();

}

//////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS 
//////////////////////////////////////////////////////////////////////

void CDepthCtrl::InitGDI()
{
	//Create our fill brushes
	m_brWater.CreateSolidBrush(crWater);	
	m_brShip.CreateSolidBrush(crShipFill);


	//Create our pens
	m_penData.CreatePen(PS_SOLID,2,crData);
	m_penAxis.CreatePen(PS_SOLID,5,crAxes);
	m_penAxisMarkers.CreatePen(PS_SOLID,1,crAxes);
	m_penShip.CreatePen(PS_SOLID,1,crShip);
	m_penGrid.CreatePen(PS_DOT,1,crGrid);

	//Set font attributes
	LOGFONT log;
	log.lfHeight		 = 14;								//Height
	//log.lfWidth		 = 3;								//Width
	log.lfEscapement	 = 0;								//Escapement
	log.lfOrientation	 = 0;								//Orientation
	log.lfWeight		 = FW_THIN;							//Weight
	log.lfItalic		 = FALSE;							//Italic
	log.lfUnderline		 = FALSE;							//Underline
	log.lfStrikeOut		 = 0;								//StrikeOut
	log.lfCharSet		 = ANSI_CHARSET;					//Char set
	log.lfOutPrecision	 = OUT_DEFAULT_PRECIS,				//Precision
	log.lfClipPrecision  = CLIP_DEFAULT_PRECIS,				//Clip Precision
	log.lfQuality		 = PROOF_QUALITY;					//Quality
	log.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS;		//Family
	strcpy(log.lfFaceName,"Arial");							//Typeface name
		
	//Create the Axis font
	m_ftAxis.CreateFontIndirect(&log);				
	
	//Create Depth Font (larger)
	log.lfHeight = 16;
	m_ftDepth.CreateFontIndirect(&log);

}

void CDepthCtrl::CreatePlane()
{
	//Delete any previous bmp
	m_bmpMain.DeleteObject();
	//recreate new bmp with new sizes
	m_bmpMain.CreateBitmap(m_Rect.Width(), m_Rect.Height(),1,32,NULL);
	//select the bmp into memory DC
	CBitmap *poldBmp = m_DC1.SelectObject(&m_bmpMain);	
	FillPlane();
	m_DC1.SelectObject(poldBmp);
}

void CDepthCtrl::FillPlane()
{
	m_brWater.DeleteObject();
	m_brWater.CreateSolidBrush(crWater);

	CBrush *pOldBrush = m_DC1.SelectObject(&m_brWater);
	m_DC1.FloodFill(0,0,crWater);
	m_DC1.SelectObject(pOldBrush);
}

void CDepthCtrl::ShiftFrameColor()
{
	//Check for default behavior option
	if(bkColorSet)
		return;
	
	if(m_nLastDepth > 3000)
		return;
	crWater = RGB(R-5*m_nCurrentFrame,G-3*m_nCurrentFrame,B);
}

void CDepthCtrl::DrawAxes()
{
	int h = m_Rect.Height();

	//Draw the Axis Markers
	CString str;
	CFont *pOldFont = m_DC1.SelectObject(&m_ftAxis);
	m_DC1.SetBkColor(crWater);
	for(int i=0; i<m_Rect.Height(); i++)
	{			
		if((i+m_dMinDepth)%25 == 0)
		{	
			POINT temp = {15,i};
			//draw the grid
			m_DC1.SelectObject(&m_penGrid);	
			m_DC1.MoveTo(temp);
			temp.x = m_Rect.right;
			m_DC1.LineTo(temp);
			temp.x = 0;
			m_DC1.MoveTo(temp);
			//draw the axis mark
			m_DC1.SelectObject(&m_penAxisMarkers);	
			temp.x = 10;
			m_DC1.LineTo(temp);
			//Draw the Text			
			str.Format("%d", i+m_dMinDepth);
			m_DC1.TextOut(temp.x+5, temp.y-5, str);
		}
		if((i+m_dMinDepth) >= m_nBottomDepth){
			void *old = m_DC1.SelectObject(m_brBottom);
			m_DC1.FillSolidRect(0,i+m_dMinDepth,m_Rect.Width(), m_Rect.Height(), crBottom);
			m_DC1.SelectObject((CBrush*)old);
			return;
		}
	}
	//DRAW BASIC VERTICAL DEPTH AXIS
	POINT pt1 = {0,0};
	POINT pt2 = {0,h};
	m_DC1.SelectObject(pOldFont);
	
	CPen * pOldPen = m_DC1.SelectObject(&m_penAxis);
	m_DC1.MoveTo(pt1);
	m_DC1.LineTo(pt2);
	m_DC1.SelectObject(pOldPen);
}

void CDepthCtrl::DrawShip(int y)
{
	//SHIP MARKER LAYOUT - ROV
	///////////////////////////////////////
	//                                   //
	//    2*-*3                          //
	//     |  \      -----------*5       //
	//     |   *4---/             \      //   
	//     *1						*6   //
	//     |   *8---\             /      //
	//     |  /      -----------*7       //
	//   10*-*9							 //
    //									 //
	///////////////////////////////////////

	int x	   = m_posX;	//temp
	int scalar = 3;			//Controls actual size

	CPen *pOldPen = m_DC1.SelectObject(&m_penShip);
	CBrush *pOldBrush = m_DC1.SelectObject(&m_brShip);
	m_DC1.SetBkColor(crWater);
	switch(EMarkerType)
	{
	case ROV:{	//Fill the Polyline values			
				//PT 1
				m_Polygon[0].x = x;
				m_Polygon[0].y = y;
				//PT 2
				m_Polygon[1].x = x;
				m_Polygon[1].y = y+(3*scalar);
				//PT 3
				m_Polygon[2].x = x+scalar;
				m_Polygon[2].y = y+(3*scalar);
				//PT 4
				m_Polygon[3].x = x+(2*scalar);
				m_Polygon[3].y = y+(scalar);
				//PT 5
				m_Polygon[4].x = x+(10*scalar);
				m_Polygon[4].y = y+(2*scalar);
				//PT 6
				m_Polygon[5].x = x+(12*scalar);
				m_Polygon[5].y = y;
				//PT 7
				m_Polygon[6].x = x+(10*scalar);
				m_Polygon[6].y = y-(2*scalar);
				//PT 8
				m_Polygon[7].x = x+(2*scalar);
				m_Polygon[7].y = y-(scalar);
				//PT 9
				m_Polygon[8].x = x+scalar;
				m_Polygon[8].y = y-(3*scalar);
				//PT 10
				m_Polygon[9].x = x;
				m_Polygon[9].y = y-(3*scalar);
	
				m_DC1.Polygon(m_Polygon, POLYGON_VERTICES);

				//NOW DRAW THE ACTUAL DEPTH OUT
				m_DC1.SelectObject(&m_ftDepth);
				//Start at the front most point of marker
				POINT tmp = m_Polygon[1];
				//Move out a litle more, then write
				
				tmp.y -=35;
				CString str;
				str.Format("Depth = %.1f m", m_nLastDepth);
				m_DC1.TextOut(tmp.x, tmp.y, str);
				
				tmp.y +=35;
				str.Format("Alt.    = %.1f m", m_fLastAlt);
				m_DC1.TextOut(tmp.x, tmp.y, str);

				break;
			 }
	case SQUARE:{ 
				m_DC1.SelectObject(&m_ftDepth);
				POINT l = {x,y+(3*scalar)};
				POINT r = {x+(10*scalar), y-(3*scalar)};
				CRect rect(l,r);
				m_DC1.Rectangle(&rect);
				POINT tmp;
				tmp.x = x+6;
				tmp.y = y+10;
				CString str;
				str.Format("%f m", m_nLastDepth);
				m_DC1.TextOut(tmp.x, tmp.y, str);
				}
	case OVAL:{
				m_DC1.SelectObject(&m_ftDepth);
				POINT l = {x,y+(3*scalar)};
				POINT r = {x+(10*scalar), y-(3*scalar)};
				CRect rect(l,r);
				m_DC1.Ellipse(rect);
				POINT tmp;
				tmp.x = x+6;
				tmp.y = y+6;
				CString str;
				str.Format("%f m", m_nLastDepth);
				m_DC1.TextOut(tmp.x, tmp.y, str);
			  }
	}//end Switch


	m_DC1.SelectObject(pOldPen);
	m_DC1.SelectObject(pOldBrush);
}

void CDepthCtrl::DrawDepthLine()
{
	int size   = m_qHistory.size();
	int offset = m_posX; 

	if(size >= 2)
	{
		if(m_PolyLine){
	  	delete [] m_PolyLine;
		m_PolyLine = NULL;
		}
	  
		m_PolyLine = new POINT[size];
	  
		ZeroMemory(m_PolyLine,size*sizeof(POINT));

		for(int i = 0; i < size; i++)
		{	
			
			m_PolyLine[i].x = offset;//--;
			offset -=2;
			//If a value is above our currrent scale, set to 0
			if(m_qHistory[i] < (float)m_dMinDepth)	
				m_PolyLine[i].y = 0;
			//If value is below current scale, set to max height(bottom)
			else if(m_qHistory[i] > m_dMaxDepth)
				m_PolyLine[i].y = m_Rect.Height()+1;
			//otherwise we are in range
			else
				m_PolyLine[i].y = (int)floor(m_qHistory[i]) - m_dMinDepth;		
		  }
		  
			m_DC1.SelectObject(&m_penData); 
			//Draw the depth data
			m_DC1.Polyline(m_PolyLine, size); 
		
		}
	
}

void CDepthCtrl::Render()
{
	CDC	*pDC = m_pParentWnd->GetDC();

	pDC->StretchBlt(m_Rect.left,m_Rect.top,
						    m_Rect.Width(), m_Rect.Height(),
							&m_DC1,
							0,0,m_Rect.Width(),m_Rect.Height(),SRCCOPY);		
	
	m_pParentWnd->ReleaseDC(pDC);
	
}

void CDepthCtrl::ShiftRangeUp(int newMaxDepth)
{
	if((newMaxDepth - m_Rect.Height()) < 0)
	{
		m_dMinDepth = 0;
		m_dMaxDepth = m_Rect.Height();
	}
	else
	{
		m_dMaxDepth = newMaxDepth;	
		m_dMinDepth = newMaxDepth - m_Rect.Height();
	}	
}

void CDepthCtrl::ShiftRangeDown(int newMinDepth)
{
	m_dMinDepth = newMinDepth;
	m_dMaxDepth = newMinDepth + m_Rect.Height();

}

void CDepthCtrl::UpdateShift()
{
	CreatePlane();
	DrawShip(m_nLastDepth-m_dMaxDepth);
	DrawDepthLine();
	DrawAxes();		
	Render();

}