
// Own header
#include "mythimage.h"

// C++ headers
#include <stdint.h>

// QT headers
#include <QPainter>
#include <QMatrix>
#include <QRgb>

// Mythdb headers
#include "mythverbose.h"

// Myth headers
#include "remotefile.h"

// MythUI headers
#include "mythuihelper.h"
#include "mythmainwindow.h"

MythUIHelper *MythImage::m_ui = NULL;

MythImage::MythImage(MythPainter *parent)
{
    if (!parent)
        VERBOSE(VB_IMPORTANT, "ERROR: Image created without parent!");

    m_Parent = parent;
    m_RefCount = 0;

    m_Changed = false;

    m_isGradient = false;
    m_gradBegin = QColor("#000000");
    m_gradEnd = QColor("#FFFFFF");
    m_gradAlpha = 255;
    m_gradDirection = FillTopToBottom;

    m_isReflected = false;

    m_imageId = 0;

    m_FileName = "";

    m_cached = false;
    if (!m_ui)
        m_ui = GetMythUI();
}

MythImage::~MythImage()
{
    m_Parent->DeleteFormatImage(this);
}

void MythImage::UpRef(void)
{
    QMutexLocker locker(&m_RefCountLock);
    if (m_ui && m_cached && m_RefCount == 1)
        m_ui->ExcludeFromCacheSize(this);
    m_RefCount++;
}

bool MythImage::DownRef(void)
{
    m_RefCountLock.lock();
    m_RefCount--;
    if (m_ui && m_cached)
    {
        if (m_RefCount == 1)
            m_ui->IncludeInCacheSize(this);
        else if (m_RefCount == 0)
            m_ui->ExcludeFromCacheSize(this);
    }

    if (m_RefCount <= 0)
    {
        m_RefCountLock.unlock();
        delete this;
        return true;
    }
    m_RefCountLock.unlock();
    return false;
}

int MythImage::RefCount(void)
{
    QMutexLocker locker(&m_RefCountLock);
    return m_RefCount;
}

void MythImage::SetIsInCache(bool bCached)
{
    QMutexLocker locker(&m_RefCountLock);
    if (m_ui && m_RefCount == 1)
    {
        if (!m_cached && bCached)
            m_ui->IncludeInCacheSize(this);
        else if (m_cached && !bCached)
            m_ui->ExcludeFromCacheSize(this);
    }
    m_cached = bCached;
}

void MythImage::Assign(const QImage &img)
{
    QMutexLocker locker(&m_RefCountLock);
    if (m_ui && m_RefCount == 1 && m_cached)
        m_ui->ExcludeFromCacheSize(this);
    *(static_cast<QImage *> (this)) = img;
    if (m_ui && m_RefCount == 1 && m_cached)
        m_ui->IncludeInCacheSize(this);
    SetChanged();
}

void MythImage::Assign(const QPixmap &pix)
{
    Assign(pix.toImage());
}

void MythImage::Resize(const QSize &newSize, bool preserveAspect)
{
    if ((size() == newSize) && !isNull())
        return;

    if (m_isGradient)
    {
        *(static_cast<QImage *> (this)) = QImage(newSize, QImage::Format_ARGB32);
        MakeGradient(*this, m_gradBegin, m_gradEnd, m_gradAlpha, m_gradDirection);
        SetChanged();
    }
    else
    {
        Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio;
        if (preserveAspect)
            mode = Qt::KeepAspectRatio;

        Assign(scaled(newSize, mode, Qt::SmoothTransformation));
    }
}

