// OutputBlockView.cpp : implementation file
//

#include "stdafx.h"
#include "DiagramEng.h"
#include "OutputBlockView.h"
#include <math.h>				// rqd. since fabs() used in DrawGraph()

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

/////////////////////////////////////////////////////////////////////////////
// COutputBlockView

IMPLEMENT_DYNCREATE(COutputBlockView, CView)

COutputBlockView::COutputBlockView()
{
}

COutputBlockView::~COutputBlockView()
{
}


BEGIN_MESSAGE_MAP(COutputBlockView, CView)
	//{{AFX_MSG_MAP(COutputBlockView)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COutputBlockView drawing

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

	// DiagramEng (start)

	// Var decl.
	int i;
	int j;
	int *data;
	int delta_x = 50;
	int ncols = 20;
	int nrows = 20;
	int n_time_steps = 100;
	int t_cnt;
	double delta_t = 2.0;
	TEXTMETRIC tm;
	CPoint ptText(0,0);
	CString string1;

	// -- MEMORY NEW
	data = new int[n_time_steps];

	// -- Var init.
	pDC->GetTextMetrics(&tm);
	int nLineHeight = tm.tmHeight + tm.tmExternalLeading;
	
	// -- Plot Text
	for(i=0; i<nrows; i++)		// row i
	{
		for(j=0; j<ncols; j++)	// col j
		{
			string1.Format(" (%d,%d)\n", i, j);
			pDC->TextOut(ptText.x, ptText.y, string1);
			ptText.x += delta_x;
		}
		ptText.x = 0;
		ptText.y += nLineHeight;
	}

	// -- Set data
	for(t_cnt=0; t_cnt<n_time_steps; t_cnt++)
	{
		data[t_cnt] = t_cnt*delta_t;
	}

	// -- Plot Graph
	pDC->MoveTo(0, data[0]);
	for(t_cnt=1; t_cnt<n_time_steps; t_cnt++)
	{
		pDC->LineTo(t_cnt, data[t_cnt]);
	}

	// -- MEMORY DELETE
	delete [] data;

	// DiagramEng (end)
}*/

void COutputBlockView::OnDraw(CDC* pDC)
{
	CDocument* pDoc = GetDocument();
	// TODO: add draw code here
	// DiagramEng (start) 
	int ncols;						// no. of cols of m_dOutputMatrix
	int nrows;						// no. of rows of m_dOutputMatrix
	int valid = 0;					// (-1) operational error, (1) no error
	double min;						// min data value
	double max;						// max data value
	double **dOutputMatrix = NULL;	// local output matrix
	CString	sMsg;					// string to be displayed
	LONG wnd_width;					// window width
	LONG wnd_height;				// window height
	RECT rect;						// rectangle data structure (left, top, right, bottom)
	UINT nType = MB_OK;				// style of msg. box
	UINT nIDhelp = 0;				// help context ID for the msg.
	
	// -- GET OUTPUT MATRIX DATA AND DIMENSIONS
	if(m_pOutputBlk == NULL)
	{
		return;	// no data to draw
	}
	dOutputMatrix = m_pOutputBlk->GetOutputMatrix();
	m_pOutputBlk->GetDataDimensions(nrows, ncols);
	//PrintMatrix(dOutputMatrix, nrows, ncols);

	// -- DETERMINE MAX/MIN DATA VALS 
	valid = DetermineMaxMinValues(dOutputMatrix, nrows, ncols, max, min);
	if(valid == 0)			// invalid data: hence nothing to graph
	{
		//sMsg.Format("\n COutputBlockView::OnDraw(), no data to graph\n\n");
		//AfxMessageBox(sMsg, nType, nIDhelp);
		return;
	}
	//sMsg.Format("\n COutputBlockView::OnDraw(), min = %lf, max = %lf\n\n", min, max);
	//AfxMessageBox(sMsg, nType, nIDhelp);
	
	// -- DETERMINE WINDOW DIMENSIONS
	GetClientRect(&rect);
	wnd_width = (rect.right - rect.left);	// rect.left = 0
	wnd_height = (rect.bottom - rect.top);	// rect.top = 0
	//sMsg.Format("\n COutputBlockView::OnDraw(), wnd_width = %d, wnd_height = %d\n\n", wnd_width, wnd_height);
	//AfxMessageBox(sMsg, nType, nIDhelp);
	
	// -- DRAW GRAPH
	DrawGraph(pDC, wnd_width, wnd_height, dOutputMatrix, nrows, ncols, max, min);
	// DiagramEng (end)
}

