/******************************************************************************
**
** Authors : Satish Hota
** Date    : 21-Nov-01
** Filename: MFCDevConHelper.cpp
** Purpose : Implementation of the CMFCDevConHelper class
**
** History:
** 21-Nov-01 Satish Hota
**     File Created
** 25-Nov-01 Satish Hota
**     * Added pens and brushes as member variables that are created on first use
**       and are destroyed when the destructor is called.
**     * Calling invalidate on the view only if it is valid in PostUpdate.
** 26-Nov-01 Satish Hota
**     Added new text methods GetTextExtents and TextOut.  Added a font to the
**     objects created.  Changed pen style to SOLID.
** 26-Nov-01 Edwin Chan
**     Changed m_pOldBrush to use WHITE_BRUSH as default
** 30-Nov-01 Satish Hota
**     Added a dashed (dotted) pen.  Changed the interface of most of the Draw
**     methods to accept pen-type rather than a bSelected flag.  Implemented
**     display of arrow heads where specified.
** 02-Dec-01 Satish Hota
**     Deleting points array in DrawLine.
******************************************************************************/

#include "stdafx.h"
#include "ConGraT.h"
#include "MFCDevConHelper.h"
#include <math.h>

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

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CMFCDevConHelper::CMFCDevConHelper(CView* pView, CDC* pDC)
{
	// Define solid pen
	m_LogPen.lopnStyle = PS_SOLID;
	m_LogPen.lopnWidth.x = 1;
	m_LogPen.lopnWidth.y = 1;
	m_LogPen.lopnColor = RGB(0, 0, 0);

	// Define dotted pen
	m_LogDashedPen.lopnStyle = PS_DOT;
	m_LogDashedPen.lopnWidth.x = 1;
	m_LogDashedPen.lopnWidth.y = 1;
	m_LogDashedPen.lopnColor = RGB(0, 0, 0);

	// Define thick pen
	m_LogThickPen.lopnStyle = PS_SOLID;
	m_LogThickPen.lopnWidth.x = 3;
	m_LogThickPen.lopnWidth.y = 3;
	m_LogThickPen.lopnColor = RGB(0, 0, 0);

	// Define black brush
    m_LogBrush.lbStyle = BS_SOLID;
	m_LogBrush.lbColor = RGB(0, 0, 0);
	m_LogBrush.lbHatch = HS_HORIZONTAL;

    m_pPen = NULL;
    m_pThickPen = NULL;
    m_pDashedPen = NULL;
    m_pBrush = NULL;

    m_pView = pView;
    m_pDC = pDC;
}

CMFCDevConHelper::~CMFCDevConHelper()
{
    if(m_pPen)
    {
        delete m_pPen;
    }

    if(m_pThickPen)
    {
        delete m_pThickPen;
    }

    if(m_pDashedPen)
    {
        delete m_pDashedPen;
    }

    if(m_pBrush)
    {
        delete m_pBrush;
    }
}

bool CMFCDevConHelper::SwitchDrawingObjects(PenType ePenType, LineEndType eLineEndType)
{
	if(!m_pPen)
    {
        m_pPen = new CPen;
        if(!m_pPen->CreatePenIndirect(&m_LogPen))
            return false;
    }

	if(!m_pThickPen)
    {
        m_pThickPen = new CPen;
        if(!m_pThickPen->CreatePenIndirect(&m_LogThickPen))
            return false;
    }

	if(!m_pDashedPen)
    {
        m_pDashedPen = new CPen;
        if(!m_pDashedPen->CreatePenIndirect(&m_LogDashedPen))
            return false;
    }

	if(!m_pBrush)
    {
        m_pBrush = new CBrush;
        if(!m_pBrush->CreateBrushIndirect(&m_LogBrush))
            return false;
    }

    if(ePenType == CMFCDevConHelper::PenType_Solid)
    {
	    m_pOldPen = m_pDC->SelectObject(m_pPen);
    }
    else if(ePenType == CMFCDevConHelper::PenType_Thick)
    {
	    m_pOldPen = m_pDC->SelectObject(m_pThickPen);
    }
    else if(ePenType == CMFCDevConHelper::PenType_Dashed)
    {
	    m_pOldPen = m_pDC->SelectObject(m_pDashedPen);
    }

	m_pOldBrush = (CBrush*)m_pDC->SelectStockObject(WHITE_BRUSH);
	//m_pOldBrush = m_pDC->SelectObject(&brush);

    m_pOldFont = (CFont*)m_pDC->SelectStockObject(SYSTEM_FONT);

    return true;
}

bool CMFCDevConHelper::RestoreDrawingObjects()
{
	m_pDC->SelectObject(m_pOldBrush);
	m_pDC->SelectObject(m_pOldPen);
    m_pDC->SelectObject(m_pOldFont);

    m_pOldBrush = NULL;
    m_pOldPen = NULL;
    m_pOldFont = NULL;

    return true;
}

long CMFCDevConHelper::DrawRectangle(int nLeft, int nTop, 
                                     int nRight, int nBottom, 
                                     PenType ePenType)
{
    if(!SwitchDrawingObjects(ePenType, LineEndType_Default))
		return CG_E_FAIL;

    m_pDC->Rectangle(CRect(nLeft, nTop, nRight, nBottom));

    RestoreDrawingObjects();

    return CG_S_OK;

}


