// BathyDisplay.cpp : implementation file
//

#include "stdafx.h"
#include "6046dryend.h"
#include "6046DryEndDlg.h"
#include "BathyDisplay.h"
#include "DlgSimpleInput.h"
#include <math.h>


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CBathyDisplay dialog


CBathyDisplay::CBathyDisplay(CWnd* pParent /*=NULL*/)
	: CDialog(CBathyDisplay::IDD, pParent)
{
	//{{AFX_DATA_INIT(CBathyDisplay)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT

	m_pBuf		= new char[MAX_BATHY_BUFFER];
	m_pFlipBuf	= new char[MAX_BATHY_BUFFER];

    m_dwPos  = 0;

	m_dBeamSpacing		= 0.64377682;//0.6410256;
	m_dSoundVelocity	= 1490.0;

	m_bFirst	= TRUE;
}


void CBathyDisplay::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CBathyDisplay)
		// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CBathyDisplay, CDialog)
	//{{AFX_MSG_MAP(CBathyDisplay)
	ON_WM_LBUTTONDOWN()
	ON_WM_DESTROY()
	ON_WM_SIZE()
	ON_WM_TIMER()
	ON_COMMAND(ID_SETTINGS_COLOR, OnSettingsColor)
	ON_COMMAND(ID_SETTINGS_POINTSIZE, OnSettingsPointsize)
	ON_COMMAND(ID_SETTINGS_TEMPORALSPACING, OnSettingsTemporalspacing)
	ON_WM_GETMINMAXINFO()
	ON_WM_PAINT()
	ON_WM_SHOWWINDOW()
	ON_WM_CLOSE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CBathyDisplay message handlers

BOOL CBathyDisplay::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	m_Menu.LoadMenu(IDR_BATHY_MENU);
	SetMenu(&m_Menu);

	HICON icon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	SetIcon(icon, FALSE);
	
	m_pConfig = &((CMy6046DryEndApp*)AfxGetApp())->m_SystemConfig;
	if(m_pConfig->Open(CSystemConfig::accessRead))
	{
		m_pConfig->Read();
		m_pConfig->Close();

		this->SetWindowPos(NULL, m_pConfig->BATHYWindowRect.left, m_pConfig->BATHYWindowRect.top,
								 m_pConfig->BATHYWindowRect.Width(), m_pConfig->BATHYWindowRect.Height(),
								 SWP_NOZORDER);
	}

	CRect rect;
	GetClientRect(&rect);

	if(!m_bathyDisplay.CreateEx(WS_EX_CLIENTEDGE,
                      AfxRegisterWndClass(CS_VREDRAW | CS_HREDRAW, ::LoadCursor(NULL, IDC_ARROW), (HBRUSH) ::GetStockObject(BLACK_BRUSH),::LoadIcon(NULL, IDI_APPLICATION)),
                      NULL,
                      WS_VISIBLE | WS_CHILD,
                      rect,
                      this,
                      NULL))
		  AfxMessageBox("Failed to initialize OpenGL for Bathy Display!");

	 
	//m_hFile = CreateFile("Sample.pts", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
	//if(m_hFile != INVALID_HANDLE_VALUE)
	//	ReadFile(m_hFile, m_pBuf, MAX_BATHY_BUFFER, &m_dwSize, NULL);

	
	m_bathyDisplay.InitGL();
	m_bathyDisplay.CreateGLWindow();
	m_bathyDisplay.ShowWindow(SW_SHOW);
	m_bathyDisplay.SetPalette(DEFAULT);
	m_bathyDisplay.MoveWindow(&rect);


	m_bathyDisplay.ResizeGL(rect.Width(), rect.Height());

	m_bathyDisplay.ForceRender();

	m_bFlip = m_pConfig->bFlip;
	m_bathyDisplay.SetFlip(m_bFlip);
	
	//SetTimer(0,100,NULL);

	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CBathyDisplay::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	//PostMessage( WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM( point.x, point.y));	
	CDialog::OnLButtonDown(nFlags, point);
}

void CBathyDisplay::OnDestroy() 
{
	CDialog::OnDestroy();
	
	m_bathyDisplay.KillGLWindow();
	
	delete [] m_pBuf;
	m_pBuf = NULL;

	delete [] m_pFlipBuf;
	m_pFlipBuf = NULL;
}

