
//	Title:		Signal.cpp
//	Purpose:	Contains all Signal related code.
//	Authors:	IntEng gp.
//	Source:		C++ texts & IntEng gp.
//	Date:		28-8-2008 (start), 1-12-08 (updated)

#include "stdafx.h"		// rqd. for precompiled header info.
#include <fstream>		// rqd. for file i/o in WriteConnectionDataToFile()
#include <iostream>
#include <string.h>
#include <math.h>		// rqd. for math ops. in DrawArrowHead()
#include <vector>
#include <list>
using namespace std;
#include "Signal.h" 
#include "DiagramEngDoc.h"	// rqd. for usage of GetDocumentGlobalFn() returning a ptr-to-CDiagramEngDoc


CSignal::CSignal(void)
{
	// Init.
	m_iNrows = 0;						// no initial output signal data hence zero rows
	m_iNcols = 0;						// no initial output signal data hence zero cols
	m_iValidity = 0;					// initial invalid signal
	m_strSignalName = "signal_name";	// default place-holder name
	m_strOutputSignal = "";				// no initial output signal data hence empty string
	m_dSignalMatrix = NULL;				// no initial output signal data hence NULL matrix
}

CSignal::~CSignal(void)
{
	int i;

	// MEMORY DELETE
	if(m_dSignalMatrix != NULL)
	{
		for(i=0; i<m_iNrows; i++)
		{
			delete [] m_dSignalMatrix[i];
		}
		delete [] m_dSignalMatrix;
		m_dSignalMatrix = NULL;
	}
}

CString CSignal::GetSignalName(void)
{
	return m_strSignalName;
}

void CSignal::SetSignalName(CString name)
{
	m_strSignalName = name;
}

CConnection::CConnection(void)
{
	//AfxMessageBox("\n CConnection::CConnection()\n", MB_OK, 0);

	m_pRefFromPoint = NULL;		// not connected to a bend point initially
	m_pRefFromPort = NULL;		// from-port ref.
	m_pRefToPort = NULL;		// to-port ref.
	m_pSignal = new CSignal;	// contained ptr-to-CSignal init. to NULL.
}

CConnection::~CConnection(void)
{
	//AfxMessageBox("\n CConnection::~CConnection()\n", MB_OK, 0);

	// Delete the connection-held signal data
	if(m_pSignal != NULL)
	{
		delete m_pSignal;
		m_pSignal = NULL;
	}
}

CSignal *CConnection::GetSignal(void)
{
	//AfxMessageBox("\n CConnection::GetSignal()\n", MB_OK, 0);

	return m_pSignal;	// return private member var m_pSignal of type ptr-to-CSignal
}

CConnection::CConnection(CPoint ptFrom, CPoint ptTo)
{
	// Init. member vars.
	m_ptTail = ptFrom;			// pt from which connection is drawn
	m_ptHead = ptTo;			// pt to which connection is drawn
	m_pRefFromPoint = NULL;		// not connected to a bend point initially
	m_pRefFromPort = NULL;		// from-port ref.
	m_pRefToPort = NULL;		// to-port ref.
	m_pSignal = new CSignal;	// contained ptr-to-CSignal 
}

