#include "linkobj.h"

#include <math.h>

#include <QDebug>
#include <QGraphicsEllipseItem>

/////////////////////////////////////////////////////////////////
// LinkObj
/////////////////////////////////////////////////////////////////

LinkObj::LinkObj(QGraphicsItem *parent) : MapObj(parent)
{
    //qDebug() << "Const LinkObj this=" << this;
    init();
}

LinkObj::~LinkObj()
{
    //qDebug()<< "Destructor LO  this=" << this;
    delLink();
    deleteBottomLine();
}

void LinkObj::init()
{
    l = nullptr;
    p = nullptr;
    linkcolor = Qt::black;
    thickness_start = 8;
    style = NoLink;
    arcsegs = 13;

    // FIXME-3 instead of linkcolor pen.color() could be used all around
    pen.setWidth(1);
    pen.setColor(linkcolor);
    pen.setCapStyle(Qt::RoundCap);

    bottomLine = nullptr;
}

void LinkObj::createBottomLine()
{
    if (bottomLine) return;

    bottomLine = new QGraphicsLineItem(this);
    bottomLine->setPen(pen);
}

void LinkObj::deleteBottomLine()
{
    if (bottomLine) {
        delete bottomLine;
        bottomLine = nullptr;
    }
}

bool LinkObj::hasBottomLine()
{
    if (bottomLine)
        return true;
    else
        return false;
}

void LinkObj::delLink()
{
    switch (style) {
        case Line:
            delete (l);
            break;
        case Parabel:
            while (!segments.isEmpty())
                delete segments.takeFirst();
            break;
        case PolyLine:
            delete (p);
            break;
        case PolyParabel:
            delete (p);
            break;
        case ListDash:
            delete (p);
            break;
        default:
            break;
    }
}

void LinkObj::setLinkStyle(Style newStyle)
{
    if (style == newStyle) return;

    delLink();

    style = newStyle;

    QGraphicsLineItem *cl;
    switch (style) {
        case Line:
            l = new QGraphicsLineItem(this);
            l->setPen(pen);
            break;
        case Parabel:
            for (int i = 0; i < arcsegs; i++) {
                cl = new QGraphicsLineItem(this);
                cl->setLine(QLineF(i * 5, 0, i * 10, 100));
                cl->setPen(pen);
                segments.append(cl);
            }
            pa0.resize(arcsegs + 1);
            break;
        case PolyLine:
            p = new QGraphicsPolygonItem(this);
            p->setPen(pen);
            p->setBrush(QBrush(pen.color()));
            pa0.resize(3);
            break;
        case PolyParabel:
            p = new QGraphicsPolygonItem(this);
            p->setPen(pen);
            p->setBrush(QBrush(pen.color()));
            pa0.resize(arcsegs * 2 + 2);
            pa1.resize(arcsegs + 1);
            pa2.resize(arcsegs + 1);
            break;
        case ListDash:
            p = new QGraphicsPolygonItem(this);
            p->setPen(pen);
            p->setBrush(QBrush(pen.color()));
            pa0.resize(3);
            break;
        default:
            break;
    }
}

LinkObj::Style LinkObj::getLinkStyle() { return style; }

LinkObj::Style LinkObj::styleFromString(const QString &s)
{
    LinkObj::Style style;
    if (s == "StyleNoLink")
        return LinkObj::NoLink;
    else if (s == "StyleLine")
        return LinkObj::Line;
    else if (s == "StyleParabel")
        return LinkObj::Parabel;
    else if (s == "StylePolyLine")
        return LinkObj::PolyLine;
    else if (s == "StylePolyParabel")
        return LinkObj::PolyParabel;
    else if (s == "ListDash")
        return LinkObj::ListDash;

    return  LinkObj::Undefined;
}

QString LinkObj::styleString(int style)
{
    switch (style) {
        case LinkObj::NoLink:
            return "StyleNoLink";
        case LinkObj::Line:
            return "StyleLine";
        case LinkObj::Parabel:
            return "StyleParabel";
        case LinkObj::PolyLine:
            return "StylePolyLine";
        case LinkObj::PolyParabel:
            return "StylePolyParabel";
        case LinkObj::ListDash:
            return "ListDash";
        default:
            return "StyleUndefined";
    }
}

void LinkObj::setLinkColorHint(ColorHint hint)
{
    colorHint = hint;
}

LinkObj::ColorHint LinkObj::linkColorHint()
{
    return colorHint;
}

LinkObj::ColorHint LinkObj::linkColorHint(const QString &s)
{
    if (s == "HeadingColor")
        return HeadingColor;
    else
        return DefaultColor;
}

QString LinkObj::linkColorHintName(ColorHint hint)
{
    QString s;

    if (hint == LinkObj::HeadingColor)
        return "HeadingColor";
    else
        return "DefaultColor";
}

