// Line2DGL.cpp: implementation of the CPGLLine2D class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "PGL/PGLLine2D.h"
#include "PGL/PGLLine2DPropPage.h"

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

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

IMPLEMENT_SERIAL(CPGLLine2D, CPGLLine,1);

void CPGLLine2D::Serialize(CArchive &archive)
{
    int i;
    // call base class function first
    // base class is CObject in this case
    CPGLLine::Serialize( archive );

    // now do the stuff for our specific class
    if( archive.IsStoring() )
    {
        archive<< m_bFilled;
        archive << m_iNPoints;
        for (i=0;i<2;i++)
        {
            archive <<m_spHeadShow[i] << m_spHeadThick[i] << m_spHeadLength[i];
        }
        for (i=0;i<m_iNPoints;i++)
        {
            archive << m_pX[i] << m_pY[i];
        }
    }
    else
    {
        archive>> m_bFilled;
        archive >>    m_iNPoints;
        for (i=0;i<2;i++)
        {
            archive >>m_spHeadShow[i] >> m_spHeadThick[i] >> m_spHeadLength[i];
        }

        // reallocation memory
        if (m_pX)
            delete[] m_pX; 
        if (m_pY)
            delete[] m_pY; 
        m_pX=new double[m_iNPoints];
        m_pY=new double[m_iNPoints];

        for (i=0;i<m_iNPoints;i++)
        {
            archive >> m_pX[i] >> m_pY[i];
        }
    }
}

#ifdef _DEBUG
void CPGLLine2D::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLLine::Dump( dc );

    // now do the stuff for our specific class
    dc << _T("CPGLLine2D ID ") << GetID() << endl;
    dc << _T("nb points: ") << m_iNPoints << endl;
}
void CPGLLine2D::AssertValid() const
{
    // call inherited AssertValid first
    CPGLLine::AssertValid();

    // check members...
    ASSERT(m_iNPoints>0);

} 
#endif


CPGLLine2D::CPGLLine2D()
: CPGLLine()
{
    m_spHeadShow[0]=m_spHeadShow[1]=FALSE;
    m_spHeadThick[0]=m_spHeadThick[1]=12;
    m_spHeadLength[0]=m_spHeadLength[1]=16;
    m_iNPoints=0;
    m_pX=NULL;
    m_pY=NULL;
    m_bFilled=FALSE;

    LoadBitmap(IDB_PGL_LINE2D_BITMAP);
}

CPGLLine2D::CPGLLine2D(const CPGLLine2D &l)
: CPGLLine(l)
{
    int i;

    m_bFilled = l.m_bFilled;
    m_iNPoints=l.m_iNPoints;
    for (i=0;i<2;i++)
    {
        m_spHeadShow[i]=l.m_spHeadShow[i];
        m_spHeadThick[i]=l.m_spHeadThick[i];
        m_spHeadLength[i]=l.m_spHeadLength[i];
    }

    m_pX=new double[m_iNPoints];
    m_pY=new double[m_iNPoints];

    for (i=0;i<m_iNPoints;i++)
    {
        m_pX[i]=l.m_pX[i];
        m_pY[i]=l.m_pY[i];
    }    

    LoadBitmap(IDB_PGL_LINE2D_BITMAP);
}

CPGLLine2D& CPGLLine2D::operator = (const CPGLLine2D& l)
{
    int i;

    // prevent self copy
    if (&l != this)
    {
        // invoke CPGLLine copy assignement operator
        this->CPGLLine::operator=(l);

        for (i=0;i<2;i++)
        {
            m_spHeadShow[i]=l.m_spHeadShow[i];
            m_spHeadThick[i]=l.m_spHeadThick[i];
            m_spHeadLength[i]=l.m_spHeadLength[i];
        }

        m_bFilled = l.m_bFilled;
        // copy the operand
        if (m_iNPoints!=l.m_iNPoints)
        {
            // reallocation memory
            m_iNPoints=l.m_iNPoints;
            if (m_pX)
                delete[] m_pX; 
            m_pX=new double[m_iNPoints];
            if (m_pY)
                delete[] m_pY; 
            m_pY=new double[m_iNPoints];
        }
        for (i=0;i<m_iNPoints;i++)
        {
            m_pX[i]=l.m_pX[i];
            m_pY[i]=l.m_pY[i];
        }    
    }
    return *this;
}

CPGLLine2D::~CPGLLine2D() 
{
    if (m_pX)
        delete[] m_pX; 
    if (m_pY)
        delete[] m_pY;
};

void CPGLLine2D::AddContextMenuItems(CMenu* pMenu)
{
    ASSERT_VALID(pMenu);
    // first call base class function
    CPGLLine::AddContextMenuItems(pMenu);

    // add separator
    // add own entries...
//    pMenu->AppendMenu(MF_ENABLED | MF_STRING, ,"Test Line2D");    
}

void CPGLLine2D::AddPropertyPage(CPropertySheet* pPropSheet)
{
    ASSERT_VALID(pPropSheet);
    // call own functions
    CPGLLine2DPropPage* propPage=new CPGLLine2DPropPage(this);
    pPropSheet->AddPage(propPage);

    // first call base class function
    CPGLLine::AddPropertyPage(pPropSheet);
}

void CPGLLine2D::SetDatas(int _nPoints, double *_x, double *_y)
{
    // cleaning old datas.
    if (m_pX)
        delete[] m_pX;
    if (m_pY)
        delete[] m_pY;
    // storing pointers...
    m_iNPoints=_nPoints;
    m_pX=_x;
    m_pY=_y;

    PostUpdateExtent();
}