void CConnection::DrawConnection(CDC *pDC)
{
	double dDeltaLength;			// std default reference delta length
	double length;					// length of side of rectangle bounding ellipse
	CPoint bend_pt;					// temp pt. to hold bend pt.
	CPoint bottom_right;			// bottom right cnr. coord. of rectangle bounding ellipse
	CPoint final_pt = m_ptTail;		// init as tail pt. in case there are no bend pts.
	CPoint top_left;				// top left cnr. coord. of rectangle bounding ellipse
	list<CPoint>::iterator it_pt;

	// Set length
	dDeltaLength = GetDocumentGlobalFn()->GetDeltaLength();
	length = 0.2*dDeltaLength;

	// Move to the tail pt.
	pDC->MoveTo(m_ptTail);

	// Iterate through the bend pts. drawing lines bw. them.
	for(it_pt = m_lstBendPoints.begin(); it_pt != m_lstBendPoints.end(); it_pt++)
	{
		// Draw a line to the bend pt.
		pDC->LineTo(*it_pt);

		// Set the current bend pt. as the pt prior to the head_pt: used for DrawArrowHead().
		final_pt = *it_pt;
	}
	
	// Line to head pt.
	pDC->LineTo(m_ptHead);

	// Iterate through the bend pts. drawing ellipses at each pt.
	for(it_pt = m_lstBendPoints.begin(); it_pt != m_lstBendPoints.end(); it_pt++)
	{
		// Draw an ellipse about the bend pt.
		bend_pt = *it_pt;
		top_left.x = (int)(bend_pt.x - length*0.5);
		top_left.y = (int)(bend_pt.y - length*0.5);
		bottom_right.x = (int)(bend_pt.x + length*0.5);
		bottom_right.y = (int)(bend_pt.y + length*0.5);
		pDC->Ellipse(top_left.x, top_left.y, bottom_right.x, bottom_right.y);	
	}

	// Draw arrowhead
	DrawArrowHead(pDC, final_pt, m_ptHead);
}

void CConnection::DrawArrowHead(CDC *pDC, CPoint tail, CPoint head)
{
	int pen_color;			// pen color: White = RGB(255,255,255), Black = RBF(0,0,0)
	double d;				// opp. side length of subtriangle with hypotenuse h and base side length length/2.
	double dDeltaLength;	// std ref delta length
	double h;				// hypotenuse of subtriangle with opp. side length d and base side length length/2.
	double length;			// a fraction of a std ref delta length
	double length_u;		// length of vector u
	double length_v;		// length of vector v
	double theta;			// angle of ith body rotated positively clockwise w.r.t. the global screen coord sys. X axis.
	double u[2];			// one of two vectors used to determined angle theta
	double v[2];			// one of two vectors used to determined angle theta: the unit vector [1,0]
	CBrush *pBrush = NULL;	// a ptr to brush
	CPoint A;				// vertex in anticlock direc from pointing vertex B of arrowhead
	CPoint B;				// vertex of arrowhead pointing in direc of arrow (m_ptHead)
	CPoint C;				// vertex in clock direc from pointing vertex B of arrowhead
	CPoint vertices[3];		// vertices array to hold triangle vertices
	CString sMsg;			// main msg string
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.

	// Set length
	dDeltaLength = GetDocumentGlobalFn()->GetDeltaLength();
	length = 0.15*dDeltaLength;

	// Assign lengths to arrowhead (triangle) paras.
	d = 0.2887*length;
	h = 0.5773*length;

	// Print msg.
	sMsg.Format("\n CConnection::DrawArrowHead(), d = %lf, h = %lf\n", d, h);
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// Length of vecs u and v
	v[0] = 1.0;						// x cmpt. of unit vector [1,0]
	v[1] = 0.0;						// y cmpt. of unit vector [1,0]
	u[0] = head.x - tail.x;	// length in x direc of connection vector
	u[1] = head.y - tail.y;	// length in y direc of connection vector

	length_u = sqrt(pow(u[0],2) + pow(u[1],2));
	length_v = sqrt(pow(v[0],2) + pow(v[1],2));

	// Angle between vecs u and v
	theta = acos((u[0]*v[0] + u[1]*v[1])/(length_u*length_v));
	if(u[1] < 0)
	{
		theta = -theta;	// negate theta if y-cmpt of u < 0, since acos result is an element from [0,pi] radians ONLY (non-neg).
	}

	// Global position vecs of arrowhead triangle vertices
	A.x = head.x + long(cos(theta)*(-(h+d)) - sin(theta)*(-length*0.5));
	A.y = head.y + long(sin(theta)*(-(h+d)) + cos(theta)*(-length*0.5));
	
	B.x = head.x;
	B.y = head.y;

	C.x = head.x + long(cos(theta)*(-(h+d)) - sin(theta)*(length*0.5));
	C.y = head.y + long(sin(theta)*(-(h+d)) + cos(theta)*(length*0.5));

	// -- EXTRACT PEN COLOR FROM PEN	
	CPen *lpen = pDC->GetCurrentPen();
	EXTLOGPEN elPen; 
	lpen->GetExtLogPen(&elPen);
	pen_color = elPen.elpColor;
	
	// Draw arrowhead
	pDC->MoveTo(A);
	pDC->LineTo(B);
	pDC->LineTo(C);
	pDC->LineTo(A);

	// Prepare for filling the polygon
	CBrush NewBrush(pen_color);		// create a new brush
	
	vertices[0] = A;
	vertices[1] = B;
	vertices[2] = C;

	// Select the new brush
	pBrush = pDC->SelectObject(&NewBrush);

	// Fill the polygon
	pDC->Polygon(vertices, 3);

	// Reset the prev. brush
	pDC->SelectObject(pBrush);
}

