#include "branchitem.h"

#include "attributeitem.h"
#include "branchobj.h"
#include "task.h"
#include "taskmodel.h"
#include "vymmodel.h"
#include "xlink.h"
#include "xlinkitem.h"

extern TaskModel *taskModel;

//#include <QDir>

BranchItem::BranchItem(TreeItem *parent)
    : MapItem(parent)
{
    //qDebug()<< "Constr. BranchItem this=" << this << "parent:" << parent;

    // Set type if parent is known yet
    // if not, type is set in insertBranch or TreeItem::appendChild
    if (parent == rootItem)
        setType(MapCenter);
    else
        setType(Branch);

    scrolled = false;
    tmpUnscrolled = false;

    includeImagesVer = false;
    includeImagesHor = false;
    includeChildren = false;
    childrenLayout = BranchItem::AutoPositioning;

    lastSelectedBranchNum = 0;
    lastSelectedBranchNumAlt = 0;

    task = NULL;
}

BranchItem::~BranchItem()
{
    // qDebug()<< "Destr. BranchItem  this="<<this<<"  "<<getHeading();
    if (mo) {
        delete mo;
        mo = NULL;
    }
    clear();
}

void BranchItem::clear()
{
    if (task)
        taskModel->deleteTask(task);
}

void BranchItem::copy(BranchItem *other) // TODO lacks most of data...
{
    scrolled = other->scrolled;
    tmpUnscrolled = other->tmpUnscrolled;
}

BranchItem *BranchItem::parentBranch() { return (BranchItem *)parentItem; }

void BranchItem::insertBranch(int pos, BranchItem *branch)
{
    if (pos < 0)
        pos = 0;
    if (pos > branchCounter)
        pos = branchCounter;
    childItems.insert(pos + branchOffset, branch);
    branch->parentItem = this;
    branch->rootItem = rootItem;
    branch->setModel(model);
    if (parentItem == rootItem)
        setType(MapCenter);
    else
        setType(Branch);

    if (branchCounter == 0)
        branchOffset = childItems.count() - 1;
    branchCounter++;
}

QString BranchItem::saveToDir(const QString &tmpdir, const QString &prefix,
                              const QPointF &offset, QList<Link *> &tmpLinks)
{
    // Cloudy stuff can be hidden during exports
    if (hidden)
        return QString();

    // Save uuid
    QString idAttr = attribut("uuid", uuid.toString());

    QString s, a;

    // Update of note is usually done while unselecting a branch

    QString scrolledAttr;
    if (scrolled)
        scrolledAttr = attribut("scrolled", "yes");
    else
        scrolledAttr = "";

    // save area, if not scrolled   // not needed if HTML is rewritten...
    // also we could check if _any_ of parents is scrolled
    QString areaAttr;
    if (mo && parentItem->isBranchLikeType() &&
        !((BranchItem *)parentItem)->isScrolled()) {
        qreal x = mo->getAbsPos().x();
        qreal y = mo->getAbsPos().y();
        areaAttr =
            attribut("x1", QString().setNum(x - offset.x())) +
            attribut("y1", QString().setNum(y - offset.y())) +
            attribut("x2", QString().setNum(x + mo->width() - offset.x())) +
            attribut("y2", QString().setNum(y + mo->height() - offset.y()));
    }
    else
        areaAttr = "";

    QString elementName;
    if (parentItem == rootItem)
        elementName = "mapcenter";
    else
        elementName = "branch";

    // Free positioning of children
    QString layoutAttr;
    if (childrenLayout == BranchItem::FreePositioning)
        layoutAttr += attribut("childrenFreePos", "true");

    // Save rotation
    QString rotAttr;
    if (mo && mo->getRotation() != 0)
        rotAttr = attribut("rotation", QString().setNum(mo->getRotation()));

    s = beginElement(elementName + getMapAttr() + getGeneralAttr() +
                     scrolledAttr + getIncludeImageAttr() + rotAttr +
                     layoutAttr + idAttr);
    incIndent();

    // save heading
    s += heading.saveToDir();

    // save note
    if (!note.isEmpty())
        s += note.saveToDir();

    // Save frame  // not saved if there is no MO
    if (mo) {
        // Avoid saving NoFrame for objects other than MapCenter
        if (depth() == 0 || ((OrnamentedObj *)mo)->getFrame()->getFrameType() !=
                                FrameObj::NoFrame)
            s += ((OrnamentedObj *)mo)->getFrame()->saveToDir();
    }

    // save names of flags set
    s += standardFlags.saveState();
    s += userFlags.saveState();

    // Save Images
    for (int i = 0; i < imageCount(); ++i)
        s += getImageNum(i)->saveToDir(tmpdir, prefix);

    // save attributes
    for (int i = 0; i < attributeCount(); ++i)
        s += getAttributeNum(i)->getDataXML();

    // save task
    if (task)
        s += task->saveToDir();

    // Save branches
    int i = 0;
    TreeItem *ti = getBranchNum(i);
    while (ti) {
        s += getBranchNum(i)->saveToDir(tmpdir, prefix, offset, tmpLinks);
        i++;
        ti = getBranchNum(i);
    }

    // Mark Links for save
    for (int i = 0; i < xlinkCount(); ++i) {
        Link *l = getXLinkItemNum(i)->getLink();
        if (l && !tmpLinks.contains(l))
            tmpLinks.append(l);
    }
    decIndent();
    s += endElement(elementName);
    return s;
}