void LinkObj::setLinkColor(QColor col)
{
    if (linkcolor == col) return;

    linkcolor = col;
    pen.setColor(col);

    if (bottomLine)
        bottomLine->setPen(pen);

    switch (style) {
        case Line:
            l->setPen(pen);
            break;
        case Parabel:
            for (int i = 0; i < segments.size(); ++i)
                segments.at(i)->setPen(pen);
            break;
        case PolyLine:
            p->setBrush(QBrush(linkcolor));
            p->setPen(pen);
            break;
        case PolyParabel:
            p->setBrush(QBrush(linkcolor));
            p->setPen(pen);
            break;
        case ListDash:
            p->setBrush(QBrush(linkcolor));
            p->setPen(pen);
            break;
        default:
            break;
    }
}

QColor LinkObj::getLinkColor() { return linkcolor; }

void LinkObj::updateLinkGeometry()
{
    // needs:
    //	upLinkPosParent
    //	linkPosSelf
    //	orient   of parent
    //	style
    //
    // sets:
    //	drawing of bottomLine and upLink

    // Draw the horizontal line below heading (from childRefPos to parentPos)
    if (bottomLine)
        bottomLine->setLine(QLineF(upLinkPosSelf, downLinkPos));

    if (style == NoLink)
        return;

    double par_x = upLinkPosParent.x(); // Link is drawn from P1 to P2
    double par_y = upLinkPosParent.y();

    double self_x = upLinkPosSelf.x();
    double self_y = upLinkPosSelf.y();

    double vx = self_x - par_x;
    double vy = self_y - par_y;

    int z = -20000;
    double a; // angle
    if (vx > -0.000001 && vx < 0.000001)
        a = M_PI_2;
    else
        a = atan(vy / vx);

    // "turning point" for drawing polygonal links
    QPointF tp(sin(a) * thickness_start, - cos(a) * thickness_start);

    // Draw the link
    switch (style) {
        case Line:
            l->setLine(par_x, par_y, self_x, self_y);
            break;
        case Parabel:
            //qDebug() << "LO::updateLG for parabel  x,y=" << QPointF(par_x, par_y) << QPointF(self_x, self_y);
            parabel(pa0, par_x, par_y, self_x, self_y);
            for (int i = 0; i < segments.size(); ++i) {
                segments.at(i)->setLine(QLineF(
                            pa0.at(i).x(),
                            pa0.at(i).y(),
                            pa0.at(i + 1).x(),
                            pa0.at(i + 1).y()));
            }
            break;
        case PolyLine:
            pa0.clear();
            pa0 << QPointF(par_x + tp.x(), par_y + tp.y());
            pa0 << QPointF(par_x - tp.x(), par_y - tp.y());
            pa0 << QPointF(self_x, self_y);
            p->setPolygon(QPolygonF(pa0));
            break;
        case PolyParabel:
            parabel(pa1, par_x + tp.x(), par_y + tp.y(), self_x, self_y);
            parabel(pa2, par_x - tp.x(), par_y - tp.y(), self_x, self_y);
            pa0.clear();
            for (int i = 0; i <= arcsegs; i++)
                pa0 << QPointF(pa1.at(i));
            for (int i = 0; i <= arcsegs; i++)
                pa0 << QPointF(pa2.at(arcsegs - i));
            p->setPolygon(QPolygonF(pa0));
            break;
        case ListDash: {  // FIXME-3 WIP, cont here. Not used.
                          // Width defined in BC:  linkSPaceContainer->setHeading("   ")
            pa0.clear();
            // center of LinkContainer, which will contain the list
            qDebug() << "LO::updateLG for ListDash bPP=" << bulletPointPos;
            //pa0 << QPointF(bulletPointPos + QPointF(-4, 0) + QPointF(self_x, self_y));
            /*
            pa0 << QPointF(bulletPointPos + QPointF( 4, 0));
            pa0 << QPointF(bulletPointPos + QPointF(-4, 0));
            p->setPolygon(QPolygonF(pa0));
            */
            }
            break;
        default:
            qWarning() << "LinkObj::updateLinkGeometry - Unknown LinkStyle s=" << style << " in " << __LINE__;
            break;
    }
}

void LinkObj::setUpLinkPosParent(const QPointF& p)
{
    upLinkPosParent = p;
}

void LinkObj::setUpLinkPosSelf(const QPointF& p)
{
    upLinkPosSelf = p;
}

void LinkObj::setDownLinkPos(const QPointF& p)
{
    downLinkPos= p;
}

void LinkObj::setBulletPointPos(const QPointF& p)
{
    bulletPointPos= p;
}

void LinkObj::parabel(QPolygonF &ya, qreal p1x, qreal p1y, qreal p2x,
                             qreal p2y)
{
    qreal vx = p2x - p1x;
    qreal vy = p2y - p1y;

    qreal dx; // delta x during calculation of parabel

    qreal pnx; // next point
    qreal pny;
    qreal m;

    if (vx > -0.0001 && vx < 0.0001)
        m = 0;
    else
        m = - (vy / (vx * vx));
    dx = vx / (arcsegs);
    ya.clear();
    ya << QPointF(p1x, p1y);
    for (int i = 1; i <= arcsegs; i++) {
        pnx = p1x + dx;
        pny = m * (pnx - p2x) * (pnx - p2x) + p2y;
        ya << QPointF(pnx, pny);
        p1x = pnx;
        p1y = pny;
    }
}

void LinkObj::reposition()
{
    //qDebug() << "LC::reposition " << info();
    return;
}