CPoint CConnection::GetConnectionPointTail() const
{
	// Return tail point.
	return m_ptTail;
}

CPoint CConnection::GetConnectionPointHead() const
{
	// Return head point.
	return m_ptHead;
}

void CConnection::SetConnectionPointTail(CPoint new_pt)
{
	// Assign the member var the new pt.
	m_ptTail = new_pt;
}

void CConnection::SetConnectionPointHead(CPoint new_pt)
{
	// Assign the member var the new pt.
	m_ptHead = new_pt;
}

CPort* CConnection::GetRefFromPort()
{
	return m_pRefFromPort;
}

CPort* CConnection::GetRefToPort()
{
	return m_pRefToPort;
}

void CConnection::SetRefFromPort(CPort *from_port)
{
	m_pRefFromPort = from_port;
}

void CConnection::SetRefToPort(CPort *to_port)
{
	m_pRefToPort = to_port;
}

list<CPoint>& CConnection::GetConnectionBendPointsList()
{
	// Return connection's bend points list
	return m_lstBendPoints;
}

CPoint* CConnection::GetRefFromPoint()
{
	return m_pRefFromPoint;
}

void CConnection::SetRefFromPoint(CPoint *from_pt)
{
	m_pRefFromPoint = from_pt;
}

void CSignal::ResetSignalData()
{
	int i;

	// MEMORY DELETE
	// Delete the arrays held at each of the entries of the column vector
	if(m_dSignalMatrix != NULL)
	{
		for(i=0; i<m_iNrows; i++)
		{
			delete [] m_dSignalMatrix[i];
		}
		delete [] m_dSignalMatrix;
	}
	
	// -- RESET VARS
	m_iNrows = 0;
	m_iNcols = 0;
	m_iValidity = 0;
	m_strOutputSignal = "";		// rqd as a result of introducing a COutputSignalDialog object into the project.
	m_dSignalMatrix = NULL;
	
	return;
}

double** CSignal::GetSignalData(int *nrows, int *ncols)
{
	// Addresses of nrows and ncols are passed, hence deref. with a ptr.
	
	// Assign CSignal data values
	*nrows = m_iNrows;
	*ncols = m_iNcols;

	// Return member signal matrix data
	return m_dSignalMatrix;
}

void CSignal::SetSignalData(double **matrix, int nrows, int ncols)
{
	int i;
	int j;

	// DELETE POSSIBLY EXISTING DATA
	// A resetting of signal data called upon a particular/single connection object 
	// may not reset secondary connection object data. Hence, delete data here for any signal to be certain.
	if(m_dSignalMatrix != NULL)
	{
		for(i=0; i<m_iNrows; i++)
		{
			delete [] m_dSignalMatrix[i];
		}
		delete [] m_dSignalMatrix;
	}

	// Set member vars
	m_iNrows = nrows;
	m_iNcols = ncols;

	// -- MEMORY NEW
	// Allocate an array of ptrs-to-double and return the & to m_dSignalMatrix
	// Note: an array is of type ptr, hence an array of ptrs is of type ptr-ptr.
	m_dSignalMatrix = new double *[nrows];	

	// Allocate an array of doubles for each of the entries (rows of the column vector) of m_dSignalMatrix, 
	// and return the & to m_dSignalMatrix[i].
	// Note: an array is of type ptr, and its & is stored at location i in the array m_dSignalMatrix[]
	for(i=0; i<nrows; i++)
	{
		m_dSignalMatrix[i] = new double[ncols];	// allocate an array of doubles, return & to matrix[i]
	}

	// -- ASSIGN MATRIX VALUES TO THE SIGNAL
	for(i=0; i<nrows; i++)
	{
		for(j=0; j<ncols; j++)
		{
			m_dSignalMatrix[i][j] = matrix[i][j];
		}
	}

	return;		
}