void CPGLLine2D::SetDatas(const std::vector<double>& vx, const std::vector<double>& vy)
{
    // cleaning old datas.
    if (m_pX)
    {
        delete[] m_pX;
        m_pX=NULL;
    }
    if (m_pY)
    {
        delete[] m_pY;
        m_pY=NULL;
    }

    // storing pointers...
    m_iNPoints=__min(vx.size(),vy.size());
    m_pX=new double [m_iNPoints];
    m_pY=new double [m_iNPoints];
    for (UINT i=0;i<m_iNPoints;i++)
    {
        m_pX[i]=vx[i];
        m_pY[i]=vy[i];
    }

    PostUpdateExtent();
}

double* CPGLLine2D::GetExtent(CPGLView* pView)
{
    if (NeedUpdateExtent())
        UpdateExtent(pView);

    return CPGLLine::GetExtent(pView);
}

void CPGLLine2D::UpdateExtent(CPGLView* pView)
{
    // Calling base class function
    CPGLLine::UpdateExtent(pView);

    int i;
    // init extent :
    if (m_iNPoints>0)
    {
        m_extent[0]=m_extent[1]=m_pX[0];
        m_extent[2]=m_extent[3]=m_pY[0];
    }

    for (i=1;i<m_iNPoints;i++)
    {
        // testing left and right
        m_extent[0]=__min(m_extent[0],m_pX[i]);
        m_extent[1]=__max(m_extent[1],m_pX[i]);
        // testing bottom and up
        m_extent[2]=__min(m_extent[2],m_pY[i]);
        m_extent[3]=__max(m_extent[3],m_pY[i]);
    }
}

void CPGLLine2D::PlotLineStripGfx(gfxinterface::CGfxInterface& gfx)
{
    if (m_bFilled)
    {
        gfx.PushState();
        gfx.SetFillColor(GetColor().GetRed(), GetColor().GetGreen(), GetColor().GetBlue(), GetColor().GetAlpha());
        gfx.SetColor(0,0,0);
        switch (GetInterpolationType())
        {
        case PGL_INTERPOLATION_STEP:
            gfx.DrawStepStrip(m_iNPoints,m_pX,m_pY, false, true);
            break;
        case PGL_INTERPOLATION_LINEAR:
            gfx.DrawLineStrip(m_iNPoints,m_pX,m_pY, false, true);
            break;
        case PGL_INTERPOLATION_SEGMENT:
            gfx.DrawMultipleLineStrip(m_iNPoints,m_iStripSize,m_pX,m_pY, false, true);
            break;
        }
        gfx.PopState();
    }
    else
    {
        switch (GetInterpolationType())
        {
        case PGL_INTERPOLATION_STEP:
            gfx.DrawStepStrip(m_iNPoints,m_pX,m_pY);
            break;
        case PGL_INTERPOLATION_LINEAR:
            gfx.DrawLineStrip(m_iNPoints,m_pX,m_pY);
            break;
        case PGL_INTERPOLATION_SEGMENT:
            gfx.DrawMultipleLineStrip(m_iNPoints,m_iStripSize, m_pX, m_pY);
            break;
        }
    }
}

void CPGLLine2D::PlotPointStripGfx(gfxinterface::CGfxInterface& gfx)
{
    // choose type of interpolation between points
    gfx.SetFillColor(GetColor().GetRed(), GetColor().GetGreen(), GetColor().GetBlue(), GetColor().GetAlpha());
    switch(GetPointType())
    {
    case PGL_POINT_SIMPLE:
        gfx.DrawCircleStrip(m_iNPoints, m_pX, m_pY, GetPointWidth(), true);
        break;
    case PGL_POINT_CONTOUR:
        gfx.DrawCircleStrip(m_iNPoints, m_pX, m_pY, GetPointWidth());
        break;
    case PGL_POINT_TRIANGLE:
        gfx.DrawTriangleStrip(m_iNPoints, m_pX, m_pY, GetPointWidth());
        break;
    case PGL_POINT_SQUARE:
        gfx.DrawSquareStrip(m_iNPoints, m_pX, m_pY, GetPointWidth());
        break;
    }
}


void CPGLLine2D::PlotArrowsGfx(gfxinterface::CGfxInterface& gfx)
{
    double x,y,vx,vy,norm;
    int i;

    // drawing arrows if needed
    if (GetNPoints()<=1)
        return;

    for (i=0;i<2;i++)
    {
        // start arrow
        if (!m_spHeadShow[i])
            continue;

        if (i==0)
        {
            x=GetXi(0);
            y=GetYi(0);
            vx=GetXi(0)-GetXi(1);
            vy=GetYi(0)-GetYi(1);
        }
        else
        {
            x=GetXi(GetNPoints()-1);
            y=GetYi(GetNPoints()-1);
            vx=GetXi(GetNPoints()-1)-GetXi(GetNPoints()-2);
            vy=GetYi(GetNPoints()-1)-GetYi(GetNPoints()-2);
        }

        norm=sqrt(vx*vx+vy*vy);
        if (norm <PGL_EPS)
            continue;

        vx*=m_spHeadLength[i]/(norm);
        vy*=m_spHeadLength[i]/(norm);
        // starting path
        // sending points to eps
        gfx.DrawArrowAbs(x, y, vx, vy, GetLineWidth()-1.0, m_spHeadThick[i], m_spHeadLength[i]);
    }
}