// WaterfallDisplay.cpp: implementation of the CWaterfallDisplay class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WaterfallDisplay.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// CWaterfallDisplay Construction/Destruction
//////////////////////////////////////////////////////////////////////

CWaterfallDisplay::CWaterfallDisplay()
{
	//Init some pointers...
	m_pParentWnd			= NULL;
	m_pBltWindow			= NULL;
	m_pParentDC				= NULL;			

	//Set some defaults...
	m_uiLinesScrolled		= 0;
	m_uiBrightness			= 0;
	m_iLastBright			= 0;
	m_uiContrast			= 0;
	m_uiScrollLines			= DEFAULT_SCROLL;
	m_uiMaxPingSize			= DEFAULT_MAX_PING;							
	m_uiMaxVerticalPings	= ::GetSystemMetrics(SM_CYFULLSCREEN);						
	m_eScalingMode			= MEAN;	
	m_ePaletteType			= GREYSCALE;
	m_eScrollType			= TOP_DOWN;
	m_eLastError			= ERR_NONE;

}

CWaterfallDisplay::~CWaterfallDisplay()
{

}

//////////////////////////////////////////////////////////////////////
// CWaterfallDisplay Routines
//////////////////////////////////////////////////////////////////////

BOOL CWaterfallDisplay::Configure( 	CWnd	*pParentWnd,		CWnd	*pBltArea,   UINT			uiMaxPingSize,
									UINT	uiMaxVerticalPings,	UINT	uiScrollSize, EScrollType	scrollType )
									
{
	//Make sure its valid CWnd...
	if(pParentWnd == NULL) 
	{
		m_eLastError = ERR_NULL_CWND;
		return FALSE;
	}
	
	//OK, assign CWnd ptr
	m_pParentWnd = pParentWnd;
	
	//Assign BLT area CWnd ptr
	if(pBltArea != NULL)
		m_pBltWindow = pBltArea;
		
	//Assign maximums for history
	if(uiMaxPingSize > 0)
		m_uiMaxPingSize = uiMaxPingSize;
	if(uiMaxVerticalPings > 0)
		m_uiMaxVerticalPings = uiMaxVerticalPings;

	//Assign scroll size
	if(uiScrollSize > 0)
		m_uiScrollLines = uiScrollSize;
	
	//Determine scroll behavior and set Blt function ptr
	m_eScrollType = scrollType;
	switch(m_eScrollType)
	{
	case  TOP_DOWN:
	case  BOTTOM_UP:	{	pBltFxn = CWaterfallDisplay::DrawVertDib; break;}
	
	case  LEFT_RIGHT:	{	m_dcSideScroll.CreateCompatibleDC(m_pParentDC);
							pBltFxn = CWaterfallDisplay::DrawLeftRightDib;
							break;}
	
	case  RIGHT_LEFT:	{	m_dcSideScroll.CreateCompatibleDC(m_pParentDC);
							pBltFxn =  CWaterfallDisplay::DrawRightLeftDib;
							break;}
	}
		
	
	//Try and Initialize the DIB and History
	if(m_dibImage.InitDib(scrollType, m_pParentWnd))
	{
		if(m_dibImage.CreateDib(GetBltRect()))
		{
			if(m_pingHistory.Configure(m_uiMaxPingSize, m_uiMaxVerticalPings) == FALSE)
			{	
				m_eLastError = ERR_PNG_HST;
				return FALSE;
			}
				
		}
		else
			m_eLastError = ERR_DIB_BIT;
	}
	else
		m_eLastError = ERR_DIB_HDR;


	//Try and Initialize the DIB and History
	if(m_dibProcImage.InitDib(scrollType, m_pParentWnd))
	{
		if(m_dibProcImage.CreateDib(GetBltRect()))
		{
							
		}
		else
			m_eLastError = ERR_DIB_BIT;
	}
	else
		m_eLastError = ERR_DIB_HDR;
	
	m_byReverseProfile = new BYTE[m_uiMaxPingSize];

	return FALSE;	
}

void CWaterfallDisplay::SetAttributes(EPaletteType palette, EScalingMode scaling )
{
	m_eScalingMode		= scaling;	
	m_ePaletteType		= palette;

	SetPalette(palette);
	SetScaleMode(scaling);
}