void CBathyDisplay::OnSize(UINT nType, int cx, int cy) 
{
	CDialog::OnSize(nType, cx, cy);
	
	if(m_bathyDisplay.m_hWnd == NULL)
		return;
	
	RECT rect;
	GetClientRect(&rect);
	m_bathyDisplay.MoveWindow(&rect);	
}


void CBathyDisplay::OnSettingsColor() 
{
	CColorDialog dlg;
	if(dlg.DoModal() == IDOK)
	{
		m_bathyDisplay.SetPointColor((DWORD)dlg.GetColor());
		
		//Save the new color
		m_pConfig->bathyPoints = (DWORD)dlg.GetColor();
		if(m_pConfig->Open(CSystemConfig::accessWrite))
		{
			m_pConfig->Save();
			m_pConfig->Close();
		}	
	}
}

void CBathyDisplay::OnSettingsPointsize() 
{
	CDlgSimpleInput dlg;
	dlg.SetMinMax(1,50);
	if(dlg.DoModal() == IDOK)
		m_bathyDisplay.SetPointSize(((float)dlg.GetValue())/10);
}

void CBathyDisplay::OnSettingsTemporalspacing() 
{
	CDlgSimpleInput dlg;
	dlg.SetMinMax(1,11);
	if(dlg.DoModal() == IDOK)
		m_bathyDisplay.SetPointSpace(((float)dlg.GetValue())/100);
	
}

BOOL CBathyDisplay::PreTranslateMessage(MSG* pMsg) 
{
	return CDialog::PreTranslateMessage(pMsg);
}


void CBathyDisplay::AddBathyData(void *pData )
{
	if(pData)
	{
		BOOL err = FALSE;
		//pointer to header
		R7006 *pDat = (R7006*)pData;
		//pointer to data points
		float *points  = (float*)((char*)pDat + sizeof(R7006));
		
		try{
			
			memcpy(m_pBuf, points, sizeof(float)*pDat->beams);

		}
		catch(...)
		{
			TRACE("INVALID BATHY MEMORY EXCEPTION!!!");
			err = TRUE;
		}

		if(!err)
			ProcessBathyData(pDat->beams);	//::PostMessage(this->m_hWnd,BATHY_PROCESS,pDat->beams,0);
		
	}
}

LRESULT CBathyDisplay::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	if(message == BATHY_PROCESS)
	{
		//if(!ProcessBathyData((UINT)wParam))
		//{
		//	TRACE("Error processing Bathy Points\r\n");
		//}
	}	
	if(message == WM_EXITSIZEMOVE)
	{
		CRect rect;
		GetWindowRect(&rect);

		
		m_pConfig->BATHYWindowRect = rect;

		if(m_pConfig->Open(CSystemConfig::accessWrite))
		{
			m_pConfig->Save();
			m_pConfig->Close();
		}

	}

	return CDialog::WindowProc(message, wParam, lParam);
}

