/****************************************************************************
**
** Copyright (C) 2005-2005 Trolltech AS. All rights reserved.
**
** This file is part of the example classes of the Qt Toolkit.
**
** This file may be used under the terms of the GNU General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
** http://www.trolltech.com/products/qt/opensource.html
**
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://www.trolltech.com/products/qt/licensing.html or contact the
** sales department at sales@trolltech.com.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include <math.h>
#include <QtGui>

#include "displayshape.h"

DisplayShape::DisplayShape(const QPointF &position, const QSizeF &maxSize)
    : pos(position), maxSize(maxSize), interactive(false)
{
}

bool DisplayShape::animate()
{
    if (!targetPos.isNull()) {
        QLineF displacement(pos, targetPos);
        QPointF newPosition = displacement.pointAt(0.25);
        if (displacement.length() <= 1.0) {
            pos = targetPos;
            targetPos = QPointF();
        } else {
            pos = newPosition;
        }
        return true;
    }

    return false;
}

bool DisplayShape::contains(const QString &key) const
{
    return meta.contains(key);
}

bool DisplayShape::isInteractive() const
{
    return interactive;
}

QVariant DisplayShape::metaData(const QString &key) const
{
    return meta.value(key);
}

void DisplayShape::paint(QPainter *painter) const
{
    painter->save();
    painter->drawImage(pos, image);
    painter->restore();
}

QPointF DisplayShape::position() const
{
    return pos;
}

QRectF DisplayShape::rect() const
{
    return QRectF(pos, image.size());
}

void DisplayShape::removeMetaData(const QString &key)
{
    meta.remove(key);
}

void DisplayShape::setInteractive(bool enable)
{
    interactive = enable;
}

void DisplayShape::setMetaData(const QString &key, const QVariant &value)
{
    meta[key] = value;
}

void DisplayShape::setPosition(const QPointF &position)
{
    pos = position;
}

void DisplayShape::setTarget(const QPointF &position)
{
    targetPos = position;
}

QSizeF DisplayShape::size() const
{
    return maxSize;
}

QPointF DisplayShape::target() const
{
    return targetPos;
}

PanelShape::PanelShape(const QPainterPath &path, const QBrush &normal,
                       const QBrush &highlighted, const QPen &pen,
                       const QPointF &position, const QSizeF &maxSize)
    : DisplayShape(position, maxSize), highlightedBrush(highlighted),
      normalBrush(normal), path(path), pen(pen)
{
    brush = normalBrush;
}

bool PanelShape::animate()
{
    bool updated = false;

    if (!meta.contains("destroy")) {
        if (meta.contains("fade")) {
            QColor penColor = pen.color();
            QColor brushColor = brush.color();
            int penAlpha = penColor.alpha();
            int brushAlpha = brushColor.alpha();
            int fadeMinimum = meta.value("fade minimum").toInt();

            if (penAlpha != fadeMinimum || brushAlpha != fadeMinimum
                || meta.value("fade").toInt() > 0) {

                penAlpha = qBound(fadeMinimum,
                                  penAlpha + meta.value("fade").toInt(), 255);
                brushAlpha = qBound(fadeMinimum,
                                  brushAlpha + meta.value("fade").toInt(), 255);

                penColor.setAlpha(penAlpha);
                brushColor.setAlpha(brushAlpha);
                pen.setColor(penColor);
                brush.setColor(brushColor);

                if (penAlpha == 0 && brushAlpha == 0) {
                    meta["destroy"] = true;
                    meta.remove("fade");
                } else if (penAlpha == 255 && brushAlpha == 255)
                    meta.remove("fade");

                updated = true;
            }
        } else if (meta.contains("highlight")) {
            qreal scale = meta.value("highlight scale").toDouble();
            QColor color = brush.color();

            if (meta.value("highlight").toBool())
                scale = qBound(0.0, scale + 0.5, 1.0);
            else
                scale = qBound(0.0, scale - 0.2, 1.0);

            if (scale == 0.0) {
                brush = normalBrush;
                meta.remove("highlight");
                meta.remove("highlight scale");
                updated = true;
            } else if (scale != meta["highlight scale"]) {
                meta["highlight scale"] = scale;

                if (scale == 1.0)
                    brush = highlightedBrush;
                else {
                    QColor normal = normalBrush.color();
                    QColor highlighted = highlightedBrush.color();

                    color.setRedF((1.0-scale) * normal.redF()
                                  + scale*highlighted.redF());
                    color.setGreenF((1.0-scale) * normal.greenF()
                                    + scale*highlighted.greenF());
                    color.setBlueF((1.0-scale) * normal.blueF()
                                   + scale*highlighted.blueF());
                    brush.setColor(color);
                }
                updated = true;
            }
        }
    }

    return DisplayShape::animate() || updated;
}

void PanelShape::paint(QPainter *painter) const
{
    painter->save();
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setBrush(brush);
    painter->setPen(pen);
    painter->translate(pos);
    painter->drawPath(path);
    painter->restore();
}

QRectF PanelShape::rect() const
{
    return QRectF(pos + path.boundingRect().topLeft(),
                  path.boundingRect().size());
}

TitleShape::TitleShape(const QString &text, const QFont &f,
                       const QPen &pen, const QPointF &position,
                       const QSizeF &maxSize, Qt::Alignment alignment)
    : DisplayShape(position, maxSize), font(f), text(text), pen(pen),
      alignment(alignment)
{
    QFontMetricsF fm(font);
    textRect = fm.boundingRect(QRectF(QPointF(0, 0), maxSize), alignment, text);

    qreal textWidth = qMax(fm.width(text), textRect.width());
    qreal textHeight = qMax(fm.height(), textRect.height());

    qreal scale = qMin(maxSize.width()/textWidth,
                       maxSize.height()/textHeight);

    font.setPointSizeF(font.pointSizeF() * scale);
    fm = QFontMetricsF(font);
    textRect = fm.boundingRect(QRectF(QPointF(0, 0), maxSize), alignment, text);
}

bool TitleShape::animate()
{
    bool updated = false;

    if (!meta.contains("destroy")) {
        if (meta.contains("fade")) {
            QColor penColor = pen.color();
            int penAlpha = penColor.alpha();

            penAlpha = qBound(meta.value("fade minimum").toInt(),
                              penAlpha + meta.value("fade").toInt(), 255);

            penColor.setAlpha(penAlpha);
            pen.setColor(penColor);

            if (penAlpha == 0) {
                meta["destroy"] = true;
                meta.remove("fade");
            } else if (penAlpha == 255)
                meta.remove("fade");

            updated = true;
        }
    }

    return DisplayShape::animate() || updated;
}

void TitleShape::paint(QPainter *painter) const
{
    QRectF rect(textRect);
    rect.translate(pos);
    painter->save();
    painter->setRenderHint(QPainter::TextAntialiasing);
    painter->setPen(pen);
    painter->setFont(font);
    painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, text);
    painter->restore();
}

QRectF TitleShape::rect() const
{
    QRectF rect(textRect);
    return rect.translated(pos);
}

ImageShape::ImageShape(const QImage &original, const QPointF &position,
                       const QSizeF &maxSize, int alpha,
                       Qt::Alignment alignment)
    : DisplayShape(position, maxSize), alpha(alpha), alignment(alignment)
{
    source = original.convertToFormat(QImage::Format_ARGB32_Premultiplied);
    scale = qMin(qMin(maxSize.width()/source.width(),
                      maxSize.height()/source.height()), 1.0);
    image = QImage(int(ceil(scale * source.width())),
                   int(ceil(scale * source.height())),
                   QImage::Format_ARGB32_Premultiplied);

    offset = QPointF(0.0, 0.0);

    if (alignment & Qt::AlignHCenter)
        offset.setX((maxSize.width() - image.width())/2);
    else if (alignment & Qt::AlignRight)
        offset.setX(maxSize.width() - image.width());

    if (alignment & Qt::AlignVCenter)
        offset.setY((maxSize.height() - image.height())/2);
    else if (alignment & Qt::AlignBottom)
        offset.setY(maxSize.height() - image.height());

    redraw();
}

void ImageShape::redraw()
{
    image.fill(qRgba(alpha, alpha, alpha, alpha));

    QPainter painter;
    painter.begin(&image);
    painter.setRenderHint(QPainter::SmoothPixmapTransform);
    painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
    painter.scale(scale, scale);
    painter.drawImage(0, 0, source);
    painter.end();
}

void ImageShape::paint(QPainter *painter) const
{
    painter->drawImage(pos + offset, image);
}

QRectF ImageShape::rect() const
{
    return QRectF(pos, maxSize);
}

bool ImageShape::animate()
{
    bool updated = false;

    if (!meta.contains("destroy")) {
        if (meta.contains("fade")) {
            alpha = qBound(meta.value("fade minimum").toInt(),
                           alpha + meta.value("fade").toInt(), 255);
            redraw();

            if (alpha == 0) {
                meta["destroy"] = true;
                meta.remove("fade");
            } else if (alpha == 255)
                meta.remove("fade");

            updated = true;
        }
    }

    return DisplayShape::animate() || updated;
}

DocumentShape::DocumentShape(const QString &text, const QFont &f,
                       const QPen &pen, const QPointF &position,
                       const QSizeF &maxSize)
    : DisplayShape(position, maxSize), font(f), pen(pen)
{
    paragraphs = text.split("\n", QString::SkipEmptyParts);

    QFontMetricsF fm(font);
    qreal scale = qMax(maxSize.height()/(fm.lineSpacing()*20), 1.0);
    font.setPointSizeF(font.pointSizeF() * scale);

    qreal oldHeight = maxSize.height();
    qreal height = formatText();
    if (height > oldHeight) {
        font.setPointSizeF(font.pointSizeF() * oldHeight/height);
        formatText();
    }
}

DocumentShape::~DocumentShape()
{
    qDeleteAll(layouts);
    layouts.clear();
}

bool DocumentShape::animate()
{
    bool updated = false;

    if (!meta.contains("destroy")) {
        if (meta.contains("fade")) {
            QColor penColor = pen.color();
            int penAlpha = penColor.alpha();

            penAlpha = qBound(meta.value("fade minimum").toInt(),
                              penAlpha + meta.value("fade").toInt(), 255);

            penColor.setAlpha(penAlpha);
            pen.setColor(penColor);

            if (penAlpha == 0) {
                meta["destroy"] = true;
                meta.remove("fade");
            } else if (penAlpha == 255)
                meta.remove("fade");

            updated = true;
        }
    }

    return DisplayShape::animate() || updated;
}

qreal DocumentShape::formatText()
{
    qDeleteAll(layouts);
    layouts.clear();

    QFontMetricsF fm(font);
    qreal lineHeight = fm.height();
    qreal y = 0.0;
    qreal leftMargin = 0.0;
    qreal rightMargin = maxSize.width();

    foreach (QString paragraph, paragraphs) {

        QTextLayout *textLayout = new QTextLayout(paragraph, font);
        textLayout->beginLayout();

        while (true) {
            QTextLine line = textLayout->createLine();
            if (!line.isValid())
                break;

            line.setLineWidth(rightMargin - leftMargin);
            line.setPosition(QPointF(leftMargin, y));
            y += line.height();
        }

        textLayout->endLayout();
        layouts.append(textLayout);

        y += lineHeight;
    }

    maxSize.setHeight(y);
    return y;
}

void DocumentShape::paint(QPainter *painter) const
{
    painter->save();
    painter->setRenderHint(QPainter::TextAntialiasing);
    painter->setPen(pen);
    painter->setFont(font);
    foreach (QTextLayout *layout, layouts)
        layout->draw(painter, pos);
    painter->restore();
}

QRectF DocumentShape::rect() const
{
    return QRectF(pos, maxSize);
}
