#include "frameobj.h"

#include <QColor>
#include <QDebug>
#include <QGraphicsScene>

#include "misc.h" //for roof function

/////////////////////////////////////////////////////////////////
// FrameObj
/////////////////////////////////////////////////////////////////
FrameObj::FrameObj(QGraphicsItem *parent) : MapObj(parent) { init(); }

FrameObj::~FrameObj() { clear(); }

void FrameObj::init()
{
    type = NoFrame;
    clear();
    borderWidth = 1;
    penColor = QColor(Qt::black);
    brushColor = QColor(Qt::white);
    includeChildren = false;
}

void FrameObj::clear()
{
    switch (type) {
    case NoFrame:
        break;
    case Rectangle:
        delete rectFrame;
        break;
    case RoundedRectangle:
        delete pathFrame;
        break;
    case Ellipse:
        delete ellipseFrame;
        break;
    case Cloud:
        delete pathFrame;
        break;
    }
    type = NoFrame;
    padding = 0; // No frame requires also no padding
    xsize = 0;
}

void FrameObj::move(double x, double y)
{
    switch (type) {
    case NoFrame:
        break;
    case Rectangle:
        rectFrame->setPos(x, y);
        break;
    case RoundedRectangle:
        pathFrame->setPos(x, y);
        break;
    case Ellipse:
        ellipseFrame->setPos(x, y);
        break;
    case Cloud:
        pathFrame->setPos(x, y);
        break;
    }
}

void FrameObj::moveBy(double x, double y) { MapObj::moveBy(x, y); }

void FrameObj::positionBBox() {}

void FrameObj::calcBBoxSize() {}

void FrameObj::setRect(const QRectF &r)
{
    bbox = r;
    switch (type) {
    case NoFrame:
        break;

    case Rectangle:
        rectFrame->setRect(
            QRectF(bbox.x(), bbox.y(), bbox.width(), bbox.height()));
        break;

    case RoundedRectangle: {
        QPointF tl = bbox.topLeft();
        QPointF tr = bbox.topRight();
        QPointF bl = bbox.bottomLeft();
        QPointF br = bbox.bottomRight();
        QPainterPath path;

        qreal n = 10;
        path.moveTo(tl.x() + n / 2, tl.y());

        // Top path
        path.lineTo(tr.x() - n, tr.y());
        path.arcTo(tr.x() - n, tr.y(), n, n, 90, -90);
        path.lineTo(br.x(), br.y() - n);
        path.arcTo(br.x() - n, br.y() - n, n, n, 0, -90);
        path.lineTo(bl.x() + n, br.y());
        path.arcTo(bl.x(), bl.y() - n, n, n, -90, -90);
        path.lineTo(tl.x(), tl.y() + n);
        path.arcTo(tl.x(), tl.y(), n, n, 180, -90);
        pathFrame->setPath(path);
    } break;
    case Ellipse:
        ellipseFrame->setRect(
            QRectF(bbox.x(), bbox.y(), bbox.width(), bbox.height()));
        xsize = 20; // max(bbox.width(), bbox.height()) / 4;
        break;

    case Cloud:
        QPointF tl = bbox.topLeft();
        QPointF tr = bbox.topRight();
        QPointF bl = bbox.bottomLeft();
        QPainterPath path;
        path.moveTo(tl);

        float w = bbox.width();  // width
        float h = bbox.height(); // height
        int n = w / 40;          // number of intervalls
        float d = w / n;         // width of interwall

        // Top path
        for (float i = 0; i < n; i++) {
            path.cubicTo(tl.x() + i * d, tl.y() - 100 * roof((i + 0.5) / n),
                         tl.x() + (i + 1) * d,
                         tl.y() - 100 * roof((i + 0.5) / n),
                         tl.x() + (i + 1) * d + 20 * roof((i + 1) / n),
                         tl.y() - 50 * roof((i + 1) / n));
        }
        // Right path
        n = h / 20;
        d = h / n;
        for (float i = 0; i < n; i++) {
            path.cubicTo(tr.x() + 100 * roof((i + 0.5) / n), tr.y() + i * d,
                         tr.x() + 100 * roof((i + 0.5) / n),
                         tr.y() + (i + 1) * d, tr.x() + 60 * roof((i + 1) / n),
                         tr.y() + (i + 1) * d);
        }
        n = w / 60;
        d = w / n;
        // Bottom path
        for (float i = n; i > 0; i--) {
            path.cubicTo(bl.x() + i * d, bl.y() + 100 * roof((i - 0.5) / n),
                         bl.x() + (i - 1) * d,
                         bl.y() + 100 * roof((i - 0.5) / n),
                         bl.x() + (i - 1) * d + 20 * roof((i - 1) / n),
                         bl.y() + 50 * roof((i - 1) / n));
        }
        // Left path
        n = h / 20;
        d = h / n;
        for (float i = n; i > 0; i--) {
            path.cubicTo(tl.x() - 100 * roof((i - 0.5) / n), tr.y() + i * d,
                         tl.x() - 100 * roof((i - 0.5) / n),
                         tr.y() + (i - 1) * d, tl.x() - 60 * roof((i - 1) / n),
                         tr.y() + (i - 1) * d);
        }
        pathFrame->setPath(path);
        xsize = 50;
        break;
    }
}

void FrameObj::setPadding(const int &i) { padding = i; }

int FrameObj::getPadding()
{
    if (type == NoFrame)
        return 0;
    else
        return padding;
}

qreal FrameObj::getTotalPadding() { return xsize + padding + borderWidth; }

qreal FrameObj::getXPadding() { return xsize; }