BOOL CBathyDisplay::ProcessBathyData( UINT beams )
{

	//Debug option
	/*
	FILE *fp = fopen("C:/BathyData.txt","w+r");
	if(fp)
	{
		fprintf(fp, "Beam      Angle       RAW Value        Depth        Distance \r\n");
	}
	*/

	//Process RAW bathy data first. The range is given in
	//two-way travel time for the ping, so we convert it
	//to a one way travel time and convert to meters
	BOOL success = FALSE;

	try{

		//setup vars for running min and max used for scaling/display
		double mind = 1000000.0, maxd = 0.0, mindist = 1000000.0, maxdist = 0.0;
		
		//Convert char* to float* for ease
		float *data = (float*)(m_pBuf);// + sizeof(R7006));
			
		//more stuff
		double curAngle, resultCos, resultSin; //used for depth calculations
		//double range, depth; //used fo Dist. calculations

		for(int i =0; i< beams; i++)
		{	
			curAngle = i*m_dBeamSpacing - (((float)beams)/2 * m_dBeamSpacing);
			
			resultCos = cos((curAngle*PI/180.0));
			resultSin = sin((curAngle*PI/180.0));

			m_bathyDisplay.m_pDepths[i]		= ((data[i]*m_dSoundVelocity)/2) * resultCos;
			m_bathyDisplay.m_pDistances[i]  = ((data[i]*m_dSoundVelocity)/2) * resultSin;			
				
			//fprintf(fp, "%d         %.1f          %.2f          %.2f        %.2f \r\n",i,curAngle,data[i],m_bathyDisplay.m_pDepths[i],m_bathyDisplay.m_pDistances[i] );
		
			//Min max distance handle
			if(m_bathyDisplay.m_pDistances[i] < mindist)
				mindist = m_bathyDisplay.m_pDistances[i];

			if(m_bathyDisplay.m_pDistances[i] > maxdist)
				maxdist = m_bathyDisplay.m_pDistances[i];
			
			//Min/Max Depth handling
			if(m_bathyDisplay.m_pDepths[i] < mind)
				mind = m_bathyDisplay.m_pDepths[i];

			if(m_bathyDisplay.m_pDepths[i] > maxd)
				maxd = m_bathyDisplay.m_pDepths[i];

		}

		if(m_bFirst)
		{
		  m_bathyDisplay.m_dMaxDepth	 = maxd;
		  m_bathyDisplay.m_dMinDepth	 = mind;
  	      m_bathyDisplay.m_dRealMaxDepth = maxd;
		  m_bathyDisplay.m_dRealMinDepth = mind;
		}
		else
		{
		  //Update the real min and max for display
		  m_bathyDisplay.m_dRealMaxDepth = maxd;
		  m_bathyDisplay.m_dRealMinDepth = mind;

		  //this keeps everything within bounds of the display
		  m_bathyDisplay.m_dMaxDepth = 0.10 * maxd * 1.1 + m_bathyDisplay.m_dMaxDepth * 0.9;
		  m_bathyDisplay.m_dMinDepth = 0.10 * mind * 0.9 + m_bathyDisplay.m_dMinDepth * 0.9;
		}
	
		m_bathyDisplay.m_dMinDistance	 = 0.0;
		m_bathyDisplay.m_dMaxDistance	 = maxdist - mindist;
		m_bathyDisplay.m_dDistanceOffset = mindist;
		m_bathyDisplay.m_dwValues		 = beams;

		//TRACE("\r\nMin Depth : %.1f   Max Depth : %.1f\r\n", mind, maxd);
		//TRACE("Min Dist : %.1f   Max Dist: %.1f\r\n", mindist, maxdist);

		//Now draw it all
		m_bathyDisplay.ForceFullRender();

		/*
		if(fp)
		{
			fclose(fp);
		}
		*/

		success = TRUE;
	}
	catch(...)
	{
		success = FALSE;
	}

	m_bFirst = FALSE;

	return success;
}

void CBathyDisplay::SetSoundVelocity(	double SV )
{
	if(SV > 0)
		m_dSoundVelocity = SV;
}

void CBathyDisplay::SetBeamSpacing(	double BW )
{
	if(BW > 0)
		m_dBeamSpacing = BW;
}

void CBathyDisplay::SetFlip( BOOL bFlip )
{
	m_bFlip = bFlip;
	m_bFirst = TRUE;
	m_bathyDisplay.SetFlip(bFlip);
	m_bathyDisplay.ClearAll();
}

