// GosTracePane.cpp : implementation file
//

#include "stdafx.h"
#include "gosview.h"
#include "GosTracePane.h"
#include "GosViewDoc.h"
#include "TimeFrame.h"
#include "ZoomDialog.h"
#include "math.h"

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

/////////////////////////////////////////////////////////////////////////////
// CGosTracePane

IMPLEMENT_DYNCREATE(CGosTracePane, CView)

CGosTracePane::CGosTracePane()
{

	m_rSecPerPixel = 1.0E-3;	// 10^-9 ..10^+3
	m_tCPUFreq = 731000000;
	m_rPixelPerDiv = 10.0;	// 10 .. 75
	m_rSecPerDiv = m_rSecPerPixel * m_rPixelPerDiv;	// 10^-8 .. 10^+4
	m_tViewStartTick = 0;
	m_tViewStartOffTick = 0;
	m_tDiv0StartTick = 0;
	m_nScrollShift = 0;
	m_fSelectRect = false;
	m_fDisplaySelectRect = false;
	m_fShowEntry = false;
	m_fAlreadyUpdating = false;
	m_nLineThickness = 1;
}

CGosTracePane::~CGosTracePane()
{
}


BEGIN_MESSAGE_MAP(CGosTracePane, CView)
	//{{AFX_MSG_MAP(CGosTracePane)
	ON_WM_ERASEBKGND()
	ON_WM_CREATE()
	ON_WM_SIZE()
	ON_WM_HSCROLL()
	ON_WM_VSCROLL()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_COMMAND(ID_SHOWENTRYOFF, OnShowentryoff)
	ON_UPDATE_COMMAND_UI(ID_SHOWENTRYOFF, OnUpdateShowentryoff)
	ON_COMMAND(ID_SHOWENTRYON, OnShowentryon)
	ON_UPDATE_COMMAND_UI(ID_SHOWENTRYON, OnUpdateShowentryon)
	ON_COMMAND(ID_SINGLE_THICK, OnSingleThick)
	ON_UPDATE_COMMAND_UI(ID_SINGLE_THICK, OnUpdateSingleThick)
	ON_COMMAND(ID_TRIPLE_THICK, OnTripleThick)
	ON_UPDATE_COMMAND_UI(ID_TRIPLE_THICK, OnUpdateTripleThick)
	ON_COMMAND(ID_DOUBLE_THICK, OnDoubleThick)
	ON_UPDATE_COMMAND_UI(ID_DOUBLE_THICK, OnUpdateDoubleThick)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CGosTracePane drawing

void CGosTracePane::QueryZoom()
{
	if( !m_pParentFrame->ZoomDialog()->Ready() )
		return;
	CGosViewDoc* pDoc = (CGosViewDoc *)GetDocument();
	m_tCPUFreq = pDoc->m_nFreq;
	// get the time info from the zoom control
	m_rSecPerPixel = m_pParentFrame->ZoomDialog()->SecPerPixel();
	m_rPixelPerDiv = m_pParentFrame->ZoomDialog()->PixelPerDiv();
	m_rSecPerDiv = m_pParentFrame->ZoomDialog()->SecPerDiv();
	m_tTicksPerDiv = (_int64)(double(m_tCPUFreq) * m_rSecPerDiv);
	m_rTicksPerPixel = double(m_tCPUFreq) * m_rSecPerPixel;
}

char *TimeToString( double secs )
{
	static char TimeStr[20];

	if( secs == 0.0f )
	{
		sprintf(TimeStr,"0S");
	}
	if( secs >= 1.0f )
	{
		sprintf(TimeStr,"%.2f S", secs);
	}
	else
	if( secs*1000.f >= 1.0f )
	{
		sprintf(TimeStr,"%.2f mS", secs*1000.f);
	}
	else
	if( secs*1000000.f >= 1.0f )
	{
		sprintf(TimeStr,"%.2f us", secs*1000000.f);
	}
	else
	{
		sprintf(TimeStr,"%.0f nS", secs*1000000000.f);
	}
	return TimeStr;
}


