#include "branchitem.h"

#include "attributeitem.h"
#include "branch-container.h"
#include "branch-wrapper.h"
#include "frame-container.h"
#include "heading-container.h"
#include "image-container.h"
#include "task.h"
#include "taskmodel.h"
#include "vymmodel.h"
#include "xlink.h"
#include "xlinkitem.h"

extern TaskModel *taskModel;

extern FlagRowMaster *systemFlagsMaster;

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;

    lastSelectedBranchNum = 0;
    lastSelectedBranchNumAlt = 0;

    task = nullptr;

    branchWrapperInt = nullptr;

    branchContainer = nullptr;
}

BranchItem::~BranchItem()
{
    // std::cout << "Destr. BranchItem: this=" << this << "  " << headingPlain().toStdString() << "  branchContainer=" << branchContainer << std::endl;
    if (task)
        taskModel->deleteTask(task);

    if (branchContainer) {
        // This deletes only the first container here.
        // All other containers deeper down in tree will unlink themselves 
        // by calling BranchItem::unlinkBranchContainer, which will set 
        // the branchContainer == nullptr;
        //
        // QGraphicsItems such as BranchContainer will delete all their children 
        // themselves
        delete branchContainer;
    }

    if (branchWrapperInt) {
        delete branchWrapperInt;
        branchWrapperInt = nullptr;
    }
}

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

BranchItem *BranchItem::parentBranch()
{
    // For MapCenters this will return rootItem
    return (BranchItem *)parentItem;
}

BranchWrapper *BranchItem::branchWrapper()
{
    if (!branchWrapperInt)
        branchWrapperInt = new BranchWrapper(this);

    return branchWrapperInt;
}

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

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

void BranchItem::insertImage(int pos, ImageItem *image)
{
    if (pos < 0)
        pos = 0;
    if (pos > imageCounter)
        pos = imageCounter;
    childItems.insert(pos + imageOffsetInt, image);

    // Set parentItem, rootItem and model
    image->setParentBranch(this);

    imageCounter++;
    branchOffsetInt++;
}

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

    QString attr;

    // Save uuid
    attr += attribute("uuid", uuid.toString());

    // vymLink, Url, hideExport, localTarget
    attr += getGeneralAttr();

    // Linkable attributes
    attr += getLinkableAttr();

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

    if (scrolled)
        attr += attribute("scrolled", "yes");

    // save area, if not scrolled
    // FIXME-5 we could check if _any_ of parents is scrolled
    if (exportBoundingBoxes && branchContainer && parentItem->hasTypeBranch() &&
        !((BranchItem *)parentItem)->isScrolled()) {
        QRectF r_bc = branchContainer->mapToScene(branchContainer->rect()).boundingRect();
        attr +=
            attribute("x1", QString().setNum(r_bc.topLeft().x() - offset.x())) +
            attribute("y1", QString().setNum(r_bc.topLeft().y() - offset.y())) +
            attribute("x2", QString().setNum(r_bc.bottomRight().x() - offset.x())) +
            attribute("y2", QString().setNum(r_bc.bottomRight().y() - offset.y()));
    }

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

    // Free positioning of children
    if (!branchContainer->branchesContainerAutoLayout)
        // Save the manually set layout for children branches
        attr += attribute("branchesLayout", branchContainer->layoutString(branchContainer->branchesContainerLayout()));

    if (!branchContainer->imagesContainerAutoLayout)
        // Save the manually set layout for children Images
        attr += attribute("imagesLayout", branchContainer->Container::layoutString(branchContainer->imagesContainerLayout()));

    if (!branchContainer->rotationsAutoDesign()) {
        attr += attribute("rotHeading", QString("%1").arg(branchContainer->rotationHeading()));

        attr += attribute("rotSubtree", QString("%1").arg(branchContainer->rotationSubtree()));
    }

    if (!branchContainer->scaleAutoDesign()) {
        attr += attribute("scaleHeading", QString("%1").arg(branchContainer->scaleHeading()));

        attr += attribute("scaleSubtree", QString("%1").arg(branchContainer->scaleSubtree()));
    }

    // width of heading
    if (!branchContainer->columnWidthAutoDesign())
        attr += attribute("colWidth", QString("%1").arg(branchContainer->getHeadingContainer()->columnWidth()));

    if (parentItem == rootItem || branchContainer->isFloating())
        attr += getPosAttr();

    QString s = beginElement(elementName + " " + attr);
    incIndent();

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

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

    // Save frame
    if (branchContainer->frameType(true) != FrameContainer::NoFrame ||
        branchContainer->frameType(false) != FrameContainer::NoFrame)
        s += branchContainer->saveFrame();

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

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

    // 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, tmpXLinks, exportBoundingBoxes);
        i++;
        ti = getBranchNum(i);
    }

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

void BranchItem::setHeadingColor(QColor color)
{
    TreeItem::setHeadingColor(color);
    branchContainer->setColor(color);
}

void BranchItem::updateTaskFlag()
{
    systemFlags.deactivateGroup("system-tasks");
    if (task) {
        QString s = "system-" + task->iconString();
        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; }

Flag *BranchItem::taskFlag()
{
    if (!task)
        return nullptr;

    return systemFlagsMaster->findFlagByName("system-" + task->iconString());
}

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;

    if (scrolled) {
        scrolled = false;
        systemFlags.deactivate(QString("system-scrolledright"));
    }
    else {
        scrolled = true;
        systemFlags.activate(QString("system-scrolledright"));
    }

    branchContainer->updateChildrenStructure();     // needed to insert linkSpaceContainer
    branchContainer->updateVisibility();
    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->hasTypeBranch())
        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->hasTypeBranch())
        result = pi->resetTmpUnscroll();

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