BOOL CWaterfallDisplay::AddImageLine8(BYTE *byImagery, UINT uiBytes)
{
	//First check valid
	if((byImagery == NULL) || (uiBytes == 0))
		return FALSE;

	//Add the imagery data
	Record *success = m_pingHistory.AddImagery(byImagery, uiBytes);
	
	if(success == NULL)
	{
		m_eLastError = ERR_PNG_HST;
		return FALSE;
	}
		
	//Ok now lets scroll the images...
	m_dibImage.Scroll();
	m_dibProcImage.Scroll();

	//And now lets stretch the ping in to the 
	//image line we just freed up...
	StretchProfile(&success->data[0], success->size, m_dibImage.m_pBitmapBits);
	
	float minx = m_uiContrast;
	float maxx = 255-m_uiContrast ;
	float slope = .5*(255)/(maxx-minx); 

	//ok, we have a valid ptr to the data
	//
	//Do some brightness/ Contrast adjusting etc
	//Add it to our profile display or
	//add it to the histogram ctrl
	//
	BYTE *raw  = m_dibImage.m_pBitmapBits;
	BYTE *proc = m_dibProcImage.m_pBitmapBits;

	for(int i = 0; i< m_dibImage.GetWidth(); i++)
	{
		
		//BRIGHTNESS
		if((raw[i] + m_uiBrightness) > 255)
			proc[i] = 255;
		else if ((raw[i] + m_uiBrightness)<0)
			proc[i] = 0;
		else
			proc[i] = raw[i] + m_uiBrightness;
		
		//CONTRAST
		if (proc[i] < minx)
			proc[i] = 0;
		else if (proc[i] < maxx)
		{	
			BYTE value = (BYTE)floor((proc[i] - minx)*slope) ;
			proc[i] = value; 
		}
		else
			proc[i] = 255;
		
	}

	//Call the dynamic Blt function ptr only if
	//we have added the requested number of lines
	//as defined by configure(...uiScrollSize)
	if(m_uiLinesScrolled == m_uiScrollLines)
	{
		(this->*pBltFxn)();
		m_uiLinesScrolled = 0;
	}
	
	m_uiLinesScrolled++;

	return TRUE;
}
/*
BOOL  AddImageryLine(void *vpImagery, UINT uiSamples, SHORT	siBytesPerSample )
{
	int check = uiSamples * siBytesPerSample;
	if((vpImagery == NULL) || (check == 0))
	{
		m_eLastError = ERR_ADDLINE_ARG;
		return FALSE;
	}

	switch(siBytesPerSammple)
	{
		//Simple 8 bit data, no bit scaling needed
		case 1:


		//Data needs scaling of some kind...
		case 2:

		//Lets assume 16 bit data
		default: {  break;}
	}



}
*/

CRect CWaterfallDisplay::GetBltRect()
{
	CRect temp;
	
	//If using a frame 
	if(m_pBltWindow)
	{
		m_pBltWindow->GetWindowRect(&temp);
		m_pParentWnd->ScreenToClient(&temp);
		return temp;
	}
	//Otherwise....
	else
	{
		m_pParentWnd->GetClientRect(&temp);
		return temp;
	}	
}

///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
//	BEGIN BLITTING FUNCTIONS
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
void CWaterfallDisplay::DrawVertDib()
{
	//Get our rect
	
	currentDibRect = GetBltRect();
	
	m_pParentDC = m_pParentWnd->GetDC();

	int cnt  = StretchDIBits(m_pParentDC->m_hDC,
                        currentDibRect.left,					// x dest
                        currentDibRect.top,						// y dest
                        currentDibRect.Width(),					// width dest
                        currentDibRect.Height(),				// height dest
                        0,										// x src
                        0,										// y src
                        m_dibProcImage.GetWidth(),				// height src
                        m_dibProcImage.GetHeight(),				// height src
                        m_dibProcImage.m_pBitmapBits,			// data section
                        (BITMAPINFO*)m_dibProcImage.m_pBitmapInfo,	// header section
                        DIB_RGB_COLORS,							// Color info
                        SRCCOPY									// Raster Option
                       );

	m_pParentWnd->ReleaseDC(m_pParentDC);
}