void CGosTracePane::OnDraw(CDC* pDC)
{
	CGosViewDoc* pDoc = (CGosViewDoc *)GetDocument();

	// Draw the vertical divisions

	if( !m_pParentFrame->ZoomDialog()->Ready() )
		return;

	QueryZoom();


	// find how many ticks into the first visible division the left of the view is
	_int64 leftTickInDiv = ( m_tViewStartOffTick - m_tDiv0StartTick ) % m_tTicksPerDiv;
	// handle -ve mod problem
	if( m_tViewStartOffTick < m_tDiv0StartTick )
		leftTickInDiv += m_tTicksPerDiv;

	RECT rect;
	GetClientRect(&rect);

	double pixno;
	double viewWidth = double(rect.right-rect.left);

	pDC->SelectStockObject( WHITE_PEN );


	for( pixno = (double)(m_tTicksPerDiv - leftTickInDiv) / m_rTicksPerPixel; pixno < viewWidth; pixno += m_rPixelPerDiv )
	{
		pDC->MoveTo( (int)pixno, 0 );
		pDC->LineTo( (int)pixno, rect.bottom - rect.top );
	}

	// Now draw the actual trace

	if( !pDoc->TraceReady() )
		return;

	pDC->SelectStockObject( BLACK_PEN );
	bool independent = true;

	DWORD *pLogEntry;
	DWORD tag = pDoc->m_pLog[0];
	DWORD time;
	DWORD oldTime = pDoc->m_pLog[1];	// so tDiff = 0 first time
	DWORD tDiff;
	int left = 0;
	int right = 0;
	int y = 0;
	DWORD curTag;

	_int64 tRight = pDoc->TLogStart();
	_int64 tLeft;
	_int64 endViewTick = m_tViewStartTick + (_int64)((double)(rect.right-rect.left) * m_rTicksPerPixel);

	CTimeFrame::TagNode dummyNode;
	dummyNode.onScreen = false;

	CTimeFrame::TagNode *pNode = &dummyNode;

	CPen greyPen( PS_SOLID, 1, RGB(128,128,128) );	// thin grey pen

	int lastXforY[2048];
	int i;
	for( i=0; i<2048; i++ )
		lastXforY[i] = 0;

	pLogEntry = pDoc->m_pLog;
	unsigned long startLogLo = (unsigned long)m_tViewStartTick;

	if( ( ( pDoc->m_tLogStart ^ pDoc->m_tLogEnd ) >> 31 ) == 0 )	// check that start & end could be compared directly
	{
		// Quick scan forward to avoid some of the stuff to the left of the visible region
		unsigned int qOff = 0;
		unsigned int qChg = ( (unsigned long)pDoc->m_pLogEnd - (unsigned long)pDoc->m_pLog ) >> 3;	// '1/2 of the whole log
		while( qChg > 64 )
		{
			qChg &= 0xfffffff0;
			if( pLogEntry[qOff+qChg+1] < startLogLo )
				qOff += qChg;
			qChg >>= 1;
		}
		if( qOff > 32 )
			pLogEntry += (qOff-4);
	}

	for( ; pLogEntry < pDoc->m_pLogEnd; oldTime = time )
	{
		curTag = tag;
		tag = *pLogEntry++;
		time = *pLogEntry++;
		tDiff = time - oldTime;
		tLeft = tRight;
		tRight += tDiff;

		if( tRight > endViewTick )
		{
			if( pNode->onScreen )
			{
				if( m_fShowEntry && !(curTag & 0x100000) )
				{
					pDC->SetPixel( left, pNode->screenY-1, 0x0000FF );
				}
				pDC->MoveTo( left, pNode->screenY );
				pDC->LineTo( rect.right, pNode->screenY );

				if( m_nLineThickness > 1 )
				{
					pDC->MoveTo( left, pNode->screenY+1 );
					pDC->LineTo( rect.right, pNode->screenY+1 );
				}
				if( m_nLineThickness > 2 )
				{
					pDC->MoveTo( left, pNode->screenY-1 );
					pDC->LineTo( rect.right, pNode->screenY-1 );
				}

			}
			break;
		}

		if( tRight >= m_tViewStartTick )
		{
			right = TimeToXCoord( tRight );

			if( right > 0 )
			{
				if( pNode->onScreen )
				{
					if( m_fShowEntry && !(curTag & 0x100000) )
					{
						pDC->SetPixel( left, pNode->screenY-3, 0x0000FF );
						pDC->SetPixel( left, pNode->screenY-2, 0x0000FF );
						if( m_nLineThickness > 1 )
						{
							pDC->SetPixel( left+1, pNode->screenY-3, 0x0000FF );
							pDC->SetPixel( left+1, pNode->screenY-2, 0x0000FF );
						}
					}
					if( lastXforY[pNode->screenY] < right )
					{
						lastXforY[pNode->screenY] = right;
						pDC->MoveTo( left, pNode->screenY );
						pDC->LineTo( right+1, pNode->screenY );
						if( m_nLineThickness > 1 )
						{
							pDC->MoveTo( left, pNode->screenY+1 );
							pDC->LineTo( right+1, pNode->screenY+1 );
						}
						if( m_nLineThickness > 2 )
						{
							pDC->MoveTo( left, pNode->screenY+2 );
							pDC->LineTo( right+1, pNode->screenY+2 );
						}

					}
				}
			}
			left = right;
		}

		pNode = m_pParentFrame->AnyIDToTag(tag);
		if( pNode->onScreen )
		{
			if( independent != pNode->independent )
			{
				independent = pNode->independent;
				if( independent )
					pDC->SelectStockObject( BLACK_PEN );
				else
					pDC->SelectObject( greyPen );
			}

		}
	}
	if( !independent )
		pDC->SelectStockObject( BLACK_PEN );	// so it is safe to delete greyPen


	if( m_fDisplaySelectRect )
	{
		RECT rect;
		GetClientRect(&rect);
		CPen pen( PS_SOLID, 1, RGB(255,0,0) );	// thin red pen
		CPen *pOldPen = pDC->SelectObject(&pen);
		pDC->SelectStockObject( NULL_BRUSH );
		pDC->Rectangle(m_nSelectRectStart,0,m_nSelectRectEnd,rect.bottom-rect.top);
		pDC->SelectObject(pOldPen);

		COLORREF oldColor = pDC->SetTextColor(0x000000FF);
		pDC->SetBkMode( TRANSPARENT );
		pDC->TextOut(0,0,TimeToString( fabs(m_rSecPerPixel*(double)(m_nSelectRectEnd-m_nSelectRectStart)) ));
		pDC->SetTextColor(oldColor);



	}

}