void BranchItem::setBranchesLayout(const QString &s)
{
    branchContainer->setBranchesContainerLayout(Container::layoutFromString(s));
}

void BranchItem::setImagesLayout(const QString &s)
{
    branchContainer->setImagesContainerLayout(Container::layoutFromString(s));
}

QColor BranchItem::getBackgroundColor(BranchItem *start, bool checkInnerFrame)
{
    if (!branchContainer)
        return QColor();

    // 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
        // No frame found
        return model->mapDesign()->backgroundColor();
}

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 (branchContainer->getOrientation() ==
                          BranchContainer::LeftOfParent) {
                ((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, QList <TreeItem*> excludedItems)
{
    // Search branches
    if (!isScrolled()) {
        TreeItem *ti;
        for (int i = 0; i < branchCounter; ++i) {
            ti = getBranchNum(i)->findMapItem(p, excludedItems);
            if (ti != nullptr)
                return ti;
        }
    }

    // Search images
    ImageItem *ii;
    ImageContainer *ic;
    for (int i = 0; i < imageCount(); ++i) {
        ii = getImageNum(i);
        ic = ii->getImageContainer();
        if (!excludedItems.contains(ii) && ic->isVisible() && ic->mapToScene(ic->rect()).containsPoint(p, Qt::OddEvenFill)) return ii;
    }

    // Search my container
    if (branchContainer->isVisible() && branchContainer->isInClickBox(p) && !excludedItems.contains(this) ) //   &&
        //getBranchObj()->isVisibleObj())
        return this;

    return nullptr;
}

void BranchItem::setHideMode(HideTmpMode mode)
{
    TreeItem::setHideMode(mode);
    branchContainer->updateVisibility();
}

void BranchItem::updateVisuals()
{
    branchContainer->updateVisuals();
}

BranchContainer *BranchItem::createBranchContainer(QGraphicsScene *scene)
{
    branchContainer = new BranchContainer(scene, this);

    if (parentBranch() != rootItem) {
        // For floating branches get a position hint
        parentBranch()->addToBranchesContainer(branchContainer);
        BranchContainer *pbc = branchContainer->parentBranchContainer();
        if (pbc->hasFloatingBranchesLayout())
            branchContainer->setPos(pbc->getPositionHintNewChild(branchContainer));

        // Link to parent branch visually by
        // adding my upLink to parents linkContainer
        branchContainer->linkTo(parentBranch()->getBranchContainer());
    }

    return branchContainer;
}

BranchContainer* BranchItem::getBranchContainer()
{
    if (this == rootItem && branchContainer)
        qWarning() << __func__ << "rootItem should not have BranchCOntainer";
    return branchContainer;
}

void BranchItem::unlinkBranchContainer()
{
    //qDebug() << "BI::unlinkBC in " << this << headingPlain();

    // Called from destructor of containers to 
    // avoid double deletion 
    branchContainer = nullptr;
}

Container* BranchItem::getBranchesContainer()
{
    return branchContainer->getBranchesContainer();
}

Container* BranchItem::getImagesContainer()
{
    return branchContainer->getImagesContainer();
}

void BranchItem::updateContainerStackingOrder()
{
    // After relinking branches (also moving up/down), the order of the 
    // BranchContainers does not match the order of BranchItems any longer and
    // needs to be adjusted. Or the BranchContainer has (temporarily) been linked to 
    // a completely different parent.
    //
    // It seems the QGraphicsItem::stackBefore only works, if an item is moved up. 
    // For moving below (or into another subtree), we have to reparent first  :-(

    // For simplicity we always reparent. The absolute position will not be changed here

    int n = num();

    QPointF sp = branchContainer->scenePos();

    branchContainer->setParentItem(nullptr);

    if (parentBranch() == rootItem) {
        // I am a MapCenter
        branchContainer->setPos(sp);
        return;
    }

    parentBranch()->addToBranchesContainer(branchContainer);

    while (n < parentBranch()->branchCount() - 1) {
        // Insert container of this branch above others

        // The next sibling container might currently still be temporarily 
        // linked to tmpParentContainer, in that case it is not a sibling and 
        // cannot be inserted using QGraphicsItem::stackBefore
        //
        // We try the next sibling then, if this fails, just append at the end.
        if ( (parentBranch()->getBranchNum(n + 1))->getContainer()->parentItem() != parentBranch()->getBranchesContainer() )
            n++;
        else {
            branchContainer->stackBefore( (parentBranch()->getBranchNum(n + 1))->getContainer() );
            break;
        }
    }

    branchContainer->setPos(branchContainer->parentItem()->sceneTransform().inverted().map(sp));
}

void BranchItem::addToBranchesContainer(BranchContainer *bc)
{
    branchContainer->addToBranchesContainer(bc);
}

void BranchItem::addToImagesContainer(ImageContainer *ic)
{
    // Keep scene position while relinking image container
    branchContainer->addToImagesContainer(ic);
}

void BranchItem::repositionContainers()
{
    branchContainer->reposition();
}
