// LogicView.cpp : implementation file
//


#include "stdafx.h"
#include "LogicView.h"
#include "math.h"

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

/////////////////////////////////////////////////////////////////////////////
// CLogicView

IMPLEMENT_DYNCREATE(CLogicView, CView)

CLogicView::CLogicView()
{
	displayMemory = new CDC;
	windowActive = False;
	currentOperation = Waiting;
	currentTrace = -1;
	peekingMessages = False;

	startMove = -1;

	subMode = Select;

	bugEnabled = False;
	
	zoomInCursor = AfxGetApp()->LoadCursor(IDC_ZOOM_IN);
	zoomOutCursor = AfxGetApp()->LoadCursor(IDC_ZOOM_OUT);
	zoomSelCursor = AfxGetApp()->LoadCursor(IDC_ZOOM_SEL);
	openHandCursor = AfxGetApp()->LoadCursor(IDC_OPEN_HAND);
	closeHandCursor = AfxGetApp()->LoadCursor(IDC_CLOSE_HAND);
	arrowCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);

}

CLogicView::~CLogicView()
{
	delete displayMemory;	
}

 
BEGIN_MESSAGE_MAP(CLogicView, CView)
	//{{AFX_MSG_MAP(CLogicView)
	ON_WM_SIZE()
	ON_WM_HSCROLL()
	ON_WM_VSCROLL()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_SETCURSOR()
	ON_WM_KEYDOWN()
	ON_WM_KEYUP()
	ON_WM_CONTEXTMENU()
	//}}AFX_MSG_MAP

	ON_MESSAGE(WM_DRAW_BACKGROUND, OnDrawBackground)
	ON_COMMAND_RANGE(START_MESSAGE_ID, LAST_MESSAGE_ID, OnPlugIn)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CLogicView drawing

void CLogicView::OnDraw(CDC* pDC)
{
	//CDocument* pDoc = GetDocument();
	// TODO: add draw code here

	CRect client_rect;
	GetClientRect(client_rect);
	CBitmap *old_bitmap;
	old_bitmap = displayMemory->SelectObject(myParent->screenBitmap);

	
	pDC->BitBlt(0,0,client_rect.right,client_rect.bottom, displayMemory, 
		myParent->logicSize.left, myParent->logicSize.top, SRCCOPY);
	
	displayMemory->SelectObject(old_bitmap);
}

/////////////////////////////////////////////////////////////////////////////
// CLogicView diagnostics

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

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

/////////////////////////////////////////////////////////////////////////////
// CLogicView message handlers

void CLogicView::OnInitialUpdate() 
{

	//afxDump << "CLogicView::OnInitialUpdate" << endl;

	CView::OnInitialUpdate();

	windowActive = True;

	CClientDC client_dc(this);

	if(displayMemory->GetSafeHdc() == NULL)
	{
		displayMemory->CreateCompatibleDC(&client_dc);
	}

	int h_Min, h_Max;

	h_Min = 0;
	h_Max = (int)(myParent->document->lastTrace - myParent->document->firstTrace);

		
	SetScrollRange(SB_HORZ, h_Min, h_Max);
	SetScrollPos(SB_HORZ, 0);

	// The -2 is one trace of messages and leave the last trace on screen
	SetScrollRange(SB_VERT, 0, myParent->document->traceCount - 2);
	SetScrollPos(SB_VERT, 0);

	myParent->SaveSize();
	myParent->Draw(this);
}

/////////////////////////////////////////////////////////////////////////////
// 
 
BOOL CLogicView::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) 
{
	// TODO: Add your specialized code here and/or call the base class
	
	return CWnd::Create(lpszClassName, lpszWindowName, dwStyle | WS_VSCROLL | WS_HSCROLL, rect, pParentWnd, nID, pContext);
}

void CLogicView::OnSize(UINT nType, int cx, int cy) 
{

	//afxDump << "CLogicView::OnSize" << endl;

	CView::OnSize(nType, cx, cy);
	
	if (windowActive)
	{
		myParent->SaveSize();
		myParent->SetTime();
		myParent->Draw(this);
	}
}