long CMFCDevConHelper::DrawRoundedRect(int nLeft, int nTop, 
								   int nRight, int nBottom, 
								   int nRoundX, int nRoundY,
								   PenType ePenType)
{
    if(!SwitchDrawingObjects(ePenType, LineEndType_Default))
		return CG_E_FAIL;

    m_pDC->RoundRect(CRect(nLeft, nTop, nRight, nBottom), CPoint(nRoundX, nRoundY));

    RestoreDrawingObjects();

    return CG_S_OK;

}

long CMFCDevConHelper::DrawLine(int nFromX, int nFromY, 
                                int nToX, int nToY, 
                                PenType ePenType,
                                LineEndType eLineEndType)
{
    if(!SwitchDrawingObjects(ePenType, eLineEndType))
		return CG_E_FAIL;

	m_pDC->MoveTo(nFromX, nFromY);
	m_pDC->LineTo(nToX, nToY);

    CBrush *pBrush;
	CPen   *pPen;
    int    nRadius = 5;

	// Select black brush
    pBrush= (CBrush*)m_pDC->SelectStockObject(BLACK_BRUSH);
	pPen = m_pDC->SelectObject(m_pPen);

    // Draw arrow heads as required 
    int nArrowLen = 10, nHalfArrowWidth = 3;
    if(eLineEndType & LineEndType_StartArrow)
    {
        int nDeltaX = nToX - nFromX;
        int nDeltaY = nToY - nFromY;
        
        if(nDeltaX != 0 || nDeltaY != 0)
        {
        
            double dLen = sqrt(nDeltaX*nDeltaX + nDeltaY * nDeltaY);
            double dUnitVectorX = nDeltaX/dLen;
            double dUnitVectorY = nDeltaY/dLen;

            int x1 = nFromX + nArrowLen*dUnitVectorX;
            int y1 = nFromY + nArrowLen*dUnitVectorY;

            double dNormalVectorX, dNormalVectorY;
            // Get Normal vector
            dNormalVectorX = dUnitVectorY;
            dNormalVectorY = -dUnitVectorX;

            int x2 = x1 + nHalfArrowWidth*dNormalVectorX;
            int y2 = y1 + nHalfArrowWidth*dNormalVectorY;
            int x3 = x1 - nHalfArrowWidth*dNormalVectorX;
            int y3 = y1 - nHalfArrowWidth*dNormalVectorY;

            CPoint* pPointsArray = new CPoint[3];

            pPointsArray[0].x = nFromX;
            pPointsArray[0].y = nFromY;
            pPointsArray[1].x = x2;
            pPointsArray[1].y = y2;
            pPointsArray[2].x = x3;
            pPointsArray[2].y = y3;

            m_pDC->Polygon(pPointsArray, 3);

			delete pPointsArray;
        }
    }
    if(eLineEndType & LineEndType_EndArrow)
    {
        int nDeltaX = nFromX - nToX;
        int nDeltaY = nFromY - nToY;

        if(nDeltaX != 0 || nDeltaY != 0)
        {
            double dLen = sqrt(nDeltaX*nDeltaX + nDeltaY * nDeltaY);
            double dUnitVectorX = nDeltaX/dLen;
            double dUnitVectorY = nDeltaY/dLen;

            int x1 = nToX + nArrowLen*dUnitVectorX;
            int y1 = nToY + nArrowLen*dUnitVectorY;

            double dNormalVectorX, dNormalVectorY;
            // Get Normal vector
            dNormalVectorX = dUnitVectorY;
            dNormalVectorY = -dUnitVectorX;

            int x2 = x1 + nHalfArrowWidth*dNormalVectorX;
            int y2 = y1 + nHalfArrowWidth*dNormalVectorY;
            int x3 = x1 - nHalfArrowWidth*dNormalVectorX;
            int y3 = y1 - nHalfArrowWidth*dNormalVectorY;

            CPoint* pPointsArray = new CPoint[3];

            pPointsArray[0].x = nToX;
            pPointsArray[0].y = nToY;
            pPointsArray[1].x = x2;
            pPointsArray[1].y = y2;
            pPointsArray[2].x = x3;
            pPointsArray[2].y = y3;

            m_pDC->Polygon(pPointsArray, 3);
			delete pPointsArray;
        }
    }

    // Restore the brush
    m_pDC->SelectObject(pBrush);
    m_pDC->SelectObject(pPen);

    RestoreDrawingObjects();

    return CG_S_OK;
}

void CMFCDevConHelper::PostUpdate()
{
    if(m_pView)
        m_pView->Invalidate(FALSE);
}

long CMFCDevConHelper::GetTextExtents(const std::string& strText, int *pnWidth, int *pnHeight)
{
    CSize size;
    CString cString;
    cString+=strText.c_str();
    size = m_pDC->GetTextExtent(cString);
    *pnWidth = size.cx;
    *pnHeight = size.cy;

    return CG_S_OK;
}

long CMFCDevConHelper::TextOut(int nXCoor, int nYCoor, const std::string& strText)
{
    m_pDC->TextOut(nXCoor, nYCoor, CString(strText.c_str()));
    return CG_S_OK;
}