void CWaterfallDisplay::DrawLeftRightDib()
{
	currentDibRect = GetBltRect();
	m_pParentDC = m_pParentWnd->GetDC();

	CBitmap *poldBMP = m_dcSideScroll.SelectObject(&m_dibImage.m_bmpSideScroll);

	int cnt  = StretchDIBits(m_dcSideScroll.m_hDC,
                        0,//currentDibRect.left,					// x dest
                        0,//currentDibRect.top,						// y dest
                        m_dibProcImage.GetWidth(),					// width dest
						m_dibProcImage.GetHeight(),					// height dest
                        0,										// x src
                        0,										// y src
                        m_dibProcImage.GetWidth(),					// height src
                        m_dibProcImage.GetHeight(),					// height src
                        m_dibProcImage.m_pBitmapBits,				// data section
                        (BITMAPINFO*)m_dibProcImage.m_pBitmapInfo,	// header section
                        DIB_RGB_COLORS,							// Color info
                        SRCCOPY									// Raster Option
                );
	
	//plgblt to window

	POINT P[3] = {		{currentDibRect.right,currentDibRect.top}, 
						{currentDibRect.right,currentDibRect.bottom}, 
						{currentDibRect.left,currentDibRect.top} };
	
	if(::PlgBlt(m_pParentDC->m_hDC, P, m_dcSideScroll.m_hDC, 0,0,m_dibImage.GetWidth(), m_dibImage.GetHeight(),NULL,0,0) == 0)
	{
		m_eLastError = 	ERR_PLG_BLT;
	}
	
	m_dcSideScroll.SelectObject(poldBMP);
	m_pParentWnd->ReleaseDC(m_pParentDC);
}

void CWaterfallDisplay::DrawRightLeftDib()
{
	currentDibRect = GetBltRect();
	m_pParentDC = m_pParentWnd->GetDC();

	CBitmap *poldBMP = m_dcSideScroll.SelectObject(&m_dibImage.m_bmpSideScroll);

	int cnt  = StretchDIBits(m_dcSideScroll.m_hDC,
                        0,//currentDibRect.left,					// x dest
                        0,//currentDibRect.top,						// y dest
                        m_dibProcImage.GetWidth(),						// width dest
						m_dibProcImage.GetHeight(),						// height dest
                        0,											// x src
                        0,											// y src
                        m_dibProcImage.GetWidth(),						// height src
                        m_dibProcImage.GetHeight(),						// height src
                        m_dibProcImage.m_pBitmapBits,					// data section
                        (BITMAPINFO*)m_dibProcImage.m_pBitmapInfo,		// header section
                        DIB_RGB_COLORS,								// Color info
                        SRCCOPY										// Raster Option
                );
	
	//plgblt to window

	POINT P[3] = {		{currentDibRect.left,currentDibRect.top}, 
						{currentDibRect.left,currentDibRect.bottom}, 
						{currentDibRect.right,currentDibRect.top} };
	
	if(::PlgBlt(m_pParentDC->m_hDC, P, m_dcSideScroll.m_hDC, 0,0,m_dibImage.GetWidth(), m_dibImage.GetHeight(),NULL,0,0) == 0)
	{
		m_eLastError = 	ERR_PLG_BLT;
	}
	
	m_dcSideScroll.SelectObject(poldBMP);
	m_pParentWnd->ReleaseDC(m_pParentDC);


}

///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
//	BEGIN SETTINGS/RESIZE FUNCTIONS
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////

void CWaterfallDisplay::Resize()
{
	m_dibImage.CreateDib(GetBltRect());
	m_dibProcImage.CreateDib(GetBltRect());
	FillDIBBuffer();
	Recalculate(m_uiBrightness, m_uiContrast);
	Refresh();
}

void CWaterfallDisplay::Refresh()
{
	(this->*pBltFxn)();
}

void CWaterfallDisplay::SetBrightness(UINT percent, BOOL repaint)
{
	m_iLastBright = m_uiBrightness;
	m_uiBrightness = (int)floor(((float)percent/100)*256);
	
	if(repaint)
	{
		Recalculate(m_uiBrightness, m_uiContrast);
		Refresh();
	}
	
}

void CWaterfallDisplay::SetContrast(UINT factor, BOOL repaint)
{
	m_uiContrast = (int)floor(((float)factor/100)*127);
	if(repaint)
	{
		Recalculate(m_uiBrightness, m_uiContrast);
		Refresh();
	}
}

