#include <QDebug>

#include "xlinkobj.h"

#include "branchobj.h"
#include "branchitem.h"
#include "math.h"	// atan
#include "misc.h"	// max

/////////////////////////////////////////////////////////////////
// XLinkObj
/////////////////////////////////////////////////////////////////

int XLinkObj::arrowSize = 6;		    // make instances
int XLinkObj::clickBorder = 8;
int XLinkObj::pointRadius = 10;
int XLinkObj::d_control = 300;

XLinkObj::XLinkObj (QGraphicsItem* parent,Link *l):MapObj(parent)
{
    //qDebug()<< "Const XLinkObj (parent,Link)";
    link=l;
    init();
}



XLinkObj::~XLinkObj ()
{
    //qDebug() << "Destr XLinkObj";
    delete (poly);
    delete (path);
    delete (ctrl_p0);
    delete (ctrl_p1);
    delete (pointerEnd);
    delete (pointerBegin);
}


void XLinkObj::init () 
{
    visBranch = NULL;

    stateVis = Hidden;

    QPen pen = link->getPen();

    path = scene()->addPath (QPainterPath(), pen, Qt::NoBrush);	
    path->setZValue (dZ_XLINK);

    pointerBegin = new ArrowObj(this);
    pointerBegin->setPen( pen );
    pointerBegin->setUseFixedLength( true );
    pointerBegin->setFixedLength( 0 );

    pointerEnd = new ArrowObj(this);
    pointerEnd->setPen( pen );
    pointerEnd->setUseFixedLength( true );
    pointerEnd->setFixedLength( 0 );

    pen.setStyle (Qt::SolidLine);
    poly = scene()->addPolygon (QPolygonF(), pen, pen.color());	
    poly->setZValue (dZ_XLINK);

    // Control points for bezier path
    // (We have at least a begin branch, consider its orientation)
    initC0();
    initC1();

    ctrl_p0 = scene()->addEllipse (
	c0.x(), c0.y(),
	clickBorder*2, clickBorder*2,
	pen, pen.color() );
    ctrl_p1 = scene()->addEllipse (
	c1.x(), c1.y(),
	clickBorder*2, clickBorder*2,
	pen, pen.color() );

    beginOrient = endOrient=LinkableMapObj::UndefinedOrientation;
    pen.setWidth (1);
    pen.setStyle (Qt::DashLine);

    curSelection = Unselected;

    setVisibility (true);
}

QPointF XLinkObj::getAbsPos() 
{
    switch (curSelection)
    {
	case C0:
	    return c0;
	    break;
	case C1:
	    return c1;
	    break;
	default:
	    return QPointF();
	    break;
    }
}

void XLinkObj::setStyleBegin(const QString &s)
{
    pointerBegin->setStyleEnd( s );
}

void XLinkObj::setStyleBegin(ArrowObj::OrnamentStyle os)
{
    pointerBegin->setStyleEnd( os );
}

ArrowObj::OrnamentStyle XLinkObj::getStyleBegin()
{
    return pointerBegin->getStyleEnd();
}

void XLinkObj::setStyleEnd(const QString &s)
{
    pointerEnd->setStyleEnd( s );
}

void XLinkObj::setStyleEnd(ArrowObj::OrnamentStyle os)
{
    pointerEnd->setStyleEnd( os );
}

ArrowObj::OrnamentStyle XLinkObj::getStyleEnd()
{
    return pointerEnd->getStyleEnd();
}

QPointF XLinkObj::getBeginPos()
{
    return beginPos;
}

QPointF XLinkObj::getEndPos()
{
    return endPos;
}

void XLinkObj::move (QPointF p)
{
    switch (curSelection)
    {
	case C0:
	    c0=p;
	    break;
	case C1:
	    c1=p;
	    break;
	default:
	    break;
    }
    updateXLink();
}

void XLinkObj::setEnd (QPointF p)
{
    endPos=p;
}

void XLinkObj::setSelection (CurrentSelection s)
{
    curSelection=s;
    setVisibility();
}

void XLinkObj::setSelection (int cp)
{
    if (cp==0) 
	setSelection (C0);
    else if (cp==1)
	setSelection (C1);
    else
	qWarning()<<"XLO::setSelection cp="<<cp;
}

