/************************************************************************
 *
 * Copyright 2013 Jakob Leben (jakob.leben@gmail.com)
 *
 * This file is part of SuperCollider Qt GUI.
 *
 * 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) any later version.
 *
 * 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/>.
 *
 ************************************************************************/

#pragma once

#include "../image.h"
#include "../debug.h"

#include <QPainter>

namespace QtCollider {

struct ImagePainter {
    enum HorizontalMode {
        AlignLeft,
        AlignHCenter,
        AlignRight,
        TileHorizontally,
        StretchHorizontally,
    };

    enum VerticalMode { AlignTop, AlignVCenter, AlignBottom, TileVertically, StretchVertically };

    SharedImage image;
    QRectF sourceRect;
    HorizontalMode horizontalMode;
    VerticalMode verticalMode;
    bool scaleToFit;
    qreal opacity;

    ImagePainter(): horizontalMode(AlignLeft), verticalMode(AlignTop), scaleToFit(false), opacity(1.0) {}

    bool isValid() const { return !image.isNull(); }

    void setImage(const SharedImage& image, const QRectF& rect = QRectF(), int tileMode = 1, qreal opacity = 1.0) {
        this->image = image;
        this->sourceRect = rect;
        this->opacity = opacity;
        setTileMode(tileMode);
    }

    void clear() { image.clear(); }

    void paint(QPainter* painter, const QRectF& targetRect) {
        if (!image)
            return;

        if (image->isPainting()) {
            qcErrorMsg("Can not draw image while being painted.");
            return;
        }

        const QPixmap& pixmap = image->pixmap();

        if (sourceRect.isNull() || targetRect.isNull())
            return;

        painter->save();
        painter->setOpacity(opacity);
        painter->setRenderHint(QPainter::SmoothPixmapTransform, image->transformationMode == Qt::SmoothTransformation);

        QRectF rect = sourceRect;
        qreal ratio = pixmap.devicePixelRatio();
        rect.setWidth(rect.width() / ratio);
        rect.setHeight(rect.height() / ratio);

        if (horizontalMode == StretchHorizontally) {
            rect.moveLeft(targetRect.left());
            rect.setWidth(targetRect.width());
        }
        if (verticalMode == StretchVertically) {
            rect.moveTop(targetRect.top());
            rect.setHeight(targetRect.height());
        }
        if (horizontalMode != StretchHorizontally && verticalMode != StretchVertically && scaleToFit) {
            float aspect_ratio = rect.width() / rect.height();
            rect.setHeight(targetRect.height());
            rect.setWidth(rect.height() * aspect_ratio);
            if (rect.width() > targetRect.width()) {
                rect.setWidth(targetRect.width());
                rect.setHeight(rect.width() / aspect_ratio);
            }
        }

        switch (horizontalMode) {
        case AlignLeft:
        case TileHorizontally:
            rect.moveLeft(targetRect.left());
            break;
        case AlignHCenter:
            rect.moveLeft(targetRect.left() + targetRect.width() / 2 - rect.width() / 2);
            break;
        case AlignRight:
            rect.moveRight(targetRect.right());
            break;
        default:
            break;
        };

        switch (verticalMode) {
        case AlignTop:
        case TileVertically:
            rect.moveTop(targetRect.top());
            break;
        case AlignVCenter:
            rect.moveTop(targetRect.top() + targetRect.height() / 2 - rect.height() / 2);
            break;
        case AlignRight:
            rect.moveBottom(targetRect.bottom());
            break;
        default:
            break;
        };

        bool tileVertically = verticalMode == TileVertically;
        bool tileHorizontally = horizontalMode == TileHorizontally;

        qreal y_origin = rect.top();
        do {
            do {
                painter->drawPixmap(rect, pixmap, sourceRect);

                if (tileVertically)
                    rect.moveTop(rect.top() + rect.height());
                else
                    break;
            } while (rect.top() <= targetRect.bottom());

            if (tileHorizontally) {
                rect.moveTop(y_origin);
                rect.moveLeft(rect.left() + rect.width());
            } else
                break;
        } while (rect.left() <= targetRect.right());

        painter->restore();
    }

    void setTileMode(const int mode) {
        /*
            modes :
            1 - fixed to left, fixed to top
            2 - horizontally tile, fixed to top
            3 - fixed to right, fixed to top
            4 - fixed to left, vertically tile
            5 - horizontally tile, vertically tile
            6 - fixed to right, vertically tile
            7 - fixed to left, fixed to bottom
            8 - horizontally tile, fixed to bottom
            9 - fixed to right, fixed to bottom
            10 - fit
            11 - center, center (scale)
            12 - center , fixed to top
            13 - center , fixed to bottom
            14 - fixed to left, center
            15 - fixed to right, center
            16 - center, center (no scale)
        */

        int mode_map_index = mode - 1;
        if (mode_map_index < 0 || mode_map_index >= 16)
            return;

        static int mode_map[16][2] = { { AlignLeft, AlignTop },
                                       { TileHorizontally, AlignTop },
                                       { AlignRight, AlignTop },
                                       { AlignLeft, TileVertically },
                                       { TileHorizontally, TileVertically },
                                       { AlignRight, TileVertically },
                                       { AlignLeft, AlignBottom },
                                       { TileHorizontally, AlignBottom },
                                       { AlignRight, AlignBottom },
                                       { StretchHorizontally, StretchVertically },
                                       { AlignHCenter, AlignVCenter },
                                       { AlignHCenter, AlignTop },
                                       { AlignHCenter, AlignBottom },
                                       { AlignLeft, AlignVCenter },
                                       { AlignRight, AlignVCenter },
                                       { AlignHCenter, AlignVCenter } };

        horizontalMode = (HorizontalMode)mode_map[mode_map_index][0];
        verticalMode = (VerticalMode)mode_map[mode_map_index][1];
        scaleToFit = (mode == 11);
    }
};

} // namespace QtCollider