void FrameObj::setBorderWidth(const int &i)
{
    borderWidth = i;
    repaint();
}

int FrameObj::getBorderWidth() { return borderWidth; }

FrameObj::FrameType FrameObj::getFrameType() { return type; }

FrameObj::FrameType FrameObj::getFrameType(const QString &s)
{
    if (s == "Rectangle")
        return Rectangle;
    else if (s == "RoundedRectangle")
        return RoundedRectangle;
    else if (s == "Ellipse")
        return Ellipse;
    else if (s == "Cloud")
        return Cloud;
    return NoFrame;
}

QString FrameObj::getFrameTypeName()
{
    switch (type) {
    case Rectangle:
        return "Rectangle";
        break;
    case RoundedRectangle:
        return "RoundedRectangle";
        break;
    case Ellipse:
        return "Ellipse";
        break;
    case Cloud:
        return "Cloud";
        break;
    default:
        return "NoFrame";
    }
}

void FrameObj::setFrameType(const FrameType &t)
{
    if (t != type) {
        clear();
        type = t;
        switch (type) {
        case NoFrame:
            break;
        case Rectangle:
            rectFrame = scene()->addRect(QRectF(0, 0, 0, 0), QPen(penColor),
                                         brushColor);
            rectFrame->setZValue(dZ_FRAME_LOW);
            rectFrame->setParentItem(this);
            rectFrame->show();
            break;
        case RoundedRectangle: {
            QPainterPath path;
            pathFrame = scene()->addPath(path, QPen(penColor), brushColor);
            pathFrame->setZValue(dZ_FRAME_LOW);
            pathFrame->setParentItem(this);
            pathFrame->show();
        } break;
        case Ellipse:
            ellipseFrame = scene()->addEllipse(QRectF(0, 0, 0, 0),
                                               QPen(penColor), brushColor);
            ellipseFrame->setZValue(dZ_FRAME_LOW);
            ellipseFrame->setParentItem(this);
            ellipseFrame->show();
            break;
        case Cloud: {
            QPainterPath path;
            pathFrame = scene()->addPath(path, QPen(penColor), brushColor);
            pathFrame->setZValue(dZ_FRAME_LOW);
            pathFrame->setParentItem(this);
            pathFrame->show();
            break;
        }
        }
    }
    setVisibility(visible);
}

void FrameObj::setFrameType(const QString &t)
{
    if (t == "Rectangle")
        FrameObj::setFrameType(Rectangle);
    else if (t == "RoundedRectangle")
        FrameObj::setFrameType(RoundedRectangle);
    else if (t == "Ellipse")
        FrameObj::setFrameType(Ellipse);
    else if (t == "Cloud")
        FrameObj::setFrameType(Cloud);
    else
        FrameObj::setFrameType(NoFrame);
}

void FrameObj::setPenColor(QColor col)
{
    penColor = col;
    repaint();
}

QColor FrameObj::getPenColor() { return penColor; }

void FrameObj::setBrushColor(QColor col)
{
    brushColor = col;
    repaint();
}

QColor FrameObj::getBrushColor() { return brushColor; }

void FrameObj::setFrameIncludeChildren(bool b) { includeChildren = b; }

bool FrameObj::getFrameIncludeChildren() { return includeChildren; }

void FrameObj::repaint()
{
    QPen pen;
    pen.setColor(penColor);
    pen.setWidth(borderWidth);
    QBrush brush(brushColor);
    switch (type) {
    case Rectangle:
        rectFrame->setPen(pen);
        rectFrame->setBrush(brush);
        break;
    case RoundedRectangle:
        pathFrame->setPen(pen);
        pathFrame->setBrush(brush);
        break;
    case Ellipse:
        ellipseFrame->setPen(pen);
        ellipseFrame->setBrush(brush);
        break;
    case Cloud:
        pathFrame->setPen(pen);
        pathFrame->setBrush(brush);
        break;
    default:
        break;
    }
}

void FrameObj::setZValue(double z)
{
    switch (type) {
    case NoFrame:
        break;
    case Rectangle:
        rectFrame->setZValue(z);
        break;
    case RoundedRectangle:
        pathFrame->setZValue(z);
        break;
    case Ellipse:
        ellipseFrame->setZValue(z);
        break;
    case Cloud:
        pathFrame->setZValue(z);
        break;
    }
}

void FrameObj::setVisibility(bool v)
{
    MapObj::setVisibility(v);
    switch (type) {
    case NoFrame:
        break;
    case Rectangle:
        if (visible)
            rectFrame->show();
        else
            rectFrame->hide();
        break;
    case RoundedRectangle:
        if (visible)
            pathFrame->show();
        else
            pathFrame->hide();
        break;
    case Ellipse:
        if (visible)
            ellipseFrame->show();
        else
            ellipseFrame->hide();
        break;
    case Cloud:
        if (visible)
            pathFrame->show();
        else
            pathFrame->hide();
        break;
    }
}

QString FrameObj::saveToDir()
{
    QString frameTypeAttr = attribut("frameType", getFrameTypeName());
    if (type == NoFrame)
        return singleElement("frame", frameTypeAttr);

    QString penColAttr = attribut("penColor", penColor.name());
    QString brushColAttr = attribut("brushColor", brushColor.name());
    QString paddingAttr = attribut("padding", QString::number(padding));
    QString borderWidthAttr =
        attribut("borderWidth", QString::number(borderWidth));
    QString incChildren;
    if (includeChildren)
        incChildren = attribut("includeChildren", "true");
    return singleElement("frame", frameTypeAttr + penColAttr + brushColAttr +
                                      paddingAttr + borderWidthAttr +
                                      incChildren);
}