void BranchItem::updateVisibility()
{
    // Needed to hide relinked branch, if parent is scrolled
    if (mo) {
        if (hasScrolledParent(this) || hidden)
            mo->setVisibility(false);
        else
            mo->setVisibility(true);
    }
}

void BranchItem::setHeadingColor(QColor color)
{
    TreeItem::setHeadingColor(color);
    if (mo)
        ((BranchObj *)mo)->setColor(color);
}

void BranchItem::updateTaskFlag()
{
    systemFlags.deactivateGroup("system-tasks");
    if (task) {
        QString s = "system-" + task->getIconString();
        systemFlags.activate(s);
        model->emitDataChanged(this);
    }
    // else: During initialization the task is not yet attached to branch,
    // so ignore it for now
}

void BranchItem::setTask(Task *t)
{
    task = t;
    updateTaskFlag();
}

Task *BranchItem::getTask() { return task; }

void BranchItem::scroll()
{
    if (tmpUnscrolled)
        resetTmpUnscroll();
    if (!scrolled)
        toggleScroll();
}
void BranchItem::unScroll()
{
    if (tmpUnscrolled)
        resetTmpUnscroll();
    if (scrolled)
        toggleScroll();
}

bool BranchItem::toggleScroll()
{
    // MapCenters are not scrollable
    if (depth() == 0)
        return false;

    BranchObj *bo;
    if (scrolled) {
        scrolled = false;
        systemFlags.deactivate(QString("system-scrolledright"));
        if (branchCounter > 0)
            for (int i = 0; i < branchCounter; ++i) {
                bo = (BranchObj *)(getBranchNum(i)->getMO());
                if (bo)
                    bo->setVisibility(true); // Recursively!
            }
    }
    else {
        scrolled = true;
        systemFlags.activate(QString("system-scrolledright"));
        if (branchCounter > 0)
            for (int i = 0; i < branchCounter; ++i) {
                bo = (BranchObj *)(getBranchNum(i)->getMO());
                if (bo)
                    bo->setVisibility(false); // Recursively!
            }
    }
    return true;
}

bool BranchItem::isScrolled() { return scrolled; }

bool BranchItem::hasScrolledParent(BranchItem *start)
{
    // Calls parents recursivly to
    // find out, if we are scrolled at all.
    // But ignore myself, just look at parents.

    if (!start)
        start = this;

    if (this != start && scrolled)
        return true;

    BranchItem *bi = (BranchItem *)parentItem;
    if (bi && bi != rootItem)
        return bi->hasScrolledParent(start);
    else
        return false;
}

bool BranchItem::tmpUnscroll(BranchItem *start)
{
    bool result = false;

    if (!start)
        start = this;

    // Unscroll parent (recursivly)
    BranchItem *pi = (BranchItem *)parentItem;
    if (pi && pi->isBranchLikeType())
        result = pi->tmpUnscroll(start);

    // Unscroll myself
    if (start != this && scrolled) {
        tmpUnscrolled = true;
        systemFlags.activate(QString("system-tmpUnscrolledRight"));
        toggleScroll();
        model->emitDataChanged(this);
        result = true;
    }
    return result;
}