/////////////////////////////////////////////////////////////////////////////
// CGosTracePane diagnostics

#ifdef _DEBUG
void CGosTracePane::AssertValid() const
{
	CView::AssertValid();
}

void CGosTracePane::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CGosTracePane message handlers

void CGosTracePane::CalcScrollRange()
{
	QueryZoom();
	CGosViewDoc* pDoc = (CGosViewDoc *)GetDocument();
	_int64 tDiff = pDoc->TLogEnd() - pDoc->TLogStart();

	int oldScrollShift = m_nScrollShift;
	// if we have a long log (e.g. more than 4 Sec on a 1GHz machine) we'll need to scale the scrollbar values
	for( m_nScrollShift=0; (((_int64)(1<<30))<<m_nScrollShift) < tDiff; m_nScrollShift++ )
	{;}

	SCROLLINFO info;
	info.cbSize = sizeof(SCROLLINFO);     
	GetScrollBarCtrl(SB_HORZ)->GetScrollInfo(&info, SIF_PAGE | SIF_RANGE | SIF_POS);
	info.fMask = SIF_RANGE | SIF_POS;     
	info.nMin = 0;     
	info.nMax = (DWORD)( tDiff >> m_nScrollShift ); 
	// adjust the current position based on any change in scroll shift
	if( m_nScrollShift < oldScrollShift )
		info.nPos <<= ( oldScrollShift - m_nScrollShift );
	else if( m_nScrollShift > oldScrollShift )
		info.nPos >>= ( m_nScrollShift - oldScrollShift );
	// clamp the current position to the limits
	if( info.nPos < info.nMin )
		info.nPos = info.nMin;  
	if( info.nPos > info.nMax )
		info.nPos = info.nMax;

	GetScrollBarCtrl(SB_HORZ)->SetScrollRange(info.nMin,info.nMax);
	CalcScrollPage();

}


void CGosTracePane::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
	if( !m_fAlreadyUpdating )
	{
		m_fAlreadyUpdating = true;

		CGosViewDoc* pDoc = (CGosViewDoc *)GetDocument();

		// Recalculate the limits for the scrollbar if the zoom dialog and the new trace are ready

		if( m_pParentFrame->ZoomDialog()->Ready() && pDoc->TraceReady() )
		{
			CalcScrollRange();
		}

		Invalidate(true);	
		m_fAlreadyUpdating = false;
	}
}

BOOL CGosTracePane::OnEraseBkgnd(CDC* pDC) 
{
	if( !CGosEventPane::OnEraseBkgnd(pDC) )
		return false;

	return true;
}

int CGosTracePane::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	return 0;
}



BOOL CGosTracePane::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) 
{
	((CTimeFrame *)(pContext->m_pCurrentFrame))->ZoomDialog()->SetTracePane( this );

	return CGosEventPane::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext);
}

void CGosTracePane::OnSize(UINT nType, int cx, int cy) 
{
	CView::OnSize(nType, cx, cy);
	CalcScrollPage();
}