/////////////////////////////////////////////////////////////////////////////
// COutputBlockView diagnostics

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

void COutputBlockView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

// DiagramEng (start)
CDiagramEngDoc* COutputBlockView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDiagramEngDoc)));
	return (CDiagramEngDoc*)m_pDocument;
}
// DiagramEng (end)

#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// COutputBlockView message handlers

void COutputBlockView::SetRefOutputBlock(COutputBlock *ref_blk)
{
	m_pOutputBlk = ref_blk;
}

int COutputBlockView::DetermineMaxMinValues(double **matrix, int nrows, int ncols, double &max, double &min)
{
	int i;
	int j;
	double dbl_max = 1.7976931348623157e+308;	// max floating point limit
	double dbl_min = 2.2250738585072014e-308;	// min floating point limit
	
	// Check that the matrix has values in it, if not, return
	if(matrix == NULL)
	{
		min = dbl_min;	// intentionally noticeably small
		max = dbl_max;	// intentionally noticeably large
		return 0;		// 0 denotes invalid op.
	}

	// Set max and min vals
	min = matrix[0][0];
	max = matrix[0][0];

	// Loop through data matrix and determine max min values.
	for(i=0; i<nrows; i++)
	{
		for(j=0; j<ncols; j++)
		{
			// Min
			if(matrix[i][j] < min)
			{
				min = matrix[i][j];
			}

			// Max
			if(matrix[i][j] > max)
			{
				max = matrix[i][j];
			}
		}
	}

	return 1;	// 1 denotes valid op.
}