bool BranchItem::resetTmpUnscroll()
{
    bool result = false;

    // Unscroll parent (recursivly)
    BranchItem *pi = (BranchItem *)parentItem;
    if (pi && pi->isBranchLikeType())
        result = pi->resetTmpUnscroll();

    // Unscroll myself
    if (tmpUnscrolled) {
        tmpUnscrolled = false;
        systemFlags.deactivate(QString("system-tmpUnscrolledRight"));
        toggleScroll();
        model->emitDataChanged(this);
        result = true;
    }
    return result;
}

void BranchItem::sortChildren(
    bool inverse) // FIXME-4 optimize by not using moveUp/Down
{
    int childCount = branchCounter;
    int curChildIndex;
    bool madeChanges = false;
    do {
        madeChanges = false;
        for (curChildIndex = 1; curChildIndex < childCount; curChildIndex++) {
            BranchItem *curChild = getBranchNum(curChildIndex);
            BranchItem *prevChild = getBranchNum(curChildIndex - 1);
            if (inverse) {
                if (prevChild->getHeadingPlain().compare(
                        curChild->getHeadingPlain(), Qt::CaseInsensitive) < 0) {
                    model->moveUp(curChild);
                    madeChanges = true;
                }
            }
            else if (prevChild->getHeadingPlain().compare(
                         curChild->getHeadingPlain(), Qt::CaseInsensitive) >
                     0) {
                model->moveUp(curChild);
                madeChanges = true;
            }
        }
    } while (madeChanges);
}

void BranchItem::setChildrenLayout(BranchItem::LayoutHint layoutHint)
{
    childrenLayout = layoutHint;
}

BranchItem::LayoutHint BranchItem::getChildrenLayout()
{
    return childrenLayout;
}

void BranchItem::setIncludeImagesVer(bool b) { includeImagesVer = b; }

bool BranchItem::getIncludeImagesVer() { return includeImagesVer; }

void BranchItem::setIncludeImagesHor(bool b) { includeImagesHor = b; }

bool BranchItem::getIncludeImagesHor() { return includeImagesHor; }

QString BranchItem::getIncludeImageAttr()
{
    QString a;
    if (includeImagesVer)
        a = attribut("incImgV", "true");
    if (includeImagesHor)
        a += attribut("incImgH", "true");
    return a;
}

BranchItem *BranchItem::getFramedParentBranch(BranchItem *start)
{
    BranchObj *bo = getBranchObj();
    if (bo && bo->getFrameType() != FrameObj::NoFrame) {
        if (bo->getFrame()->getFrameIncludeChildren())
            return this;
        if (this == start)
            return this;
    }
    BranchItem *bi = (BranchItem *)parentItem;
    if (bi && bi != rootItem)
        return bi->getFramedParentBranch(start);
    else
        return NULL;
}

void BranchItem::setFrameIncludeChildren(bool b)
{
    includeChildren = b; // FIXME-4 ugly: same information stored in FrameObj
    BranchObj *bo = getBranchObj();
    if (bo)
        bo->getFrame()->setFrameIncludeChildren(b);
}

bool BranchItem::getFrameIncludeChildren()
{
    BranchObj *bo = getBranchObj();
    if (bo)
        return bo->getFrame()->getFrameIncludeChildren();
    else
        return includeChildren;
}

QColor BranchItem::getBackgroundColor(BranchItem *start, bool checkInnerFrame)
{
    /*
    // Determine background color in taskEditor, first try inner frame
    if (checkInnerFrame && branchContainer->frameType(true) != FrameContainer::NoFrame)
            return branchContainer->frameBrushColor(true);

    // Outer frame
    if (branchContainer->frameType(false) != FrameContainer::NoFrame)
            return branchContainer->frameBrushColor(false);

    BranchItem *pb = parentBranch();
    if (pb && pb != rootItem)
        // Recursively try parents and check for frames there
        return pb->getBackgroundColor(start, false);
    else
    */
    BranchItem *bi = getFramedParentBranch(start);
    if (bi)
        return bi->getBranchObj()->getFrameBrushColor();

        // No frame found
        return model->getMapBackgroundColor();
}