void XLinkObj::updateXLink()	
{
    QPointF a,b;
    QPolygonF pa;

    BranchObj *beginBO=NULL;
    BranchObj   *endBO=NULL;
    BranchItem *bi=link->getBeginBranch();
    if ( bi) beginBO=(BranchObj*)(bi->getLMO());
    bi=link->getEndBranch();
    if (bi) endBO=(BranchObj*)(bi->getLMO());

    if (beginBO) 
    {
	if (beginOrient != LinkableMapObj::UndefinedOrientation  &&
	    beginOrient != beginBO->getOrientation() )
	    c0.setX( -c0.x() );
	beginOrient = beginBO->getOrientation();
    }
    if (endBO)  
    {
	if (endOrient != LinkableMapObj::UndefinedOrientation  &&
	    endOrient != endBO->getOrientation() )
	    c1.setX( -c1.x() );
	endOrient = endBO->getOrientation();
    }

    if (visBranch)   
    {
	// Only one of the linked branches is visible
	// Draw arrowhead   //FIXME-3 missing shaft of arrow
	BranchObj *bo=(BranchObj*)(visBranch->getLMO());
	if (!bo) return;

	a=b=bo->getChildRefPos();


	if (bo->getOrientation()==LinkableMapObj::RightOfCenter)
	{
	    b.setX (b.x() + 2*arrowSize);
	    pa.clear();
	    pa << a <<
	          b <<
		  QPointF (b.x(), b.y() - arrowSize) <<
                  QPointF (b.x() + arrowSize, b.y()) <<
		  QPointF (b.x(), b.y() + arrowSize) <<
		  b <<
		  a;
	    poly->setPolygon(pa);
	} else
	{
	    b.setX (b.x() - 2*arrowSize);
	    pa.clear();
	    pa << a <<
	          b <<
		  QPointF (b.x(), b.y() - arrowSize) <<
                  QPointF (b.x() - arrowSize, b.y()) <<
		  QPointF (b.x(), b.y() + arrowSize) <<
		  b <<
		  a;
	    poly->setPolygon (pa);
	}   
    } else
    {
	// Both linked branches are visible

	// If a link is just drawn in the editor,
	// we have already a beginBranch
	if (beginBO) beginPos=beginBO->getChildRefPos();
	if (endBO) endPos=endBO->getChildRefPos();

        if (beginBO && endBO)
        {
            pointerBegin->move(beginPos + c0 );
            pointerBegin->setEndPoint(beginPos);

            pointerEnd->move(endPos + c1 );
            pointerEnd->setEndPoint(endPos);
        }
    }

    // Update control points for bezier
    QPainterPath p(beginPos);
    p.cubicTo ( beginPos + c0, endPos + c1, endPos);

    clickPath=p;
    path->setPath (p);	

    // Go back to create closed curve, 
    // needed for intersection check:	
    clickPath.cubicTo ( endPos + c1, beginPos + c0, beginPos);  

    QPen pen=link->getPen();
    path->setPen (pen);
    poly->setBrush (pen.color() );
    
    pointerBegin->setPen( pen );
    pointerEnd->setPen( pen );

    pen.setStyle (Qt::SolidLine);


    ctrl_p0->setRect (
	beginPos.x() + c0.x() - pointRadius/2, beginPos.y() + c0.y() - pointRadius/2,
	pointRadius, pointRadius );
    ctrl_p0->setPen (pen);
    ctrl_p0->setBrush (pen.color() );

    ctrl_p1->setRect (
	endPos.x() + c1.x() - pointRadius/2, endPos.y() + c1.y() - pointRadius/2,
	pointRadius, pointRadius );
    ctrl_p1->setPen (pen);
    ctrl_p1->setBrush (pen.color() );

    BranchItem *bi_begin=link->getBeginBranch();
    BranchItem *bi_end  =link->getEndBranch();
    if (bi_begin && bi_end && link->getState()==Link::activeXLink)
	// Note: with MapObj being a GraphicsItem now, maybe better reparent the xlinkobj
	// line->setZValue (dZ_DEPTH * max(bi_begin->depth(),bi_end->depth()) + dZ_XLINK); 
	path->setZValue (dZ_XLINK); 
    else	
	path->setZValue (dZ_XLINK);

    setVisibility();
}

void XLinkObj::positionBBox()
{
}

void XLinkObj::calcBBoxSize()
{
}

void XLinkObj::setVisibility (bool b)
{
    if (stateVis==FullShowControls)
    {
	ctrl_p0->show();
	ctrl_p1->show();
        pointerBegin->setUseFixedLength( false );
        pointerEnd->setUseFixedLength( false );
    } else
    {
	ctrl_p0->hide();
	ctrl_p1->hide();
        pointerBegin->setUseFixedLength( true );
        pointerBegin->setFixedLength( 0 );
        pointerEnd->setUseFixedLength( true );
        pointerEnd->setFixedLength( 0 );
    }

    MapObj::setVisibility (b);
    if (b)
    {
	if (stateVis==OnlyBegin) 
	{
	    path->hide();
	    poly->show();
            pointerBegin->hide();
            pointerEnd->hide();
	}
	else if (stateVis==OnlyEnd)
	{
	    path->hide();
	    poly->show();
            pointerBegin->hide();
            pointerEnd->hide();
	}
        else
        {
            path->show();
	    poly->hide();
            pointerBegin->show();
            pointerEnd->show();
        }
    }	
    else
    {
	poly->hide();
	path->hide();
        pointerBegin->hide();
        pointerEnd->hide();
    }	

}