void CWaterfallDisplay::Recalculate(int uiBrightness, int uiContrast )
{
	

	BYTE *data = m_dibImage.m_pBitmapBits;
	BYTE *proc = m_dibProcImage.m_pBitmapBits;

	float minx = uiContrast;
	float maxx = 255-uiContrast ;
	float slope = .5*(255)/(maxx-minx); 

	for(int i =0; i < m_dibImage.m_uiWidth*m_dibImage.m_uiHeight; i++)
	{		
		//BRIGHTNESS
		if((data[i] + uiBrightness)>255)
			proc[i] = 255;
		else if ((data[i] + uiBrightness)<0)
			proc[i] = 0;
		else
			proc[i] = data[i] + uiBrightness;
		
		//CONTRAST
		if (proc[i] < minx)
			proc[i] = 0 ;
		else if (proc[i] < maxx)
			proc[i] = (BYTE)floor((proc[i] - minx)*slope) ;
		else
			proc[i] = 255;	 
		

	}
}

void CWaterfallDisplay::SetPalette(EPaletteType paletteType)
{
	m_dibProcImage.FillPalette(paletteType);
	//m_dibImage.FillPalette(paletteType);
	Refresh();
}

void CWaterfallDisplay::SetScaleMode(EScalingMode scaleMode)
{
	m_eScalingMode = scaleMode;
}

void CWaterfallDisplay::FillDIBBuffer()
{
	currentDibRect = GetBltRect();

	int sz	= m_pingHistory.GetCount();
	int wd  = m_dibImage.GetWidth();
	
	//Figure out how much to refresh
	int ht;
	if(m_eScrollType > BOTTOM_UP)
		ht  = currentDibRect.Width();	
	else
		ht  = currentDibRect.Height();

	//NOW stretch all, one at a time from history to image
	if(sz)
	{	
		Record *line;
			
		for(int c = 0; c < sz-1; c++)
		{
			if((c == ht))
				break;

			line = m_pingHistory.GetRecord(c);
			StretchProfile(line->data, line->size, m_dibImage.m_pBitmapBits+(c*wd)/*byte offset*/);	
		}
	}
}

void CWaterfallDisplay::StretchProfile(BYTE *data, UINT size, BYTE *imageLine)
{
	// setup
	int     i;
	int     idx;
	int     nError;
	int     inputSize  =  size; 
	UINT    outputSize =  m_dibImage.GetWidth();
	BYTE   *pPixel     =  imageLine;          
	BYTE   *pPoint     =  data;

	// if input and output sizes are the same, copy input to output
	if (inputSize == outputSize) {
	
		for (i=0; i<outputSize; i++) {
			 pPixel[i] = pPoint[i];
		}
		return ;
	} 
	
	else if (inputSize < outputSize) {
	  // init ptr to smaller array and error term
	  idx = nError = 0;

	  // for each element of the larger array
	  for (i=0; i < outputSize; i++) {
		 // set each element of output array to current input element
		 pPixel[i] = pPoint[idx];

		 // increment   error term by smaller array size
		 nError += inputSize;

		 // if error greater than larger array size
		 if (nError >= outputSize) {
			// increment pointer to smaller array
			idx++;
			// subtract larger array size from error term
			nError -= outputSize;
		 }
	  }
	} 
	else {
	  idx = nError = 0;
	  int      count = 0;     // counter for mean/RMS compression
	  double   sum = 0;       //

	  switch(m_eScalingMode)
	  {
	  case MEAN:                          // compress using arithmetic mean
		 for (i=0; i<inputSize; i++) {
			sum += pPoint[i];
			count++;

			nError += outputSize;
			if (nError >= inputSize) {
			   pPixel[idx] = (USHORT)(sum / count);
			   idx++; 
			   nError -= inputSize;
			   count = 0;
			   sum = 0;
			}
		 }
		 break;
	  case PEAK:                          // select highest value
		 for (i=0; i<inputSize; i++) {

			if (pPoint[i] > count) {
			   count = pPoint[i];
			}

			nError += outputSize;
			if (nError >= inputSize) {
			   pPixel[idx] = count;

			   idx++;
			   nError -= inputSize;
			   count =   0;
			}
		 }
		 break;
	  case RMS:                           // compress using RMS
		 for (i=0; i<inputSize; i++) {
			// computer sum of sqares of input element & increment count
			sum += (double)(pPoint[i] * pPoint[i]);
			count++;

			// update error term for output element pointer
			nError += outputSize;

			// if error term >= size of input array
			if (nError >= inputSize) {
			   // compute rms value of output element
			   pPixel[idx] = (USHORT)(sqrt(sum / count));
			   // increment output array pointer and reduce error term
			   idx++;
			   nError -= inputSize;
			   // reset sum and counter variables
			   sum = 0.0;
			   count = 0;
			}
		 }
		 break;
	  }

   }
   return ;
}