void CLogicView::Render(CRect placement)
{

		MY_RGB color;
		color.red = 255;
		color.blue = 255;
		color.green = 255;
		myParent->DrawCRect( placement,color);

		currentOperation = MakingTrace;
		currentTrace = 0;


		CRect size;
		myParent->logicView->GetClientRect(size);
		myParent->logicView->InvalidateRect(size);

		if (windowActive)
		{
			//drawingTimer = SetTimer(1, 10, NULL);
			color.red = 255;
			color.blue = 0;
			color.green = 0;
			
			myParent->DrawLine(
			myParent->viewedTime.startTime + (myParent->viewedTime.pixelToTime * (myParent->timeSize.left + myParent->timeView->refrenceLinePixel)),
			myParent->viewedTime.startTime + (myParent->viewedTime.pixelToTime * (myParent->timeSize.left + myParent->timeView->refrenceLinePixel)),
			myParent->logicSize.top, myParent->logicSize.bottom, 0, color, 1);


			SetScrollPos(SB_HORZ, (int)myParent->viewedTime.startTime);

			if (!peekingMessages)
			{
				PostMessage(WM_DRAW_BACKGROUND);
			}
		}
}

void CLogicView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* /*pScrollBar*/) 
{
	// TODO: Add your message handler code here and/or call default
	
	int 
		old_position,
		current_position,
		temp_position;

	old_position = current_position = GetScrollPos(SB_VERT);

	BOOL absolute = FALSE;

	switch(nSBCode)
	{
		case SB_THUMBPOSITION:
		case SB_THUMBTRACK:
			current_position = nPos;
			absolute = TRUE;
			break;

		case SB_LINELEFT:
			temp_position = 1;
			current_position -= temp_position;
			break;

		case SB_LINERIGHT:
			temp_position = 1;
			current_position += temp_position;
			break;
		
		case SB_PAGELEFT:
			temp_position = 3;
			current_position -= temp_position;
			break;
		
		case SB_PAGERIGHT:
			temp_position = 3;
			current_position += temp_position;
			break;

		case SB_RIGHT:
			temp_position = 5;
			current_position += temp_position;
			break;

		case SB_LEFT:
			temp_position = 5;
			current_position -= temp_position;
			break;

		default:
			break;
	}

	SetScrollPos(SB_VERT,current_position);

	if (old_position != current_position)
	{
		myParent->Draw(myParent->nameView);
		myParent->Draw(this);
	}

}

void CLogicView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* /*pScrollBar*/) 
{

	int 
		old_position,
		current_position,
		temp_position;

	old_position = current_position = GetScrollPos(SB_HORZ);

	BOOL absolute = FALSE;

	switch(nSBCode)
	{
		case SB_THUMBPOSITION:
		case SB_THUMBTRACK:
			current_position = nPos;
			absolute = TRUE;
			break;

		case SB_LINELEFT:
			temp_position = 1;
			current_position -= temp_position;
			break;

		case SB_LINERIGHT:
			temp_position = 1;
			current_position += temp_position;
			break;
		
		case SB_PAGELEFT:
			temp_position = 10;
			current_position -= temp_position;
			break;
		
		case SB_PAGERIGHT:
			temp_position = 10;
			current_position += temp_position;
			break;

		case SB_RIGHT:
			temp_position = 20;
			current_position += temp_position;
			break;

		case SB_LEFT:
			temp_position = 20;
			current_position -= temp_position;
			break;

		default:
			break;
	}

	MoveTime(current_position, absolute);
	
	myParent->Draw(myParent->timeView);
	myParent->Draw(this);
	
	
}

void CLogicView::MoveTime(int current_position, BOOL absolute)
{

	//CScrollBar *pScrollBar = GetScrollBarCtrl(SB_HORZ);
	CLabRatDoc *document = (CLabRatDoc *)GetDocument();

	int old_position = GetScrollPos(SB_HORZ);
	old_position += (int)document->firstTrace;
	current_position += (int)document->firstTrace;

	Time diffrence = (Time)old_position - (Time)current_position;
	
	if (absolute == TRUE)
	{
		myParent->viewedTime.startTime = current_position; 
	}
	else
	{
		myParent->viewedTime.startTime -= (diffrence)*(SCALES[myParent->viewedTime.secondEnum] * myParent->viewedTime.step)*0.5f;
	}

	Max_Clamp(myParent->viewedTime.startTime, document->lastTrace);
	Min_Clamp(myParent->viewedTime.startTime, document->firstTrace);

	myParent->viewedTime.endTime = ((Time)myParent->viewedTime.startTime) + (myParent->viewedTime.pixelToTime * myParent->timeSize.Width());
	Max_Clamp(myParent->viewedTime.endTime, document->lastTrace);

	SetScrollPos(SB_HORZ,(int)myParent->viewedTime.startTime - ((int)document->firstTrace));

}



