/***************************************************************************
 *   Copyright 2007-2008 Wouter Haffmans <cheetah@simply-life.net>         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU General Public License as        *
 *   published by the Free Software Foundation; either version 2 of        *
 *   the License or (at your option) version 3 or any later version        *
 *   accepted by the membership of KDE e.V. (or its successor approved     *
 *   by the membership of KDE e.V.), which shall act as a proxy            *
 *   defined in Section 14 of version 3 of the license.                    *
 *                                                                         *
 *   This program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/

#include "canvas.h"

#include "canvasscene.h"
#include "graphicslinesitem.h"

#include <QGraphicsView>
#include <QGraphicsPathItem>
#include <QGraphicsLineItem>
#include <QPainterPath>
#include <QMouseEvent>
#include <QSvgGenerator>

#include <QtDebug>

// -----------------------------------------------------------------------------

// Constructor/Destructor
// -----------------------------------------------------------------------------
Canvas::Canvas(QWidget * /*parent*/)
{
    m_scene = new CanvasScene(this);
    m_scene->setBackgroundBrush(Qt::white);
    m_scene->setBackgroundStyle(CanvasScene::center);

    penUp();

    QPen pen;
    pen.setStyle(Qt::SolidLine);
    pen.setCapStyle(Qt::RoundCap);
    pen.setJoinStyle(Qt::RoundJoin);
    setPen(pen);
    m_currentZValue = 0;
    setTag(0);
}
// -----------------------------------------------------------------------------

Canvas::~Canvas()
{
}
// -----------------------------------------------------------------------------

// Property getters/setters
// -----------------------------------------------------------------------------
QBrush Canvas::backgroundBrush() const
{
    return m_scene->backgroundBrush();
}
// -----------------------------------------------------------------------------

void Canvas::setBackgroundBrush(QBrush backgroundBrush)
{
    m_scene->setBackgroundBrush(backgroundBrush);
    updateCache();
}
// -----------------------------------------------------------------------------

QImage Canvas::backgroundImage() const
{
    return m_scene->backgroundImage();
}
// -----------------------------------------------------------------------------

void Canvas::setBackgroundImage(QImage backgroundImage)
{
    m_scene->setBackgroundImage(backgroundImage);
    updateCache();
}
// -----------------------------------------------------------------------------

CanvasScene::BackgroundStyle Canvas::backgroundStyle() const
{
    return m_scene->backgroundStyle();
}
// -----------------------------------------------------------------------------

void Canvas::setBackgroundStyle(CanvasScene::BackgroundStyle style)
{
    m_scene->setBackgroundStyle(style);
    updateCache();
}
// -----------------------------------------------------------------------------

QList<GraphicsLinesItem *> Canvas::lines() const
{
    return m_lines;
}
// -----------------------------------------------------------------------------

void Canvas::setLines(QList<GraphicsLinesItem *> lines)
{
    clear();
    GraphicsLinesItem *line;
    foreach(line, lines) {
        this->storeLines(line);
    }
    updateCache();
    update();
}
// -----------------------------------------------------------------------------

QPen Canvas::pen() const
{
    return m_pen;
}
// -----------------------------------------------------------------------------

void Canvas::setPen(QPen pen)
{
    storeLines();
    m_pen = pen;
}
// -----------------------------------------------------------------------------

bool Canvas::isPenDown() const
{
    return m_isPenDown;
}
// -----------------------------------------------------------------------------

QImage Canvas::image() const
{
    // Render image
    QImage image(QSize(qRound(m_scene->sceneRect().width()),
            qRound(m_scene->sceneRect().height())), QImage::Format_RGB32);
    QPainter painter(&image);
    m_scene->render(&painter, image.rect(), m_scene->sceneRect());
    return QImage(image);
}
// -----------------------------------------------------------------------------

void Canvas::saveToFile(const QString fileName) const
{
    if (!m_scene->sceneRect().isNull()) {
        QSvgGenerator svg;
        svg.setSize(QSize(qRound(m_scene->sceneRect().width()),
                qRound(m_scene->sceneRect().height())));
        svg.setFileName(fileName);

        QPainter painter(&svg);
        m_scene->render(&painter,
                QRectF(0, 0, svg.size().width(), svg.size().height()),
                m_scene->sceneRect());
        painter.end();
    }
}
// -----------------------------------------------------------------------------

// Slot implementations
// -----------------------------------------------------------------------------
void Canvas::clear(QBrush backgroundBrush)
{
    setBackgroundBrush(backgroundBrush);
    clear();
}
// -----------------------------------------------------------------------------

void Canvas::clear()
{
    // Make internal copy of items list to iterate over, delete all items
    QList<QGraphicsItem *> items = m_scene->items();
    for (QList<QGraphicsItem *>::iterator i = items.begin();
         i != items.end(); ++i) {
        m_scene->removeItem(*i);
    }
    // Clear local
    QGraphicsItem *tmpLine;
    foreach(tmpLine, m_lines) {
        delete tmpLine;
    }
    m_lines.clear();
    m_currentZValue = 0;
    updateCache();
    update();
}
// -----------------------------------------------------------------------------

void Canvas::penUp()
{
    setPenDown(false);
}
// -----------------------------------------------------------------------------

void Canvas::penDown()
{
    setPenDown(true);
}
// -----------------------------------------------------------------------------