void BranchItem::setLastSelectedBranch()
{
    int d = depth();
    if (d >= 0) {
        if (d == 1)
            // Hack to save an additional lastSelected for mapcenters in
            // MapEditor depending on orientation this allows to go both left
            // and right from there
            if (mo && ((BranchObj *)mo)->getOrientation() ==
                          LinkableMapObj::LeftOfCenter) {
                ((BranchItem *)parentItem)->lastSelectedBranchNumAlt =
                    parentItem->num(this);
                return;
            }
        ((BranchItem *)parentItem)->lastSelectedBranchNum =
            parentItem->num(this);
    }
}

void BranchItem::setLastSelectedBranch(int i) { lastSelectedBranchNum = i; }

BranchItem *BranchItem::getLastSelectedBranch()
{
    if (lastSelectedBranchNum >= branchCounter)
        return getBranchNum(branchCounter - 1);
    else
        return getBranchNum(lastSelectedBranchNum);
}

BranchItem *BranchItem::getLastSelectedBranchAlt()
{
    return getBranchNum(lastSelectedBranchNumAlt);
}

TreeItem *BranchItem::findMapItem(QPointF p, TreeItem *excludeTI)
{
    // Search branches
    TreeItem *ti;
    for (int i = 0; i < branchCount(); ++i) {
        ti = getBranchNum(i)->findMapItem(p, excludeTI);
        if (ti != NULL)
            return ti;
    }

    // Search images
    ImageItem *ii;
    for (int i = 0; i < imageCount(); ++i) {
        ii = getImageNum(i);
        MapObj *mo = ii->getMO();
        if (mo && mo->isInClickBox(p) && (ii != excludeTI) &&
            this != excludeTI && mo->isVisibleObj())
            return ii;
    }

    // Search myself
    if (getBranchObj()->isInClickBox(p) && (this != excludeTI) &&
        getBranchObj()->isVisibleObj())
        return this;

    // Search attributes
    AttributeItem *ai;
    for (int i = 0; i < attributeCount(); ++i) {
        ai = getAttributeNum(i);
        MapObj *mo = ai->getMO();
        if (mo && mo->isInClickBox(p) && (ai != excludeTI) &&
            this != excludeTI && mo->isVisibleObj())
            return ai;
    }
    return NULL;
}

void BranchItem::updateStyles(const bool &keepFrame)
{
    // Update styles when relinking branches
    if (mo) {
        BranchObj *bo = getBranchObj();
        if (parentItem != rootItem)
            bo->setParObj((LinkableMapObj *)(((MapItem *)parentItem)->getMO()));
        else
            bo->setParObj(NULL);
        bo->setDefAttr(BranchObj::MovedBranch, keepFrame);
    }
}

BranchObj *BranchItem::getBranchObj() { return (BranchObj *)mo; }

BranchObj *BranchItem::createMapObj(QGraphicsScene *scene)
{
    BranchObj *newbo;

    if (parentItem == rootItem) {
        newbo = new BranchObj(NULL, this);
        mo = newbo;
        scene->addItem(newbo);
    }
    else {
        newbo = new BranchObj(((MapItem *)parentItem)->getMO(), this);
        mo = newbo;
        // Set visibility depending on parents
        if (parentItem != rootItem &&
            (((BranchItem *)parentItem)->scrolled ||
             !((MapItem *)parentItem)->getLMO()->isVisibleObj()))
            newbo->setVisibility(false);
        if (depth() == 1) {
            qreal r = 190;
            qreal a =
                -M_PI_4 + M_PI_2 * (num()) + (M_PI_4 / 2) * (num() / 4 % 4);
            QPointF p(r * cos(a), r * sin(a));
            newbo->setRelPos(p);
        }
    }
    newbo->setDefAttr(BranchObj::NewBranch);
    initLMO();

    if (!getHeading().isEmpty()) {
        newbo->updateVisuals();
        newbo->setColor(heading.getColor());
    }

    return newbo;
}