void CLogicView::ScaleTime(int scale_factor, CPoint point)
{


	myParent->viewedTime.startTime = 
		myParent->viewedTime.startTime + 
		(point.x * myParent->viewedTime.pixelToTime);

	

	int step_enum = 0;

	for (int i = SCALE_STEP_COUNT-1; i >= 0; --i)
	{
		if (myParent->viewedTime.step <= SCALE_STEP[i])
		{
			step_enum = i;
		}
	}

	int new_step = step_enum + scale_factor;
	int new_second = myParent->viewedTime.secondEnum;
	int seconds_to_add = 0;
	
	
	if (new_step > 0.0f)
	{
		seconds_to_add = (int)new_step / SCALE_STEP_COUNT;
		new_step = new_step - (seconds_to_add * SCALE_STEP_COUNT);
		new_second += seconds_to_add;
	}
	else if (new_step < 0.0f)
	{
		int over = -new_step + SCALE_STEP_COUNT - 1;
		seconds_to_add = ((int)over / SCALE_STEP_COUNT);
		new_step = new_step + (seconds_to_add * SCALE_STEP_COUNT);
		new_second -= seconds_to_add;
	}
	
	if (!(new_second >= SCALES_COUNT) && !(new_second < 0))
	{
		myParent->viewedTime.secondEnum = new_second;
		step_enum = new_step;
		myParent->viewedTime.step = SCALE_STEP[new_step];
	}
	else
	{
		return;
	}

	myParent->SetComboBox(step_enum, myParent->viewedTime.secondEnum);
	myParent->SetTime();
		

}


LONG CLogicView::OnDrawBackground(UINT /*wParam*/, LONG /*lParam*/) 
{

#if 0
	peekingMessages = True;
	MSG message;
	
	while(::PeekMessage(&message, NULL,0,0, PM_REMOVE)) 
	{
		::TranslateMessage(&message);
		::DispatchMessage(&message);
	}

	peekingMessages = False;

#else

	if (bugEnabled)
	{
		peekingMessages = True;
		MSG message;
		
		while(::PeekMessage(&message, NULL,0,0, PM_NOREMOVE)) 
		{
			AfxGetApp()->PumpMessage();
		}

		peekingMessages = False;
	}
#endif

	if (currentTrace == myParent->traceCount)
	{
		return 0L;
	}

	if (currentOperation == MakingTrace)
	{
		//afxDump << "Making : " << currentTrace << endl;
		
		TraceLine *trace_line;
		trace_line = (TraceLine *)myParent->traceListArray->GetAt(currentTrace);
			
		trace_line->MakeTrace(
			myParent->viewedTime.startTime, 
			myParent->viewedTime.endTime, myParent->viewedTime.timeToPixel);
			
		++currentTrace;
		
		if (currentTrace == myParent->traceCount)
		{
			currentTrace = GetScrollPos(SB_VERT);
			currentOperation = DrawingTrace;
		}


		PostMessage(WM_DRAW_BACKGROUND);

	}
	else if (currentOperation == DrawingTrace)
	{

		//afxDump << "Drawing : " << currentTrace << endl;

		int place_to_draw = myParent->borderSize + myParent->logicSize.top;

		int start = GetScrollPos(SB_VERT);

		for (int i = start; i < myParent->traceCount; ++i)
		{
			if (place_to_draw >= myParent->logicSize.bottom && currentTrace != (myParent->traceCount - 1))
			{
				currentTrace = myParent->traceCount - 1; // if off of the screen just
																	  // draw the markers
				break;
			}
			
			TraceLine *trace_line;
			trace_line = (TraceLine *)myParent->traceListArray->GetAt(i);
			
			if (i == currentTrace)
			{
				trace_line->Draw(place_to_draw);
				++currentTrace;
				
				CRect size;
				myParent->logicView->GetClientRect(size);
				myParent->logicView->InvalidateRect(size);

				break;
			}
			place_to_draw += trace_line->CalculateSize();
			place_to_draw += myParent->borderSize;
		}

		if (currentTrace == myParent->traceCount)
		{
			currentTrace = 0;
			currentOperation = Waiting;
			myParent->Draw(myParent->valueView);
			// let it end
		}
		else
		{
			PostMessage(WM_DRAW_BACKGROUND);
		}
	}

	return 0L;

}