void COutputBlockView::DrawGraph(CDC *pDC, LONG wnd_width, LONG wnd_height, double **dOutputMatrix, int nrows, int ncols, double f_max, double f_min)
{
	// Note: pDC is a ptr to the device context of "Class Device Context" (CDC)
	int i;
	int j;
	int border = 100;			// graph box border distance from wnd edga
	int box_width;				// width of bounding box
	int box_height;				// height of bounding box
	int col;					// column no. of dOutputMatrix
	int color_cnt = 0;			// color counter to select curve color.
	int n_data_pts;				// no. of data points per graph
	int n_horiz_lines = 10;		// no. of horizontal lines of grid
	int n_signals_per_row;		// no. of signals per row of dOutputMatrix
	int n_TimeSteps;			// no. of simulation time steps
	int n_vertical_lines = 10;	// no. of vertical lines of grid
	int notation;				// notation variable: (0) std notation, (1) sci notation.
	int pen_width = 1;			// pen width
	int pen_color = RGB(0,0,0);	// red = RGB(255,0,0), green = RGB(0,255,0), blue = RGB(0,0,255), Black = RGB(0,0,0), White = (255,255,255)
	int sig;					// signal no.
	int time_pt_display;		// time point display variable: (0) show time points, (1) hide time points
	double data_range;			// range of data values, i.e., max - min
	double delta_t;				// time step size
	double dot_radius = 3.0;	// radius of graph curve dot
	double f;					// f = dOutputMatri[.][.], i.e. an element of the Output matrix
	double inter_pt_dist;		// distance bw. data points in x-direc.
	double t;					// time value
	double t_init;				// initial time
	double t_final;				// final time
	CPoint box_top_left;		// top left cnr. of graph-bounding box
	CPoint box_top_right;		// top right cnr. of graph-bounding box
	CPoint box_bottom_left;		// bottom left cnr. of graph-bounding box
	CPoint box_bottom_right;	// bottom right cnr. of graph-bounding box
	CPoint data_pt;				// data point
	CPoint dot_bottom_right;	// graph curve dot bounding box coord (bottom right)
	CPoint dot_top_left;		// graph curve dot bounding box coord (top left)
	CPoint text_pt(0,0);		// text posn
	CString	sMsg;				// string to be displayed
	CString str_label;			// axes string label
	UINT nType = MB_OK;			// style of msg. box
	UINT nIDhelp = 0;			// help context ID for the msg.

	// CREATE A PEN
	pen_color = RGB(190, 190, 190);
	CPen lpen(PS_SOLID, pen_width, pen_color);

	// SET THE NEW PEN AS THE DRAWING OBJ.
	CPen *pOldPen = pDC->SelectObject(&lpen);

	// SET RECTANGLE POINTS
	box_top_left.x = border;
	box_top_left.y = border;
	box_top_right.x = wnd_width - border;
	box_top_right.y = border;
	box_bottom_left.x = box_top_left.x;
	box_bottom_left.y = wnd_height - border;
	box_bottom_right.x = box_top_right.x;
	box_bottom_right.y = box_bottom_left.y;
	
	// DRAW RECTANGLE
	pDC->MoveTo(box_top_left);
	pDC->LineTo(box_top_right);
	pDC->LineTo(box_bottom_right);
	pDC->LineTo(box_bottom_left);
	pDC->LineTo(box_top_left);

	// DATA RANGE
	data_range = fabs(f_max - f_min);

	// NUMBER OF TIME STEPS AND DATA POINTS
	CDiagramEngDoc *pDoc = GetDocument();	// COutputBlockView::GetDocument()
	n_TimeSteps = pDoc->GetSystemModel().DetermineNTimeSteps();
	n_data_pts = n_TimeSteps + 1;

	// BOUNDING BOX VARS
	box_width = box_top_right.x - box_top_left.x;
	box_height = box_bottom_left.y - box_top_left.y;
	inter_pt_dist = (double)box_width/(double)n_TimeSteps;

	// DRAW BOX VERTICAL GRID LINES
	for(i=0; i<n_vertical_lines; i++)
	{
		data_pt.x = box_bottom_left.x + i*((double)box_width/(double)n_vertical_lines);
		data_pt.y = box_bottom_left.y;
		pDC->MoveTo(data_pt);
		data_pt.y = box_top_left.y;
		pDC->LineTo(data_pt);
	}

	// DRAW BOX HORIZONTAL GRID LINES
	for(i=0; i<n_horiz_lines; i++)
	{
		data_pt.x = box_top_left.x;	
		data_pt.y = box_top_left.y + i*((double)box_height/(double)n_horiz_lines);
		pDC->MoveTo(data_pt);
		data_pt.x = box_top_right.x;	
		pDC->LineTo(data_pt);
	}

	// NUMBER OF INDEP CURVES PER ROW OF DATA
	n_signals_per_row = ncols/n_data_pts;	// e.g. F(t) = [ [f_1_1, f_1_2], [f_2_1, f_2_2], ... [f_n_1, f_n_2] ], n_signals_per_row = 2.	

	// DRAW CURVES
	time_pt_display = m_pOutputBlk->GetTimePtDisplay();	// get time point display value
	for(j=0; j<nrows; j++)							// for each row of dOutputMatrix
	{	
		for(sig=0; sig<n_signals_per_row; sig++)	// for one or more signals per row
		{	
			// CREATE AND SELECT A NEW PEN
			pen_color = SelectGraphCurveColor(color_cnt);
			color_cnt++;

			// Create a pen
			CPen lnew_pen(PS_SOLID, pen_width, pen_color);

			// Set the new pen as the drawing obj.
			pDC->SelectObject(&lnew_pen);

			// FOR EACH DATA/TIME POINT OF A SIGNAL
			for(i=0; i<n_data_pts; i++)			
			{
				data_pt.x = box_bottom_left.x + i*inter_pt_dist;	// x location of pt.
				col = sig + i*n_signals_per_row;					// col of dOutputMatrix
				f = dOutputMatrix[j][col];							// f(.) value stored in dOutputMatrix
				data_pt.y = box_bottom_left.y - int((fabs(f - f_min)/data_range)*box_height);	// y location of pt.	

				//sMsg.Format("\n j= %d, sig = %d, i = %d, f = %lf, data_pt.x = %d, data_pt.y = %d\n", j, sig, i, f, data_pt.x, data_pt.y);
				//AfxMessageBox(sMsg, nType, nIDhelp);

				// Draw dot/circle about a pt
				dot_top_left.x = (int)(data_pt.x - dot_radius);
				dot_top_left.y = (int)(data_pt.y - dot_radius);
				dot_bottom_right.x = (int)(data_pt.x + dot_radius);
				dot_bottom_right.y = (int)(data_pt.y + dot_radius);

				if(time_pt_display == 0)			
				{
					pDC->Ellipse(dot_top_left.x, dot_top_left.y, dot_bottom_right.x, dot_bottom_right.y);
				}

				// Draw line connecting pts
				if(i == 0)
				{
					pDC->MoveTo(data_pt);	
				}
				else	
				{
					pDC->LineTo(data_pt);
				}
			}
		}	
	}
	
	// X AXIS LABELS
	notation = m_pOutputBlk->GetNotation();								// get notation type
	pDoc->GetSystemModel().GetTimeParameters(t_init, t_final, delta_t);	// get time parameters

	for(i=0; i<=n_vertical_lines; i++)
	{
		text_pt.x = box_bottom_left.x + i*((double)box_width/(double)n_vertical_lines) - 0.1*border;
		text_pt.y = box_bottom_left.y + 0.1*border;
		t = t_init + ((double)i/(double)n_vertical_lines)*(t_final - t_init);

		if(notation == 0)
		{
			str_label.Format(" %lf", t);
		}
		else
		{
			str_label.Format(" %-5.3e", t);
		}
		pDC->SetTextColor(RGB(0,0,255));
		pDC->TextOut(text_pt.x, text_pt.y, str_label);
	}
	text_pt.x = box_bottom_left.x + 0.5*box_width;
	text_pt.y = box_bottom_left.y + 0.475*border;
	str_label.Format(" t (s)");
	pDC->SetTextColor(RGB(0,0,0));
	pDC->TextOut(text_pt.x, text_pt.y, str_label);

	// Y AXIS LABELS
	for(i=0; i<=n_horiz_lines; i++)
	{
		text_pt.x = box_bottom_left.x - 0.85*border;
		text_pt.y = box_bottom_left.y - i*((double)box_height/(double)n_horiz_lines) - 0.075*border;
		f = f_min + ((double)i/(double)n_horiz_lines)*data_range;

		if(notation == 0)
		{
			str_label.Format(" %lf", f);
		}
		else
		{
			str_label.Format(" %5.3e", f);
		}
		pDC->SetTextColor(RGB(0,0,255));
		pDC->TextOut(text_pt.x, text_pt.y, str_label);
	}
	text_pt.x = box_bottom_left.x - 0.95*border;
	text_pt.y = box_bottom_left.y - 0.465*box_height;
	str_label.Format(" f(t)");
	pDC->SetTextColor(RGB(0,0,0));
	pDC->TextOut(text_pt.x, text_pt.y, str_label);

	// Graph title
	text_pt.x = box_bottom_left.x + 0.4*box_width;
	text_pt.y = box_top_left.y - 0.5*border;
	str_label.Format(" Plot of f(t) vs. t");
	pDC->TextOut(text_pt.x, text_pt.y, str_label);
	
	// RESET THE PREV. PEN
	pDC->SelectObject(pOldPen);
}