void CGosTracePane::CalcScrollPage()
{
	CGosViewDoc* pDoc = (CGosViewDoc *)GetDocument();

	// Recalculate the page size for the scrollbar

	if( m_pParentFrame->ZoomDialog()->Ready() && pDoc->TraceReady() )
	{
		double rtDiffLog = (double)(pDoc->TLogEnd() - pDoc->TLogStart());
		RECT rect;
		GetClientRect(&rect);

		double rtSizeView = m_rTicksPerPixel * (double)(rect.right-rect.left);

		SCROLLINFO info;
		info.cbSize = sizeof(SCROLLINFO);     
		GetScrollBarCtrl(SB_HORZ)->GetScrollInfo(&info, SIF_PAGE | SIF_RANGE | SIF_POS);
		info.fMask = SIF_PAGE;
		if( rtSizeView >= rtDiffLog )
			info.nPage = info.nMax - info.nMin;
		else
			info.nPage = (int)(rtSizeView/rtDiffLog*(double)(info.nMax - info.nMin ));
		GetScrollBarCtrl(SB_HORZ)->SetScrollInfo(&info);
		ScrollToRange(info.nPos);
	}
}

void CGosTracePane::ScrollToRange(int curpos)
{
	_int64 tOff = (_int64)curpos;
	tOff <<= m_nScrollShift;
	m_tViewStartOffTick = tOff;
	m_tViewStartTick = ((CGosViewDoc *)GetDocument())->TLogStart() + m_tViewStartOffTick;
	// set range so stats pane can limit scope
	RECT rect;
	GetClientRect(&rect);
	double rtSizeView = m_rTicksPerPixel * (double)(rect.right-rect.left);

	m_pParentFrame->m_tVisibleStart = m_tViewStartTick; 
	m_pParentFrame->m_tVisibleEnd = m_tViewStartTick + (_int64)rtSizeView; 

	GetDocument()->UpdateAllViews(this,0,NULL);	// update the other views

	Invalidate();
}

void CGosTracePane::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	// Get the minimum and maximum scroll-bar positions.
	SCROLLINFO info;
	info.cbSize = sizeof(SCROLLINFO);     

	pScrollBar->GetScrollInfo( &info, SIF_ALL);

	int minpos = info.nMin;
	int maxpos = info.nMax;
	int curpos = info.nPos;

		// Determine the new position of scroll box.
	switch (nSBCode)
	{
	case SB_LEFT:      // Scroll to far left.
		curpos = minpos;
		break;

	case SB_RIGHT:      // Scroll to far right.
		curpos = maxpos;
		break;

	case SB_ENDSCROLL:   // End scroll.
		return;
		//break;

	case SB_LINELEFT:      // Scroll left.
		curpos = max(minpos, curpos - (int) ( info.nPage >> 4 ) );
		break;

	case SB_LINERIGHT:   // Scroll right.
		curpos = min(maxpos, curpos + (int) ( info.nPage >> 4 ) );
		break;

	case SB_PAGELEFT:    // Scroll one page left.
		curpos = max(minpos, curpos - (int) info.nPage);
		break;

	case SB_PAGERIGHT:      // Scroll one page right.
		curpos = min(maxpos, curpos + (int) info.nPage);
		break;

	case SB_THUMBPOSITION: // Scroll to absolute position. nPos is the position
		curpos = info.nTrackPos;      // of the scroll box at the end of the drag operation.
		break;

	case SB_THUMBTRACK:   // Drag scroll box to specified position. nPos is the
		curpos = info.nTrackPos;     // position that the scroll box has been dragged to.
		break;
	default:
		return;
	}

	// Set the new position of the thumb (scroll box).
	pScrollBar->SetScrollPos(curpos);

	ScrollToRange(curpos);

}

void CGosTracePane::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	SCROLLINFO info;
	info.cbSize = sizeof(SCROLLINFO);     
	pScrollBar->GetScrollInfo( &info, SIF_ALL);

	switch( nSBCode & 0x0000ffff )
	{
	case SB_PAGEUP:
		info.nPos = ((unsigned)info.nPos>info.nPage)?(info.nPos-info.nPage):0;
		break;
	case SB_PAGEDOWN:
		info.nPos = ((unsigned)info.nPos+info.nPage>(unsigned)info.nMax)?info.nMax:(info.nPos+info.nPage);
		break;
	case SB_LINEUP:
		info.nPos = (info.nPos>0)?(info.nPos-1):0;
		break;
	case SB_LINEDOWN:
		info.nPos = (info.nPos+1>info.nMax)?info.nMax:(info.nPos+1);
		break;
	case SB_THUMBTRACK:
		info.nPos = nPos;
		break;
	default:
		return;
	}

	m_pParentFrame->m_VScrollPos = info.nPos;

	CGosEventPane::OnVScroll(nSBCode, nPos, pScrollBar);
	m_pParentFrame->DetermineVisibleTags();
}

