/*
    SPDX-FileCopyrightText: 2009-2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
    SPDX-FileCopyrightText: 2008 Long Huynh Huu <long.upcase@googlemail.com>
    SPDX-FileCopyrightText: 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>

    SPDX-License-Identifier: LGPL-2.0-only
*/

#include "oxygentileset.h"

#include <QPainter>

namespace Oxygen
{

//___________________________________________________________
inline bool bits(TileSet::Tiles flags, TileSet::Tiles testFlags)
{
    return (flags & testFlags) == testFlags;
}

//______________________________________________________________________________________
inline qreal devicePixelRatio(const QPixmap &pixmap)
{
    return pixmap.devicePixelRatio();
}

//______________________________________________________________________________________
inline void setDevicePixelRatio(QPixmap &pixmap, qreal value)
{
    return pixmap.setDevicePixelRatio(value);
}

//______________________________________________________________
int TileSet::_sideExtent = 32;

//______________________________________________________________
void TileSet::initPixmap(PixmapList &pixmaps, const QPixmap &source, int width, int height, const QRect &rect)
{
    QSize size(width, height);
    if (!(size.isValid() && rect.isValid())) {
        pixmaps.append(QPixmap());

    } else if (size != rect.size()) {
        const qreal dpiRatio(devicePixelRatio(source));
        const QRect scaledRect(rect.topLeft() * dpiRatio, rect.size() * dpiRatio);
        const QSize scaledSize(size * dpiRatio);
        const QPixmap tile(source.copy(scaledRect));
        QPixmap pixmap(scaledSize);

        pixmap.fill(Qt::transparent);
        QPainter painter(&pixmap);
        painter.drawTiledPixmap(0, 0, scaledSize.width(), scaledSize.height(), tile);
        setDevicePixelRatio(pixmap, dpiRatio);
        pixmaps.append(pixmap);

    } else {
        const qreal dpiRatio(devicePixelRatio(source));
        const QRect scaledRect(rect.topLeft() * dpiRatio, rect.size() * dpiRatio);
        QPixmap pixmap(source.copy(scaledRect));
        setDevicePixelRatio(pixmap, dpiRatio);
        pixmaps.append(pixmap);
    }
}

//______________________________________________________________
TileSet::TileSet(void)
    : _w1(0)
    , _h1(0)
    , _w3(0)
    , _h3(0)
{
    _pixmaps.reserve(9);
}

//______________________________________________________________
TileSet::TileSet(const QPixmap &source, int w1, int h1, int w2, int h2)
    : _w1(w1)
    , _h1(h1)
    , _w3(0)
    , _h3(0)
{
    _pixmaps.reserve(9);
    if (source.isNull())
        return;

    _w3 = source.width() / devicePixelRatio(source) - (w1 + w2);
    _h3 = source.height() / devicePixelRatio(source) - (h1 + h2);
    int w = w2;
    int h = h2;

    // initialise pixmap array
    initPixmap(_pixmaps, source, _w1, _h1, QRect(0, 0, _w1, _h1));
    initPixmap(_pixmaps, source, w, _h1, QRect(_w1, 0, w2, _h1));
    initPixmap(_pixmaps, source, _w3, _h1, QRect(_w1 + w2, 0, _w3, _h1));
    initPixmap(_pixmaps, source, _w1, h, QRect(0, _h1, _w1, h2));
    initPixmap(_pixmaps, source, w, h, QRect(_w1, _h1, w2, h2));
    initPixmap(_pixmaps, source, _w3, h, QRect(_w1 + w2, _h1, _w3, h2));
    initPixmap(_pixmaps, source, _w1, _h3, QRect(0, _h1 + h2, _w1, _h3));
    initPixmap(_pixmaps, source, w, _h3, QRect(_w1, _h1 + h2, w2, _h3));
    initPixmap(_pixmaps, source, _w3, _h3, QRect(_w1 + w2, _h1 + h2, _w3, _h3));
}

//______________________________________________________________
TileSet::TileSet(const QPixmap &source, int w1, int h1, int w3, int h3, int x1, int y1, int w2, int h2)
    : _w1(w1)
    , _h1(h1)
    , _w3(w3)
    , _h3(h3)
{
    _pixmaps.reserve(9);
    if (source.isNull())
        return;

    int x2 = source.width() / devicePixelRatio(source) - _w3;
    int y2 = source.height() / devicePixelRatio(source) - _h3;
    int w = w2;
    int h = h2;

    // initialise pixmap array
    initPixmap(_pixmaps, source, _w1, _h1, QRect(0, 0, _w1, _h1));
    initPixmap(_pixmaps, source, w, _h1, QRect(x1, 0, w2, _h1));
    initPixmap(_pixmaps, source, _w3, _h1, QRect(x2, 0, _w3, _h1));
    initPixmap(_pixmaps, source, _w1, h, QRect(0, y1, _w1, h2));
    initPixmap(_pixmaps, source, w, h, QRect(x1, y1, w2, h2));
    initPixmap(_pixmaps, source, _w3, h, QRect(x2, y1, _w3, h2));
    initPixmap(_pixmaps, source, _w1, _h3, QRect(0, y2, _w1, _h3));
    initPixmap(_pixmaps, source, w, _h3, QRect(x1, y2, w2, _h3));
    initPixmap(_pixmaps, source, _w3, _h3, QRect(x2, y2, _w3, _h3));
}

//___________________________________________________________
QRect TileSet::adjust(const QRect &constRect, Tiles tiles) const
{
    // adjust rect to deal with missing edges
    QRect rect(constRect);
    if (!(tiles & Left))
        rect.adjust(-_w1, 0, 0, 0);
    if (!(tiles & Right))
        rect.adjust(0, 0, _w3, 0);
    if (!(tiles & Top))
        rect.adjust(0, -_h1, 0, 0);
    if (!(tiles & Bottom))
        rect.adjust(0, 0, 0, _h3);

    return rect;
}

//___________________________________________________________
void TileSet::render(const QRect &constRect, QPainter *painter, Tiles tiles) const
{
    const bool oldHint(painter->testRenderHint(QPainter::SmoothPixmapTransform));
    painter->setRenderHint(QPainter::SmoothPixmapTransform, true);

    // check initialization
    if (_pixmaps.size() < 9)
        return;

    // copy source rect
    QRect rect(constRect);

    // get rect dimensions
    int x0, y0, w, h;
    rect.getRect(&x0, &y0, &w, &h);

    // calculate pixmaps widths
    int wLeft(0);
    int wRight(0);
    if (_w1 + _w3 > 0) {
        qreal wRatio(qreal(_w1) / qreal(_w1 + _w3));
        wLeft = (tiles & Right) ? qMin(_w1, int(w * wRatio)) : _w1;
        wRight = (tiles & Left) ? qMin(_w3, int(w * (1.0 - wRatio))) : _w3;
    }

    // calculate pixmap heights
    int hTop(0);
    int hBottom(0);
    if (_h1 + _h3 > 0) {
        qreal hRatio(qreal(_h1) / qreal(_h1 + _h3));
        hTop = (tiles & Bottom) ? qMin(_h1, int(h * hRatio)) : _h1;
        hBottom = (tiles & Top) ? qMin(_h3, int(h * (1.0 - hRatio))) : _h3;
    }

    // calculate corner locations
    w -= wLeft + wRight;
    h -= hTop + hBottom;
    const int x1 = x0 + wLeft;
    const int x2 = x1 + w;
    const int y1 = y0 + hTop;
    const int y2 = y1 + h;

    const int w2 = _pixmaps.at(7).width() / devicePixelRatio(_pixmaps.at(7));
    const int h2 = _pixmaps.at(5).height() / devicePixelRatio(_pixmaps.at(5));

    // corner
    if (bits(tiles, Top | Left))
        painter->drawPixmap(x0, y0, _pixmaps.at(0), 0, 0, wLeft * devicePixelRatio(_pixmaps.at(0)), hTop * devicePixelRatio(_pixmaps.at(0)));
    if (bits(tiles, Top | Right))
        painter->drawPixmap(x2,
                            y0,
                            _pixmaps.at(2),
                            (_w3 - wRight) * devicePixelRatio(_pixmaps.at(2)),
                            0,
                            wRight * devicePixelRatio(_pixmaps.at(2)),
                            hTop * devicePixelRatio(_pixmaps.at(2)));
    if (bits(tiles, Bottom | Left))
        painter->drawPixmap(x0,
                            y2,
                            _pixmaps.at(6),
                            0,
                            (_h3 - hBottom) * devicePixelRatio(_pixmaps.at(6)),
                            wLeft * devicePixelRatio(_pixmaps.at(6)),
                            hBottom * devicePixelRatio(_pixmaps.at(6)));
    if (bits(tiles, Bottom | Right))
        painter->drawPixmap(x2,
                            y2,
                            _pixmaps.at(8),
                            (_w3 - wRight) * devicePixelRatio(_pixmaps.at(8)),
                            (_h3 - hBottom) * devicePixelRatio(_pixmaps.at(8)),
                            wRight * devicePixelRatio(_pixmaps.at(8)),
                            hBottom * devicePixelRatio(_pixmaps.at(8)));

    // top and bottom
    if (w > 0) {
        if (tiles & Top)
            painter->drawPixmap(x1, y0, w, hTop, _pixmaps.at(1), 0, 0, w2 * devicePixelRatio(_pixmaps.at(1)), hTop * devicePixelRatio(_pixmaps.at(1)));
        if (tiles & Bottom)
            painter->drawPixmap(x1,
                                y2,
                                w,
                                hBottom,
                                _pixmaps.at(7),
                                0,
                                (_h3 - hBottom) * devicePixelRatio(_pixmaps.at(7)),
                                w2 * devicePixelRatio(_pixmaps.at(7)),
                                hBottom * devicePixelRatio(_pixmaps.at(7)));
    }

    // left and right
    if (h > 0) {
        if (tiles & Left)
            painter->drawPixmap(x0, y1, wLeft, h, _pixmaps.at(3), 0, 0, wLeft * devicePixelRatio(_pixmaps.at(3)), h2 * devicePixelRatio(_pixmaps.at(3)));
        if (tiles & Right)
            painter->drawPixmap(x2,
                                y1,
                                wRight,
                                h,
                                _pixmaps.at(5),
                                (_w3 - wRight) * devicePixelRatio(_pixmaps.at(5)),
                                0,
                                wRight * devicePixelRatio(_pixmaps.at(5)),
                                h2 * devicePixelRatio(_pixmaps.at(5)));
    }

    // center
    if ((tiles & Center) && h > 0 && w > 0)
        painter->drawPixmap(x1, y1, w, h, _pixmaps.at(4));

    // restore
    painter->setRenderHint(QPainter::SmoothPixmapTransform, oldHint);
}
}