int CSignal::GetSignalValidity()
{	
	return m_iValidity;
}

void CSignal::SetSignalValidity(int valid)
{
	// Assign integer validity
	m_iValidity = valid;
}

void CSignal::GetSignalDataDimensions(int *nrows, int *ncols)
{
	// Addresses of nrows and ncols are passed, hence deref. with a ptr.

	// Assign CSignal data values
	*nrows = m_iNrows;
	*ncols = m_iNcols;
	
	return;	
}

CString CSignal::GetOutputSignalString(void)
{
	return m_strOutputSignal;
}

void CSignal::SetOutputSignalString(CString string)
{
	m_strOutputSignal = string;
}

void CConnection::WriteConnectionDataToFile(ofstream &fout)
{
	char strName[L_FILE_IO_STR];	// name of connection
	list<CPoint>::iterator it_pt;	// bend points list iterator			

	//AfxMessageBox("\n CConnection::WriteConnectionDataToFile()\n", MB_OK, 0);
	
	// Write CConnection member data to file.
	strcpy(strName, "connection");
	fout << strName << endl;
	fout << m_ptHead.x << " " << m_ptHead.y << endl;
	fout << m_ptTail.x << " " << m_ptTail.y << endl;
	fout << m_lstBendPoints.size() << endl;
	
	for(it_pt = m_lstBendPoints.begin(); it_pt != m_lstBendPoints.end(); it_pt++)
	{
		fout << (*it_pt).x << " " << (*it_pt).y << endl;
	}
}

void CConnection::ReadConnectionDataFromFile(ifstream &fin)
{
	int	i;
	int n_bend_pts;				// no. of bend points	
	char strVar[L_FILE_IO_STR];	// character array var to work with "fin >>"
	CPoint point;				// bend point
	
	//AfxMessageBox("\n CConnection::ReadConnectionDataFromFile()\n", MB_OK, 0);

	// Read CConnection member data from file
	fin >> strVar;			// connection string ID
	fin >> m_ptHead.x;		
	fin >> m_ptHead.y;
	fin >> m_ptTail.x;
	fin >> m_ptTail.y;
	fin >> n_bend_pts;

	for(i=0; i<n_bend_pts; i++)
	{
		fin >> point.x >> point.y;
		m_lstBendPoints.push_back(point);
	}
}

CConnection::CConnection(const CConnection &con)
{
	// NOTE - The copying action does not copy the pointer references.
	// Hence, SnapConnectionEndPointsAfterInitialConstruction() must be called to initialize the pointers (later).

	m_lstBendPoints = con.GetConnectionBendPointsListCopy();	// get a copy of the bend pts list (by val, not by ref.)
	m_ptHead = con.GetConnectionPointHead();					// pt to which connection is drawn
	m_ptTail = con.GetConnectionPointTail();					// pt from which connection is drawn
	m_pRefFromPoint = NULL;										// not connected to a bend point initially
	m_pRefFromPort = NULL;										// from-port ref.
	m_pRefToPort = NULL;										// to-port ref.
	m_pSignal = new CSignal;									// contained ptr-to-CSignal (no data) 
}

list<CPoint> CConnection::GetConnectionBendPointsListCopy() const
{
	// Return A COPY OF the connection's bend points list (i.e. not by reference)
	return m_lstBendPoints;
}