void Canvas::setPenDown(bool penDown)
{
    bool penWasDown = m_isPenDown;
    m_isPenDown = penDown;
    if (penDown && !penWasDown) {
        // Start new painting action
        drawLine(m_lastPoint);
    }
    else if (!penDown && penWasDown) {
        // End painting action
        storeLines();
    }
}
// -----------------------------------------------------------------------------

qint16 Canvas::tag() const
{
    return m_tag;
}
// -----------------------------------------------------------------------------

void Canvas::setTag(qint16 tag)
{
    m_tag = tag;
}
// -----------------------------------------------------------------------------

void Canvas::moveTo(QPoint pos)
{
    if (isPenDown()) {
        drawLine(pos);
    }
    else {
        // Simply store the position
        m_lastPoint = pos;
    }
}
// -----------------------------------------------------------------------------

// Painter functions
// -----------------------------------------------------------------------------
void Canvas::mouseMoveEvent(QMouseEvent *event)
{
    moveTo(event->pos());
}
// -----------------------------------------------------------------------------

void Canvas::paintEvent(QPaintEvent * /*event*/)
{
    QPainter painter(this);
    painter.drawPixmap(QPoint(0, 0), m_cacheImage);
}
// -----------------------------------------------------------------------------

void Canvas::drawLine(const QPointF &endPoint)
{
    QPointF previous;
    if (m_currentPoints.count() > 0) {
        previous = m_lastPoint;
    }
    else {
        previous = endPoint;
    }
    QPainter painter(&m_cacheImage);
    painter.setPen(pen());
    painter.drawLine(previous, endPoint);
    int rad = (pen().width() / 2) + 8;
    update(QRect(previous.toPoint(), endPoint.toPoint()).normalized()
            .adjusted(-rad, -rad, +rad, +rad));
    m_currentPoints.append(endPoint);
    m_lastPoint = endPoint;
}
// -----------------------------------------------------------------------------

void Canvas::storeLines()
{
    if (!m_currentPoints.isEmpty()) {
        // Convert linked list to vector
        QVector<QPointF> points;
        Q_FOREACH(const QPointF &point, m_currentPoints) {
            points.append(point);
        }
        // Create item
        GraphicsLinesItem *lines = new GraphicsLinesItem(points);
        lines->setTag(m_tag);
        lines->setPen(pen());
        storeLines(lines);
    }
}
// -----------------------------------------------------------------------------

void Canvas::storeLines(GraphicsLinesItem *lines)
{
    m_lines.append(lines);
    lines->setZValue(m_currentZValue);
    m_scene->addItem(lines);
    ++m_currentZValue;
    m_currentPoints.clear();
}
// -----------------------------------------------------------------------------

void Canvas::updateCache()
{
    if (!m_scene->sceneRect().isEmpty()) {
        QPixmap newImage(m_scene->sceneRect().size().toSize());
        QPainter painter(&newImage);
        m_scene->render(&painter, newImage.rect(), m_scene->sceneRect());
        m_cacheImage = newImage;
    }
}
// -----------------------------------------------------------------------------

void Canvas::adjustColors(QMap<qint16, QColor> colorMap)
{
    bool repaint = false;
    QList<GraphicsLinesItem *>::const_iterator i;
    QColor c;

    storeLines();

    for (i = m_lines.constBegin(); i != m_lines.constEnd(); ++i) {
        c = colorMap.value((*i)->tag());
        if (c.isValid()) {
            repaint = true;
            QPen p = (*i)->pen();
            p.setColor(c);
            (*i)->setPen(p);
        }
    }

    if (repaint) {
        updateCache();
        update();
    }
}
// -----------------------------------------------------------------------------

// Basics from Qt Scribble Example, scribblearea.cpp, modified
void Canvas::resizeEvent(QResizeEvent *event)
{
    if (width() > m_scene->width() || height() > m_scene->height()) {
        int newWidth = qRound(qMax((qreal)(width() + 32), m_scene->width()));
        int newHeight = qRound(qMax((qreal)(height() + 32), m_scene->height()));
        resizeImage(QSize(newWidth, newHeight));
        update();
    }
    QWidget::resizeEvent(event);
}
// -----------------------------------------------------------------------------

void Canvas::resizeImage(const QSize &newSize)
{
    if (QSize(qRound(m_scene->width()), qRound(m_scene->height())) == newSize)
        return;

    // Make sure scene is up to date
    storeLines();

    // Resize scene
    qreal dx = (newSize.width() - m_scene->width()) / 2;
    qreal dy = (newSize.height() - m_scene->height()) / 2;
    m_scene->setSceneRect(0, 0, newSize.width(), newSize.height());

    QList<QGraphicsItem *> items = m_scene->items();
    for (QList<QGraphicsItem *>::const_iterator i = items.constBegin();
         i != items.constEnd(); ++i) {
        (*i)->translate(dx, dy);
    }

    m_lastPoint.setX(m_lastPoint.x() + qRound(dx));
    m_lastPoint.setY(m_lastPoint.y() + qRound(dy));

    // Invalidate cache
    updateCache();
}
// -----------------------------------------------------------------------------

// Widget hints
// -----------------------------------------------------------------------------
QSize Canvas::sizeHint() const
{
    QSize hint(qRound(m_scene->width()), qRound(m_scene->height()));
    return hint;
}
// -----------------------------------------------------------------------------

QSize Canvas::minimumSizeHint() const
{
    return QSize(100, 100);
}
// -----------------------------------------------------------------------------

#include "canvas.moc"