void CLogicView::OnLButtonDown(UINT /*nFlags*/, CPoint point) 
{
	
	if (subMode == Move)
	{
		startMove = point.x * myParent->viewedTime.pixelToTime;
	}
	else if (subMode == Select)
	{
	
		myParent->viewedTime.startTimeSel = myParent->viewedTime.startTime + (point.x * myParent->viewedTime.pixelToTime);
		myParent->viewedTime.endTimeSel = myParent->viewedTime.startTime + (point.x * myParent->viewedTime.pixelToTime);
		

		Min_Clamp(myParent->viewedTime.startTimeSel,myParent->document->firstTrace);
		Max_Clamp(myParent->viewedTime.endTimeSel,myParent->document->lastTrace);

		SetStatusBar();
		myParent->Draw(myParent->timeView);
	}
	else if (subMode == SelectRegion)
	{
		myParent->viewedTime.endTimeSel = myParent->viewedTime.startTime + (point.x * myParent->viewedTime.pixelToTime);
		Max_Clamp(myParent->viewedTime.endTimeSel,myParent->document->lastTrace);
		SetStatusBar();
		myParent->Draw(myParent->timeView);

	}
}

void CLogicView::OnLButtonUp(UINT /*nFlags*/, CPoint point) 
{

	if (subMode == ZoomIn)
	{ 
		ScaleTime(-1, point);
		myParent->Draw(myParent->timeView);
		myParent->Draw(this);
	}
	else if (subMode == ZoomOut)
	{
		ScaleTime(1, point);
		myParent->Draw(myParent->timeView);
		myParent->Draw(this);
	}
	else if (subMode == Move)
	{
		DragTime(point);
		myParent->Draw(myParent->timeView);
		myParent->Draw(this);
		startMove = -1;
	}
	if (subMode == Select)
	{
		myParent->viewedTime.endTimeSel = myParent->viewedTime.startTime + (point.x * myParent->viewedTime.pixelToTime);
		Max_Clamp(myParent->viewedTime.endTimeSel,myParent->document->lastTrace);
		SetStatusBar();
		myParent->Draw(myParent->timeView);

	}
	if (subMode == SelectRegion)
	{
		myParent->viewedTime.endTimeSel = myParent->viewedTime.startTime + (point.x * myParent->viewedTime.pixelToTime);
		Max_Clamp(myParent->viewedTime.endTimeSel,myParent->document->lastTrace);
		SetStatusBar();
		myParent->Draw(myParent->timeView);
	}
	
	
}

void CLogicView::OnMouseMove(UINT nFlags, CPoint point) 
{
	
	if (subMode == Move)
	{
		if (startMove != -1)
		{
			DragTime(point);

			myParent->Draw(myParent->timeView);
			if(bugEnabled)
			{
				myParent->Draw(this);
			}
		}
	}
	if (subMode == Select || subMode == SelectRegion)
	{
		if (nFlags & MK_LBUTTON)
		{
			myParent->viewedTime.endTimeSel = myParent->viewedTime.startTime + (point.x * myParent->viewedTime.pixelToTime);
			Max_Clamp(myParent->viewedTime.endTimeSel,myParent->document->lastTrace);
			SetStatusBar();
			myParent->Draw(myParent->timeView);
		}
	}

	CView::OnMouseMove(nFlags, point);
}

void CLogicView::DragTime(CPoint points_to_move) 
{
	Time time_to_move = points_to_move.x * myParent->viewedTime.pixelToTime;
	time_to_move = startMove - time_to_move;
	startMove = points_to_move.x * myParent->viewedTime.pixelToTime;

	//afxDump << time_to_move << endl;

	myParent->viewedTime.startTime += time_to_move;
	myParent->viewedTime.endTime = 
		myParent->viewedTime.startTime + (myParent->viewedTime.pixelToTime * myParent->timeSize.Width());
	Max_Clamp(myParent->viewedTime.endTime, myParent->document->lastTrace);

	SetScrollPos(SB_HORZ, (int)myParent->viewedTime.startTime);
}


BOOL CLogicView::OnSetCursor(CWnd* /* pWnd */, UINT /*nHitTest*/, UINT /* message */) 
{
	

	switch(subMode)
	{
	case ZoomIn:
		::SetCursor(zoomInCursor);
		break;
	case ZoomOut:
		::SetCursor(zoomOutCursor);
		break;
	case ZoomSelect:
		::SetCursor(zoomSelCursor);
		break;
	case Select:
	case SelectRegion:
		::SetCursor(arrowCursor);
		break;
	case Move:
		::SetCursor(openHandCursor);
		break;
	}

	return True;
}