int COutputBlockView::SelectGraphCurveColor(int k)
{
	int blue;				// blue color cmpt.
	int green;				// green color cmpt.
	int pen_color;			// red = RGB(255,0,0), green = RGB(0,255,0), blue = RGB(0,0,255), Black = RGB(0,0,0), White = (255,255,255)
	int random_no;			// integer random number generated
	int red;				// red color cmpt.
	double uniformly_rand;	// uniformly random number

	// Switch upon integer	
	switch(k)
	{
	case 0:	
		// seed the random number generator with time value to get truly random numbers: done for the default case.
		srand((unsigned)time(NULL));

		pen_color = RGB(0,0,0);			// black
		break;
	case 1:	
		pen_color = RGB(0,0,255);		// blue
		break;
	case 2:	
		pen_color = RGB(255,0,0);		// red
		break;
	case 3:	
		pen_color = RGB(0,255,0);		// green
		break;
	case 4:	
		pen_color = RGB(148,0,211);		// dark violet
		break;
	case 5:	
		pen_color = RGB(255,140,0);		// dark orange
		break;
	case 6:	
		pen_color = RGB(139,69,19);		// dark saddle brown
		break;
	case 7:	
		pen_color = RGB(255,20,147);	// deep pink
		break;
	case 8:	
		pen_color = RGB(49,79,79);		// dark slate grey
		break;
	case 9:	
		pen_color = RGB(119,136,153);	// light slate grey
		break;
	default:							// assign a color at random
		random_no = rand();
		uniformly_rand = double(random_no)/double(RAND_MAX);
		red = int(uniformly_rand*255);

		random_no = rand();
		uniformly_rand = double(random_no)/double(RAND_MAX);
		green = int(uniformly_rand*255);

		random_no = rand();
		uniformly_rand = double(random_no)/double(RAND_MAX);
		blue = int(uniformly_rand*255);

		pen_color = RGB(red, green, blue);

		break;
	}// end switch

	return pen_color;
}

COutputBlock* COutputBlockView::GetRefOutputBlock()
{
	return m_pOutputBlk;
}