void CBathyDisplay::OnTimer(UINT nIDEvent) 
{
  //This is for demonstration purposes only
  //requires a sample.pts file 	
  
  static int first = 1;
  char nbbuf[32];
  double x, z;
  DWORD cnt = 0, j = 0, k;
  double mind = 1000000.0, maxd = 0.0, mindist = 1000000.0, maxdist = 0.0;

  if(m_dwPos >= m_dwSize)
  {
    m_dwPos = 0;
  }

	if(m_hFile != INVALID_HANDLE_VALUE)
  {
    while(1)
    {
      if(m_pBuf[m_dwPos] == '\n')
      {
        cnt ++;
        if(cnt == 5)
          break;
      }
      m_dwPos++;
    }
    m_dwPos++;

    while(1)
    {
      if(m_pBuf[m_dwPos] == 'I' || m_dwPos >= m_dwSize)
      {
        if(m_pBuf[m_dwPos] == 'I')
        {
          cnt = 0;
          while(1)
          {
            if(m_pBuf[m_dwPos] == '\n')
            {
              cnt ++;
              if(cnt == 4)
                break;
            }
            m_dwPos++;
          }
          m_dwPos++;
        }
        break;
      }

      k=0;
      while(1)
      {
        if(m_pBuf[m_dwPos] == ' ')
          break;
        nbbuf[k] = m_pBuf[m_dwPos];
        k++;
        m_dwPos++;
      }
      nbbuf[k] = '\0';
      x = atof(nbbuf);
      m_dwPos+=3;

      while(1)
      {
        if(m_pBuf[m_dwPos] == ' ')
          break;
        m_dwPos++;
      }
      m_dwPos+=3;

      k=0;
      while(1)
      {
        if(m_pBuf[m_dwPos] == ' ')
          break;
        nbbuf[k] = m_pBuf[m_dwPos];
        k++;
        m_dwPos++;
      }
      nbbuf[k] = '\0';
      z = atof(nbbuf);
	  //TRACE("z = %d\r\n",z);
      m_dwPos+=3;

      while(1)
      {
        if(m_pBuf[m_dwPos] == '\n')
          break;
        m_dwPos++;
      }
      m_dwPos++;

      m_bathyDisplay.m_pDepths[j] = fabs(z);
      m_bathyDisplay.m_pDistances[j] = x;
	  //TRACE("x = %d\r\n",x);

      if(m_bathyDisplay.m_pDepths[j] < mind)
        mind = m_bathyDisplay.m_pDepths[j];

      if(m_bathyDisplay.m_pDepths[j] > maxd)
        maxd = m_bathyDisplay.m_pDepths[j];

      if(m_bathyDisplay.m_pDistances[j] < mindist)
        mindist = m_bathyDisplay.m_pDistances[j];

      if(m_bathyDisplay.m_pDistances[j] > maxdist)
        maxdist = m_bathyDisplay.m_pDistances[j];

      j++;
    }

    if(first)
    {
      m_bathyDisplay.m_dMaxDepth = maxd;
      m_bathyDisplay.m_dMinDepth = mind;
      first = 0;
    }
    else
    {
      m_bathyDisplay.m_dMaxDepth = 0.10 * maxd * 1.1 + m_bathyDisplay.m_dMaxDepth * 0.90;
      m_bathyDisplay.m_dMinDepth = 0.10 * mind * 0.9 + m_bathyDisplay.m_dMinDepth * 0.90;
    }

 //   m_disp2DBathy.m_dMinDepth   = 13.0;
 //   m_disp2DBathy.m_dMaxDepth   = maxd;

    m_bathyDisplay.m_dMinDistance = 0.0;
    m_bathyDisplay.m_dMaxDistance = maxdist - mindist;
    
    m_bathyDisplay.m_dDistanceOffset = mindist;
    m_bathyDisplay.m_dwValues = j;
    
  
  }
  m_bathyDisplay.ForceRender();
  
	
  CDialog::OnTimer(nIDEvent);
}

void CBathyDisplay::OnPaint() 
{
	CPaintDC dc(this); // device context for painting
	
	// TODO: Add your message handler code here
	m_bathyDisplay.ForceRender();
	
	// Do not call CDialog::OnPaint() for painting messages
}


void CBathyDisplay::OnGetMinMaxInfo(MINMAXINFO *lpMMI)
{
	lpMMI->ptMinTrackSize.x = 300;
	lpMMI->ptMinTrackSize.y = 300;

	CDialog::OnPaint();
	CDialog::OnGetMinMaxInfo(lpMMI);
}

void CBathyDisplay::OnShowWindow(BOOL bShow, UINT nStatus) 
{
	CDialog::OnShowWindow(bShow, nStatus);
	
	// TODO: Add your message handler code here
	m_bathyDisplay.ForceRender();
}

void CBathyDisplay::OnClose() 
{
	((CMy6046DryEndDlg*)AfxGetMainWnd())->ForceClose(DSPBATHY);

	
	CDialog::OnClose();
}