void MythImage::Reflect(ReflectAxis axis, int shear, int scale, int length,
                        int spacing)
{
    if (m_isReflected)
        return;

    QImage mirrorImage;
    FillDirection fillDirection = FillTopToBottom;
    if (axis == ReflectVertical)
    {
        mirrorImage = mirrored(false,true);
        if (length < 100)
        {
            int height = (int)((float)mirrorImage.height() * (float)length/100);
            mirrorImage = mirrorImage.copy(0,0,mirrorImage.width(),height);
        }
        fillDirection = FillTopToBottom;
    }
    else if (axis == ReflectHorizontal)
    {
        mirrorImage = mirrored(true,false);
        if (length < 100)
        {
            int width = (int)((float)mirrorImage.width() * (float)length/100);
            mirrorImage = mirrorImage.copy(0,0,width,mirrorImage.height());
        }
        fillDirection = FillLeftToRight;
    }

    QImage alphaChannel(mirrorImage.size(), QImage::Format_ARGB32);
    MakeGradient(alphaChannel, QColor("#AAAAAA"), QColor("#000000"), 255,
                 false, fillDirection);
    mirrorImage.setAlphaChannel(alphaChannel);

    QMatrix shearMatrix;
    if (axis == ReflectVertical)
    {
        shearMatrix.scale(1,(float)scale/100);
        shearMatrix.shear((float)shear/100,0);
    }
    else if (axis == ReflectHorizontal)
    {
        shearMatrix.scale((float)scale/100,1);
        shearMatrix.shear(0,(float)shear/100);
    }

    mirrorImage = mirrorImage.transformed(shearMatrix, Qt::SmoothTransformation);

    QSize newsize;
    if (axis == ReflectVertical)
        newsize = QSize(mirrorImage.width(), height()+spacing+mirrorImage.height());
    else if (axis == ReflectHorizontal)
        newsize = QSize(width()+spacing+mirrorImage.width(), mirrorImage.height());

    QImage temp(newsize, QImage::Format_ARGB32);
    temp.fill(Qt::transparent);

    QPainter newpainter(&temp);
    newpainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
    if (axis == ReflectVertical)
    {
        if (shear < 0)
            newpainter.drawImage(mirrorImage.width()-width(), 0,
                                 copy(0,0,width(),height()));
        else
            newpainter.drawImage(0, 0, copy(0,0,width(),height()));

        newpainter.drawImage(0, height()+spacing, mirrorImage);
    }
    else if (axis == ReflectHorizontal)
    {
        if (shear < 0)
            newpainter.drawImage(0, mirrorImage.height()-height(),
                                 copy(0,0,width(),height()));
        else
            newpainter.drawImage(0, 0, copy(0,0,width(),height()));

        newpainter.drawImage(width()+spacing, 0, mirrorImage);
    }

    newpainter.end();

    Assign(temp);

    m_isReflected = true;
}

void MythImage::ToGreyscale()
{
    if (isGrayscale())
        return;

    for (int y = 0; y < height(); ++y)
    {
        for (int x = 0; x < width(); ++x)
        {
            QRgb oldPixel = pixel(x, y);
            int greyVal = qGray(oldPixel);
            setPixel(x, y, qRgba(greyVal, greyVal, greyVal, qAlpha(oldPixel)));
        }
    }
}

MythImage *MythImage::FromQImage(QImage **img)
{
    if (!img || !*img)
        return NULL;

    MythImage *ret = GetMythPainter()->GetFormatImage();
    ret->Assign(**img);
    delete *img;
    *img = NULL;
    return ret;
}

// FIXME: Get rid of LoadScaleImage
bool MythImage::Load(const QString &filename, bool scale)
{
    QImage *im = NULL;
    if (scale)
        im = GetMythUI()->LoadScaleImage(filename);
    else
    {
        if (filename.startsWith("myth://"))
        {
            im = new QImage();
            RemoteFile *rf = new RemoteFile(filename, false, false, 0);

            QByteArray data;
            bool ret = rf->SaveAs(data);

            delete rf;

            if (ret)
                im->loadFromData(data);
//            else
//                VERBOSE(VB_GENERAL, QString("MythImage::Load failed to load remote image %1").arg(filename));

        }
        else
        {
            im = new QImage(filename);
        }
    }

    SetFileName(filename);
    if (im)
    {
        Assign(*im);
        delete im;
        return true;
    }

    return false;
}

void MythImage::MakeGradient(QImage &image, const QColor &begin,
                             const QColor &end, int alpha, bool drawBoundary,
                             FillDirection direction)
{
    // Gradient fill colours
    QColor startColor = begin;
    QColor endColor = end;
    startColor.setAlpha(alpha);
    endColor.setAlpha(alpha);

    // Define Gradient
    QPoint pointA(0,0);
    QPoint pointB;
    if (direction == FillTopToBottom)
    {
        pointB = QPoint(0,image.height());
    }
    else if (direction == FillLeftToRight)
    {
        pointB = QPoint(image.width(),0);
    }

    QLinearGradient gradient(pointA, pointB);
    gradient.setColorAt(0, startColor);
    gradient.setColorAt(1, endColor);

    // Draw Gradient
    QPainter painter(&image);
    painter.setCompositionMode(QPainter::CompositionMode_Source);
    painter.fillRect(0, 0, image.width(), image.height(), gradient);

    if (drawBoundary)
    {
        // Draw boundary rect
        QColor black(0, 0, 0, alpha);
        painter.setPen(black);
        QPen pen = painter.pen();
        pen.setWidth(1);
        painter.drawRect(image.rect());
    }
    painter.end();
}

MythImage *MythImage::Gradient(const QSize & size, const QColor &begin,
                               const QColor &end, uint alpha,
                               FillDirection direction)
{
    QImage img(size.width(), size.height(), QImage::Format_ARGB32);

    MakeGradient(img, begin, end, alpha, true, direction);

    MythImage *ret = GetMythPainter()->GetFormatImage();
    ret->Assign(img);
    ret->m_isGradient = true;
    ret->m_gradBegin = begin;
    ret->m_gradEnd = end;
    ret->m_gradAlpha = alpha;
    ret->m_gradDirection = direction;
    return ret;
}