EErrorCode CWaterfallDisplay::GetLastError()
{
	return m_eLastError;
}

void CWaterfallDisplay::Cleanup()
{
	m_dibImage.~CDib();
	m_dibProcImage.~CDib();

	m_pingHistory.~CPingHistory();

	if(m_byReverseProfile)
		delete [] m_byReverseProfile;

	m_byReverseProfile = NULL;

	m_pParentWnd			= NULL;
	m_pBltWindow			= NULL;
	m_pParentDC				= NULL;	
}
//////////////////////////////////////////////////////////////////////
// CWaterfallDisplay::CDib    Construction/Destruction
//////////////////////////////////////////////////////////////////////

CWaterfallDisplay::CDib::CDib()
{
	m_pBitmapInfo = NULL;
	m_pBitmapBits = NULL;
	m_uiBytes	  = 0;
}

CWaterfallDisplay::CDib::~CDib()
{
	if(m_pBitmapInfo)
		free((void*)m_pBitmapInfo);
	if(m_pBitmapBits)
		delete [] m_pBitmapBits;

	m_pBitmapInfo = NULL;
	m_pBitmapBits = NULL;

}

//////////////////////////////////////////////////////////////////////
// CWaterfallDisplay::CDib    Routines
//////////////////////////////////////////////////////////////////////
BOOL CWaterfallDisplay::CDib::InitDib(EScrollType eScrollType, CWnd *pParentWnd)
{
	//CALL THIS FUNCTION ONCE TO CREATE THE HEADER, THEN ONLY MODIFY THE WIDTH 
	//AND HEIGTH FEILDS WHEN WINDOW IS RESIZED

	m_pParentWnd = pParentWnd;

	m_eDibScrollType = eScrollType;

	UINT size = (UINT)(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
	
	if(m_pBitmapInfo)
		free((void*)m_pBitmapInfo);

	m_pBitmapInfo = (BITMAPINFO *)malloc(size);
	
	for(int i=0;i<256;i++)
	{
		m_pBitmapInfo->bmiColors[i].rgbBlue	  = (unsigned char)i;
		m_pBitmapInfo->bmiColors[i].rgbRed	  = (unsigned char)i;
		m_pBitmapInfo->bmiColors[i].rgbGreen  = (unsigned char)i;
	}
	
	if(m_pBitmapInfo == NULL)
		return FALSE;	//Houston, we have a problem

	m_pBitmapInfo->bmiHeader.biSize			 = sizeof(BITMAPINFOHEADER);
	m_pBitmapInfo->bmiHeader.biWidth		 = 0;
	m_pBitmapInfo->bmiHeader.biHeight		 = 0; 
	m_pBitmapInfo->bmiHeader.biPlanes		 = 1;
	m_pBitmapInfo->bmiHeader.biBitCount		 = 8;
	m_pBitmapInfo->bmiHeader.biCompression	 = BI_RGB;
	m_pBitmapInfo->bmiHeader.biSizeImage	 = 0;
	m_pBitmapInfo->bmiHeader.biXPelsPerMeter = 0;
	m_pBitmapInfo->bmiHeader.biYPelsPerMeter = 0;
	m_pBitmapInfo->bmiHeader.biClrUsed		 = 0;
	m_pBitmapInfo->bmiHeader.biClrImportant  = 0;
	
	return TRUE;

}

BOOL CWaterfallDisplay::CDib::CreateDib(CRect size)
{
	//Determine scroll behavior
	int factor = 1;
	if((m_eDibScrollType == TOP_DOWN))
		factor = -1;

	if(m_eDibScrollType <= BOTTOM_UP)
	{
		//Set header width and Height
		m_pBitmapInfo->bmiHeader.biWidth  =  UPTO_DWORD(size.Width());
		m_pBitmapInfo->bmiHeader.biHeight =  size.Height() * factor; 
		//Set private width and height
		m_uiWidth  =  UPTO_DWORD(size.Width());
		m_uiHeight =  size.Height();
	}
	else
	{
		//Reverse width and height in this case
		m_pBitmapInfo->bmiHeader.biWidth  =  UPTO_DWORD(size.Height() ); 
		m_pBitmapInfo->bmiHeader.biHeight =  size.Width();
		//Set private width and height
		m_uiWidth  =    UPTO_DWORD(size.Height());
		m_uiHeight =  	size.Width();
	}
    
    // calc size of bitmap
    //m_nInternalWidth = UPTO_DWORD(m_uiWidth); 
    m_uiBytes		 = m_uiWidth * m_uiHeight + 100000;

    //make sure we have valid sizes
    if(m_uiBytes == 0)
	   return FALSE; 

    // allocate memory
	if(m_pBitmapBits)
		delete [] m_pBitmapBits;
	m_pBitmapBits = NULL;

    m_pBitmapBits = new BYTE[m_uiBytes];
   
    //make sure allocation is successful
    if(m_pBitmapBits == NULL)
	   return FALSE;

    //zero out the memory 
    ZeroMemory(m_pBitmapBits, m_uiBytes);

    if(m_eDibScrollType > BOTTOM_UP)
	{	
		m_bmpSideScroll.DeleteObject();
		//Get our bits into a CBitmap
		CDC	*temp = m_pParentWnd->GetDC();
		m_bmpSideScroll.CreateCompatibleBitmap(temp, m_pBitmapInfo->bmiHeader.biWidth, m_pBitmapInfo->bmiHeader.biHeight);
		m_pParentWnd->ReleaseDC(temp);
	}

    return TRUE;
}

UINT CWaterfallDisplay::CDib::GetWidth()
{
	return m_uiWidth;
}

UINT CWaterfallDisplay::CDib::GetHeight()
{
	return m_uiHeight;
}

void CWaterfallDisplay::CDib::Scroll()
{
	UINT  nCount = m_uiBytes - m_uiWidth ;

    ::MoveMemory(m_pBitmapBits + m_uiWidth, m_pBitmapBits, nCount) ;
    ::ZeroMemory(m_pBitmapBits, m_uiBytes - nCount) ; 
}

void CWaterfallDisplay::CDib::FillPalette(EPaletteType palette)
{
	switch(palette)
	{
	case GREYSCALE: {
			 for(int i=0;i<256;i++)
			 {
				m_pBitmapInfo->bmiColors[i].rgbRed	  = (unsigned char)i;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = (unsigned char)i;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = (unsigned char)i;
			 }
			 break;
			}	
	case INVERSE: {//Inverse Grey scale
			 int reverse = 255;
			 for(int i=0;i<256;i++)
			 {
				m_pBitmapInfo->bmiColors[i].rgbRed	  = (unsigned char)reverse;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = (unsigned char)reverse;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = (unsigned char)reverse;
				reverse--;
			 }
			 break;
			}	
	
	case RED: {
			 for(int i=0;i<256;i++)
			 {
				m_pBitmapInfo->bmiColors[i].rgbRed	  = (unsigned char)i;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = 0;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = 0;
			 }
			 break;
			}
	case GREEN: {
			 for(int i=0;i<256;i++)
			 {		
				m_pBitmapInfo->bmiColors[i].rgbRed	  = 0;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = (unsigned char)i;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = 0;
			 }
			 break;
			}
	case BLUE: {
			 //Blue	
		     int r1 = 29;
		     int g1 = 62;
		     int b1 = 88;
		     //Lighter blue
		     int r2 = 0;
		     int g2 = 124;
		     int b2 = 195;
			 //white
			 int r3 = 255;
			 int g3 = 255;
		     int b3 = 255;

		     float rstep1 = (float)(r2-r1) / (float)(127-1);	
		     float gstep1 = (float)(g2-g1) / (float)(127-1);	
		     float bstep1 = (float)(b2-b1) / (float)(127-1);
			 
			 float rstep2 = (float)(r3-r2) / (float)(128-1);	
		     float gstep2 = (float)(g3-g2) / (float)(128-1);	
		     float bstep2 = (float)(b3-b2) / (float)(128-1);	
			   
			 for(int i=0;i<127;i++)
			 {   		
				m_pBitmapInfo->bmiColors[i].rgbRed	  = (float)r1 + (float)i * rstep1;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = (float)g1 + (float)i * gstep1;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = (float)b1 + (float)i * bstep1;
			 }
			
			 for(int j=0;j<128;j++)
			 {   
				
				m_pBitmapInfo->bmiColors[i].rgbRed	  = (float)r2 + (float)j * rstep2;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = (float)g2 + (float)j * gstep2;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = (float)b2 + (float)j * bstep2;
				i++;
			 }
			
			 break;
			}
	case RUST: { 
			   
		     //Black	
		     int r1 = 0;
		     int g1 = 0;
		     int b1 = 0;
		     //Semi-Rust
		     int r2 = 161;
		     int g2 = 45;
		     int b2 = 4;
			 //Yellow
			 int r3 = 255;
			 int g3 = 255;
		     int b3 = 0;

		     float rstep1 = (float)(r2-r1) / (float)(127-1);	
		     float gstep1 = (float)(g2-g1) / (float)(127-1);	
		     float bstep1 = (float)(b2-b1) / (float)(127-1);
			 
			 float rstep2 = (float)(r3-r2) / (float)(128-1);	
		     float gstep2 = (float)(g3-g2) / (float)(128-1);	
		     float bstep2 = (float)(b3-b2) / (float)(128-1);	
			   
			 for(int i=0;i<127;i++)
			 {   		
				m_pBitmapInfo->bmiColors[i].rgbRed	  = (float)r1 + (float)i * rstep1;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = (float)g1 + (float)i * gstep1;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = (float)b1 + (float)i * bstep1;
			 }
			
			 for(int j=0;j<128;j++)
			 {   
				
				m_pBitmapInfo->bmiColors[i].rgbRed	  = (float)r2 + (float)j * rstep2;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = (float)g2 + (float)j * gstep2;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = (float)b2 + (float)j * bstep2;
				i++;
			 }
			
			 break;
			}
	
	case SOIL: { 
	   
			 //Black	
			 int r1 = 90;
			 int g1 = 65;
			 int b1 = 44;
			 //Semi-Rust
			 int r2 = 192;
			 int g2 = 162;
			 int b2 = 100;
			 //Yellow
			 int r3 = 255;
			 int g3 = 203;
			 int b3 = 201;

			 float rstep1 = (float)(r2-r1) / (float)(127-1);	
			 float gstep1 = (float)(g2-g1) / (float)(127-1);	
			 float bstep1 = (float)(b2-b1) / (float)(127-1);
			 
			 float rstep2 = (float)(r3-r2) / (float)(128-1);	
			 float gstep2 = (float)(g3-g2) / (float)(128-1);	
			 float bstep2 = (float)(b3-b2) / (float)(128-1);	
			   
			 for(int i=0;i<127;i++)
			 {   		
				m_pBitmapInfo->bmiColors[i].rgbRed	  = (float)r1 + (float)i * rstep1;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = (float)g1 + (float)i * gstep1;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = (float)b1 + (float)i * bstep1;
			 }
			
			 for(int j=0;j<128;j++)
			 {   	
				m_pBitmapInfo->bmiColors[i].rgbRed	  = (float)r2 + (float)j * rstep2;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = (float)g2 + (float)j * gstep2;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = (float)b2 + (float)j * bstep2;
				i++;
			 }
			
			 break;
			
	}
	case MOSS: { 
			//Black	
		     int r1 = 0;
		     int g1 = 0;
		     int b1 = 0;
		     //yellowish
		     int r2 = 242;
		     int g2 = 232;
		     int b2 = 135;
		   
		     float rstep = (float)(r2-r1) / (float)(255-1);	
		     float gstep = (float)(g2-g1) / (float)(255-1);	
		     float bstep = (float)(b2-b1) / (float)(255-1);	
			   
			 for(int i=0;i<256;i++)
			 {   
				
				m_pBitmapInfo->bmiColors[i].rgbRed	  = (float)r1 + (float)i * rstep;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = (float)g1 + (float)i * gstep;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = (float)b1 + (float)i * bstep;
			
			 }
			 break;
			}
	default:{
			 //Grey scale
			 for(int i=0;i<256;i++)
			 {
				m_pBitmapInfo->bmiColors[i].rgbRed	  = (unsigned char)i;
				m_pBitmapInfo->bmiColors[i].rgbGreen  = (unsigned char)i;
				m_pBitmapInfo->bmiColors[i].rgbBlue	  = (unsigned char)i;
			 }
			 break;
			}
	}
	
}

void CWaterfallDisplay::CDib::FillBlack()
{
	if(m_pBitmapBits) 
		ZeroMemory(m_pBitmapBits, m_uiBytes); 
}
//////////////////////////////////////////////////////////////////////
// CWaterfallDisplay::CHistory Construction/Destruction
//////////////////////////////////////////////////////////////////////

CWaterfallDisplay::CPingHistory::CPingHistory()
{
	m_byDataBuffer		= NULL;
	m_uiFront			= 0;
	m_uiRear			= 0;
	m_uiTotalRecords	= 0;															
	m_uiMaxRecords		= 0;									
	m_uiMaxLineBytes	= 0;		
}

CWaterfallDisplay::CPingHistory::~CPingHistory()
{
	if(m_byDataBuffer)
		delete [] m_byDataBuffer;

	m_byDataBuffer = NULL;
}

//////////////////////////////////////////////////////////////////////
// CWaterfallDisplay::CHistory Routines
//////////////////////////////////////////////////////////////////////
BOOL CWaterfallDisplay::CPingHistory::Configure(UINT cx, UINT cy)
{
	//Check for valid size
	if((cx*cy)==0)
		return FALSE;

	m_uiMaxRecords   = cy;
	m_uiMaxLineBytes = cx;

//	TRACE("PING HISTORY CONFIGURE\r\n");
//	TRACE("CX = %d,  CY = %d\r\n", cx, cy);

	//Check if allocated
	if(m_byDataBuffer)
		delete [] m_byDataBuffer; 
	m_byDataBuffer = NULL;

	//Reallocate
	m_byDataBuffer = new BYTE[cx*cy];
	
	//Check again
	if(m_byDataBuffer == NULL)
		return FALSE;

	//Initialize the index vector
	Record rec;
	rec.size = 0;
	rec.data = NULL;
	//assign each cell a pointer into our data buffer
	for(int i = 0; i < cy; i++)
	{
		m_vecHistoryBuffer.push_back(rec);
		m_vecHistoryBuffer[i].data = (m_byDataBuffer + (i*cx));
	}

	//Init front/rear
	m_uiFront = m_uiRear = 0;

	return TRUE;
}

Record *CWaterfallDisplay::CPingHistory::AddImagery(BYTE *byInput, UINT uiSize)
{
	//get Pointer to front of buffer
	Record *front = &(m_vecHistoryBuffer[m_uiFront]);

	//Zero out any existing values if there
	if(front->size)
	{
		if(front->size >  m_uiMaxLineBytes)
			front->size = m_uiMaxLineBytes;

		ZeroMemory((void*)front->data, front->size); 
	}

	//Copy in our new data, and check
	if(memcpy((void*)front->data, byInput, uiSize) == NULL)
		return NULL;

	front->size = uiSize;

	//increment total records only if are size is
	//less than the max records
	if(m_uiTotalRecords < m_uiMaxRecords)
		m_uiTotalRecords++;
	
	//Check if out of range...
	//else increment the front index to next record	
	m_uiFront++;
	
	if(m_uiFront >= m_uiMaxRecords)
		m_uiFront = 0;

	//return a pointer to record we just added
	if(m_uiFront == 0)
	{
		
		return &m_vecHistoryBuffer[m_uiMaxRecords-1];
	}
	
	return &m_vecHistoryBuffer[m_uiFront-1];
}

Record *CWaterfallDisplay::CPingHistory::GetRecord(UINT uiIndex)
{
	if(uiIndex > m_uiTotalRecords)
		return NULL;

//	TRACE("Q SIZE = %d", m_uiTotalRecords);
	//We internally manage the index, because this is a ring buffer.
	//The index arg must be scaled to an actual index in terms of
	//our marker for the current front of the buffer. note, that front
	//points to the next cell to fill, so an index request of [0]
	//means we want the cell[front-1]
	//

	//Our actual cell is the front minus the index
	//so check if that is within range
	
	int test = (m_uiFront-1)-uiIndex;

	if(test >= 0)
	{	
		return &m_vecHistoryBuffer[test];
	}
	else
	{	int offset = (-test);
		return &m_vecHistoryBuffer[m_uiTotalRecords-offset];
	}
	return NULL;
}

UINT CWaterfallDisplay::CPingHistory::GetCount()
{
	return m_uiTotalRecords;
}