void XLinkObj::setVisibility ()
{
    BranchItem* beginBI=link->getBeginBranch();
    BranchObj* beginBO=NULL;
    if (beginBI) beginBO=(BranchObj*)(beginBI->getLMO());

    BranchObj* endBO=NULL;
    BranchItem* endBI=link->getEndBranch();
    if (endBI) endBO=(BranchObj*)(endBI->getLMO());
    if (beginBO && endBO)
    {
	if(beginBO->isVisibleObj() && endBO->isVisibleObj())
	{   // Both ends are visible
	    visBranch=NULL;
	    if (curSelection != Unselected)
		stateVis=FullShowControls;
	    else	
		stateVis=Full;
	    setVisibility (true);
	} else
	{
	    if(!beginBO->isVisibleObj() && !endBO->isVisibleObj())
	    {	//None of the ends is visible
		visBranch=NULL;
		stateVis=Hidden;
		setVisibility (false);
	    } else
	    {	// Just one end is visible, draw a symbol that shows
		// that there is a link to a scrolled branch
		if (beginBO->isVisibleObj())
                {
                    stateVis=OnlyBegin;
		    visBranch=beginBI;
                }
		else
                {
		    visBranch=endBI;
                    stateVis=OnlyEnd;
                }
		setVisibility (true);
	    }
	}
    }
}

void XLinkObj::initC0()
{
    if ( !link ) return;
    BranchItem *beginBranch = link->getBeginBranch();
    if ( !beginBranch ) return;
    BranchObj *bo = beginBranch->getBranchObj();
    if ( !bo ) return;
    if ( bo->getOrientation() == LinkableMapObj::RightOfCenter  ) 
        c0 = QPointF ( d_control, 0);
    else
        c0 = QPointF ( -d_control, 0);
}

void XLinkObj::initC1()
{
    if (!link ) return; 
    BranchItem *endBranch = link->getEndBranch();
    if (!endBranch) return;
    BranchObj *bo = endBranch->getBranchObj();
    if (!bo) return;
    if ( bo->getOrientation() == LinkableMapObj::RightOfCenter  ) 
        c1 = QPointF ( d_control, 0);
    else
        c1 = QPointF ( -d_control, 0);
}

void XLinkObj::setC0(const QPointF &p)
{
    c0=p;
}

QPointF XLinkObj::getC0()
{
    return c0;
}

void XLinkObj::setC1(const QPointF &p)
{
    c1=p;
}

QPointF XLinkObj::getC1()
{
    return c1;
}

int XLinkObj::ctrlPointInClickBox (const QPointF &p)	
{
    CurrentSelection oldSel=curSelection;
    int ret=-1;

    QRectF r(p.x() - clickBorder, p.y() - clickBorder,
	             clickBorder *2, clickBorder*2) ;

    if (curSelection==C0 || curSelection==C1)
    {
	// If Cx selected, check both ctrl points 
	curSelection=C0;
	if (getClickPath().intersects (r) ) ret=0;
	curSelection=C1;
	if (getClickPath().intersects (r) ) ret=1;
    } 
    curSelection=oldSel;
    return ret;
}

bool XLinkObj::isInClickBox (const QPointF &p)
{
    // Return, if not visible at all...
    if (stateVis==Hidden) return false;

    CurrentSelection oldSel=curSelection;
    bool b=false;

    QRectF r(p.x() - clickBorder, p.y() - clickBorder,
	             clickBorder *2, clickBorder*2) ;

    switch (stateVis)
    {
	case FullShowControls:
	    // If Cx selected, check both ctrl points 
	    if (ctrlPointInClickBox(p) >-1) b=true;

            // Enable selecting the path, when a ctrl point is already selected
            if (!b && curSelection!=Unselected && clickPath.intersects (r)) b=true;
	    break;
	case OnlyBegin || OnlyEnd:    
	    // not selected, only partially visible
	    if (poly->boundingRect().contains(p) ) 
		b=true;
	    break;
	default:
	    // not selected, but path is fully visible
	    curSelection=Path;
	    if (getClickPath().intersects (r) ) b=true;
	    break;
    }
    curSelection=oldSel;
    return b;
}

QPainterPath XLinkObj::getClickPath()  // also needs mirroring if oriented left. Create method to generate the coordinates
{
    QPainterPath p;
    switch (curSelection)
    {
	case C0:
	    p.addEllipse (beginPos + c0,15,15);
	    return p;
	    break;
	case C1:
	    p.addEllipse (endPos + c1,15,15);
	    return p;
	    break;
	default:
	    return clickPath;
	    break;
    }
}