void CGosTracePane::ZoomViewTo( _int64 startTick, _int64 endTick )
{
	RECT rect;
	GetClientRect(&rect);
	// first change the scale
	m_pParentFrame->ZoomDialog()->ZoomToSecPerPixel( 
		(double)( endTick - startTick ) / (double)m_tCPUFreq / (double)(rect.right-rect.left) );
	// recalculate the scrollbar limits and page size now
	// CalcScrollRange();

	/*
	SCROLLINFO info;
	info.cbSize = sizeof(SCROLLINFO);     
	GetScrollBarCtrl(SB_HORZ)->GetScrollInfo(&info, SIF_PAGE | SIF_RANGE | SIF_POS);
	info.nPos = (int)((startTick - ((CGosViewDoc *)GetDocument())->TLogStart()) >> m_nScrollShift);
	info.fMask = SIF_POS;
	*/
	int newPos = (int)((startTick - ((CGosViewDoc *)GetDocument())->TLogStart()) >> m_nScrollShift);
	GetScrollBarCtrl(SB_HORZ)->SetScrollPos(newPos);
	// convert the actual scrolled values back to start/end times
	ScrollToRange(newPos);
}

void CGosTracePane::OnLButtonDown(UINT nFlags, CPoint point) 
{
	if( m_fDisplaySelectRect )
	{
		m_fDisplaySelectRect = false;
		if( !( point.x <= m_nSelectRectStart && point.x <= m_nSelectRectEnd ) &&
			!( point.x >= m_nSelectRectStart && point.x >= m_nSelectRectEnd ) )
		{
			// we clicked inside the selected rectangle so zoom the time scale to the range selected
			int left,right;
			if( m_nSelectRectStart < m_nSelectRectEnd )
			{
				left = m_nSelectRectStart;
				right = m_nSelectRectEnd;
			}
			else
			{
				left = m_nSelectRectEnd;
				right = m_nSelectRectStart;
			}
			ZoomViewTo( ViewXToTick(left), ViewXToTick(right) );
		}

	}
	else
	{
		m_fSelectRect = true;
		m_nSelectRectStart = point.x;
		m_nSelectRectEnd = point.x;
		m_fDisplaySelectRect = true;
		SetCapture();
	}
	Invalidate();
	// CView::OnLButtonDown(nFlags, point);
}

void CGosTracePane::OnLButtonUp(UINT nFlags, CPoint point) 
{
	if( m_fSelectRect )
	{
		m_fSelectRect = false;
		::ReleaseCapture();
		Invalidate();
	}
	
	// CView::OnLButtonUp(nFlags, point);
}

void CGosTracePane::OnMouseMove(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	if( m_fSelectRect )
	{
		m_nSelectRectEnd = point.x;
		Invalidate();
	}

	CView::OnMouseMove(nFlags, point);
}

void CGosTracePane::OnShowentryoff() 
{
	m_fShowEntry = false;
	Invalidate();	
}

void CGosTracePane::OnUpdateShowentryoff(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable( m_fShowEntry );
}

void CGosTracePane::OnShowentryon() 
{
	m_fShowEntry = true;
	Invalidate();	
}

void CGosTracePane::OnUpdateShowentryon(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable( !m_fShowEntry );
}

void CGosTracePane::OnInitialUpdate() 
{
	CGosViewDoc* pDoc = (CGosViewDoc *)GetDocument();
	CView::OnInitialUpdate();
	
	if( pDoc->TraceReady() )
	{
		m_pParentFrame->SetDocument(pDoc);
		m_pParentFrame->UpdateAllIds();
		m_pParentFrame->ProcessNewLog();
	}
	
}

void CGosTracePane::OnSingleThick() 
{
	m_nLineThickness = 1;
	Invalidate();
}

void CGosTracePane::OnUpdateSingleThick(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable( m_nLineThickness != 1 );
	
}

void CGosTracePane::OnTripleThick() 
{
	m_nLineThickness = 3;
	Invalidate();
}

void CGosTracePane::OnUpdateTripleThick(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable( m_nLineThickness != 3 );
	
}

void CGosTracePane::OnDoubleThick() 
{
	m_nLineThickness = 2;
	Invalidate();
}

void CGosTracePane::OnUpdateDoubleThick(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable( m_nLineThickness != 2 );
	
}
