/* This file is part of the KDE project
 * Copyright (C) 2007 Thomas Zander <zander@kde.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "KoTextAnchor.h"
#include "KoTextDocumentLayout.h"
#include "KoTextShapeContainerModel.h"

#include <KoShapeContainer.h>
#include <KoXmlWriter.h>
#include <KoShapeSavingContext.h>
#include <KoUnit.h>

#include <QTextInlineObject>
#include <QFontMetricsF>
#include <QPainter>
#include <KDebug>

#define I_WANT_BAD_LOOKING_THINGS_FOR_DEBUG 0
class KoTextAnchor::Private
{
public:
    Private(KoTextAnchor *p, KoShape *s)
            : parent(p),
            shape(s),
            horizontalAlignment(HorizontalOffset),
            verticalAlignment(VerticalOffset),
            document(0),
            position(-1),
            model(0),
            pageNumber(-1),
            anchorType(Paragraph)
    {
        Q_ASSERT(shape);
    }

    void relayout()
    {
        if (document) {
            KoTextDocumentLayout *lay = dynamic_cast<KoTextDocumentLayout*>(document->documentLayout());
            if (lay)
                lay->documentChanged(position, 0, 0);
        }
    }

    /// as multiple shapes can hold 1 text flow; the anchored shape can be moved between containers and thus models
    void setContainer(KoShapeContainer *container)
    {
        if (container == 0) {
            model = 0;
            shape->setParent(0);
            return;
        }
        KoTextShapeContainerModel *theModel = dynamic_cast<KoTextShapeContainerModel*>(container->model());
        if (theModel != model) {
            if (shape->parent() != container) {
                if (shape->parent())
                    shape->parent()->removeChild(shape);
                container->addChild(shape);
            }
            model = theModel;
            model->addAnchor(parent);
        }
    }

    KoTextAnchor * const parent;
    KoShape * const shape;
    AnchorHorizontal horizontalAlignment;
    AnchorVertical verticalAlignment;
    const QTextDocument *document;
    int position;
    KoTextShapeContainerModel *model;
    QPointF distance;
    int pageNumber;
    AnchorType anchorType;
};

KoTextAnchor::KoTextAnchor(KoShape *shape)
        : KoInlineObject(false),
        d(new Private(this, shape))
{
}

KoTextAnchor::~KoTextAnchor()
{
    if (d->model)
        d->model->removeAnchor(this);
    delete d;
}

KoShape *KoTextAnchor::shape() const
{
    return d->shape;
}

void KoTextAnchor::setAlignment(KoTextAnchor::AnchorHorizontal horizontal)
{
    if (d->horizontalAlignment == horizontal)
        return;
    d->horizontalAlignment = horizontal;
    d->relayout();
}

void KoTextAnchor::setAlignment(KoTextAnchor::AnchorVertical vertical)
{
    if (d->verticalAlignment == vertical)
        return;
    d->verticalAlignment = vertical;
    d->relayout();
}

KoTextAnchor::AnchorVertical KoTextAnchor::verticalAlignment() const
{
    return d->verticalAlignment;
}

KoTextAnchor::AnchorHorizontal KoTextAnchor::horizontalAlignment() const
{
    return d->horizontalAlignment;
}

void KoTextAnchor::updatePosition(const QTextDocument *document, QTextInlineObject object, int posInDocument, const QTextCharFormat &format)
{
    Q_UNUSED(object);
    Q_UNUSED(format);
    d->document = document;
    d->position = posInDocument;
    d->setContainer(dynamic_cast<KoShapeContainer*>(shapeForPosition(document, posInDocument)));
}

void KoTextAnchor::resize(const QTextDocument *document, QTextInlineObject object, int posInDocument, const QTextCharFormat &format, QPaintDevice *pd)
{
    Q_UNUSED(document);
    Q_UNUSED(object);
    Q_UNUSED(posInDocument);
    Q_UNUSED(format);
    Q_UNUSED(pd);

    if (horizontalAlignment() == HorizontalOffset && verticalAlignment() == VerticalOffset) {
        object.setWidth(d->shape->size().width());
        object.setAscent(d->shape->size().height());
    } else {
        QFontMetricsF fm(format.font());
        object.setWidth(0);
        object.setAscent(fm.ascent());
    }
    object.setDescent(0);
}

void KoTextAnchor::paint(QPainter &painter, QPaintDevice *pd, const QTextDocument *document, const QRectF &rect, QTextInlineObject object, int posInDocument, const QTextCharFormat &format)
{
    Q_UNUSED(document);
    Q_UNUSED(painter);
    Q_UNUSED(pd);
    Q_UNUSED(rect);
    Q_UNUSED(object);
    Q_UNUSED(posInDocument);
    Q_UNUSED(format);
#if I_WANT_BAD_LOOKING_THINGS_FOR_DEBUG
    kDebug() << "*****************************************************************************************************************************";
    kDebug() << "painting KoTextAnchor : " << rect ;
    kDebug() << "*****************************************************************************************************************************";
    painter.setOpacity(0.5);
    painter.drawLine(0, 0, 15, 15);
    painter.drawLine(15, 0, 0, 15);
    painter.setOpacity(1);
#endif
    // we never paint ourselves; the shape can do that.
}

int KoTextAnchor::positionInDocument() const
{
    return d->position;
}

const QTextDocument *KoTextAnchor::document() const
{
    return d->document;
}

const QPointF &KoTextAnchor::offset() const
{
    return d->distance;
}

void KoTextAnchor::setOffset(const QPointF &offset)
{
    if (d->distance == offset)
        return;
    d->distance = offset;
    d->relayout();
}

void KoTextAnchor::saveOdf(KoShapeSavingContext & context)
{
    shape()->removeAdditionalAttribute("text:anchor-page-number");
    switch (d->anchorType) {
    case Page:
        shape()->setAdditionalAttribute("text:anchor-type", "page");
        Q_ASSERT(d->pageNumber >= 1);
        shape()->setAdditionalAttribute("text:anchor-page-number", QString::number(d->pageNumber));
        break;
    case Frame:
        shape()->setAdditionalAttribute("text:anchor-type", "frame");
        break;
    case Paragraph:
        shape()->setAdditionalAttribute("text:anchor-type", "paragraph");
        break;
    case Char:
        shape()->setAdditionalAttribute("text:anchor-type", "char");
        break;
    case AsChar:
        shape()->setAdditionalAttribute("text:anchor-type", "as-char");
        break;
    }

    QPointF offset = d->distance - shape()->absolutePosition(KoFlake::TopLeftCorner);
    context.addShapeOffset(shape(), QMatrix(1, 0, 0, 1, offset.x(), offset.y()));
    shape()->saveOdf(context);
    context.removeShapeOffset(shape());
}

bool KoTextAnchor::loadOdfFromShape()
{
    setOffset(shape()->position());
    shape()->setPosition(QPointF(0, 0));
    if (shape()->hasAdditionalAttribute("text:anchor-type")) {
        d->pageNumber = -1;
        QString anchorType = shape()->additionalAttribute("text:anchor-type");
        if (anchorType == "paragraph")
            d->anchorType = Paragraph;
        else if (anchorType == "page") {
            d->anchorType = Page;
            if (shape()->hasAdditionalAttribute("text:anchor-page-number"))
                d->pageNumber = shape()->additionalAttribute("text:anchor-page-number").toInt();
        } else if (anchorType == "frame")
            d->anchorType = Frame;
        else if (anchorType == "char")
            d->anchorType = Char;
        else if (anchorType == "as-char")
            d->anchorType = AsChar;
        return true;
    }
    return false;
}

int KoTextAnchor::pageNumber() const
{
    return d->pageNumber;
}