void CLogicView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	
	CView::OnKeyDown(nChar, nRepCnt, nFlags);

	switch(nChar)
	{
	case VK_SHIFT :
		if (subMode == Select || subMode == SelectRegion)
		{
			subMode = SelectRegion;
		}
		else
		{
			subMode = Select;
		}
		::SetCursor(arrowCursor);
		break;

	case VK_CONTROL :
		subMode = Move;
		::SetCursor(openHandCursor);
		break;

	case 'Z':
		subMode = ZoomIn;
		::SetCursor(zoomInCursor);
		break;
	
	case 'X':
		subMode = ZoomOut;
		::SetCursor(zoomOutCursor);
		break;

	}
}

void CLogicView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	
	CView::OnKeyUp(nChar, nRepCnt, nFlags);
	switch(nChar)
	{
	case VK_SHIFT:
	case VK_CONTROL :
	case 'Z':
	case 'X':
		if (myParent->currentMode == CChildFrame::ZoomMode)
		{
			subMode = ZoomIn;
			::SetCursor(zoomInCursor);
		}
		else if (myParent->currentMode == CChildFrame::MoveMode)
		{
			subMode = Move;
			::SetCursor(openHandCursor);
		}
		if (myParent->currentMode == CChildFrame::SelectMode)
		{
			subMode = Select;
			::SetCursor(arrowCursor);
		}

		break;
	}
}


void CLogicView::OnContextMenu(CWnd* /*pWnd*/, CPoint point) 
{

	int selection = FindSelectionScreen(point);

	if (selection != -1)
	{
		CMenu menu;
		menu.CreateMenu();
		CMenu *sub_menu;
		TraceLine *trace_line;
		menuOnTraceIndex = selection;
		trace_line = (TraceLine *)myParent->traceListArray->GetAt(selection);

		sub_menu = myParent->analyzerRegistry.CreateMenu(trace_line->GetTraceType());	
		
		menu.AppendMenu( MF_POPUP, (int)sub_menu->GetSafeHmenu());
		menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
				point.x, point.y, this);	
		sub_menu->DestroyMenu();
		menu.DestroyMenu();
		delete sub_menu;
	}

}

int CLogicView::FindSelectionScreen(CPoint point)
{

	CRect c_rect;
	GetClientRect(c_rect);
	ClientToScreen(c_rect);

	point.x -= c_rect.left;
	point.y -= c_rect.top;

	int trace_hit = -1;

	int top = myParent->borderSize;
	int start = GetScrollPos(SB_VERT);

	for (int i = start; i < myParent->traceCount; ++i)
	{
		
		TraceLine *trace_line;
		trace_line = (TraceLine *)myParent->traceListArray->GetAt(i);
		
		int bottom = top + trace_line->CalculateSize();
		
		if (point.y < bottom && point.y > top)
		{
			trace_hit = i;
		}
		

		top = bottom;
		top += myParent->borderSize;
	}

	return trace_hit;

}


void CLogicView::OnPlugIn( UINT nID )
{
	myParent->analyzerRegistry.Analyze(myParent, menuOnTraceIndex, nID);
}

void CLogicView::SetStatusBar(void)
{

	char temp[60];
	char time[60];
	
	sprintf(time, "%8.4f", (myParent->viewedTime.startTimeSel) / SCALES[myParent->viewedTime.secondEnum]);
	strcpy(temp, time);
	strcat(temp, SCALES_TEXT[myParent->viewedTime.secondEnum]);


	CString str;
	
	str = "StartSel : ";
	str += temp;
	str += "  ";
	
	sprintf(time, "%8.4f", (myParent->viewedTime.endTimeSel) / SCALES[myParent->viewedTime.secondEnum]);
	strcpy(temp, time);
	strcat(temp, SCALES_TEXT[myParent->viewedTime.secondEnum]);

	str += "EndSel : ";
	str += temp;
	str += "  ";

	sprintf(time, "%8.4f", (fabs(myParent->viewedTime.endTimeSel-myParent->viewedTime.startTimeSel)) / SCALES[myParent->viewedTime.secondEnum]);
	strcpy(temp, time);
	strcat(temp, SCALES_TEXT[myParent->viewedTime.secondEnum]);

	str += "TotalTimeSelected : ";
	str += temp;

#if 0
	 str.Format("StartSel: %fs  EndSel: %fs  TotalTimeSelected: %fs", 
		 myParent->viewedTime.startTimeSel, myParent->viewedTime.endTimeSel,
		 fabs(myParent->viewedTime.endTimeSel-myParent->viewedTime.startTimeSel));
#endif

	 CMainFrame *mainfrm = (CMainFrame*) AfxGetApp()->m_pMainWnd;
	 CStatusBar *status = &mainfrm->m_wndStatusBar;
	 status->SetPaneText(1, str);

}
