/*******************************************************************************************************
 DkImageStorage.h
 Created on:	12.07.2013

 nomacs is a fast and small image viewer with the capability of synchronizing multiple instances

 Copyright (C) 2011-2013 Markus Diem <markus@nomacs.org>
 Copyright (C) 2011-2013 Stefan Fiel <stefan@nomacs.org>
 Copyright (C) 2011-2013 Florian Kleber <florian@nomacs.org>

 This file is part of nomacs.

 nomacs 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 3 of the License, or
 (at your option) any later version.

 nomacs 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

#pragma warning(push, 0) // no warnings from includes - begin
#include <QColor>
#include <QFutureWatcher>
#include <QImage>
#include <QObject>
#include <QVector>

// opencv
#ifdef WITH_OPENCV
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#endif
#pragma warning(pop) // no warnings from includes - end

#ifdef Q_OS_WIN
#pragma warning(disable : 4251) // TODO: remove
#pragma warning(disable : 4714) // Qt's force inline
#endif

#if defined(__GNUC__) && defined(Q_OS_MAC) || defined(__clang__)
#pragma GCC diagnostic ignored "-Winvalid-source-encoding"
#endif

#ifndef DllCoreExport
#ifdef DK_CORE_DLL_EXPORT
#define DllCoreExport Q_DECL_EXPORT
#elif DK_DLL_IMPORT
#define DllCoreExport Q_DECL_IMPORT
#else
#define DllCoreExport Q_DECL_IMPORT
#endif
#endif

// Qt defines
class QPixmap;
class QString;
class QSize;
class QColor;
class QTimer;

namespace nmc
{
class DkRotatingRect;

/**
 * DkImage holds some basic image processing
 * methods that are generally needed.
 **/
class DllCoreExport DkImage
{
public:
    /**< interpolation mapping OpenCV -> Qt */
    enum { ipl_nearest, ipl_area, ipl_linear, ipl_cubic, ipl_lanczos, ipl_end };

#ifdef WITH_OPENCV
    static cv::Mat qImage2Mat(const QImage &img);
    static QImage mat2QImage(cv::Mat img);
    static void mapGammaTable(cv::Mat &img, const QVector<unsigned short> &gammaTable);
    static void gammaToLinear(cv::Mat &img);
    static void linearToGamma(cv::Mat &img);
    static void logPolar(const cv::Mat &src, cv::Mat &dst, cv::Point2d center, double scaleLog, double angle, double scale = 1.0);
    static void tinyPlanet(QImage &img, double scaleLog, double angle, QSize s, bool invert = false);
#endif

    static QString getBufferSize(const QSize &imgSize, const int depth);
    static float getBufferSizeFloat(const QSize &imgSize, const int depth);
    static QImage resizeImage(const QImage &img, const QSize &newSize, double factor = 1.0, int interpolation = ipl_cubic, bool correctGamma = true);

    template<typename numFmt>
    static QVector<numFmt> getGamma2LinearTable(int maxVal = USHRT_MAX);
    template<typename numFmt>
    static QVector<numFmt> getLinear2GammaTable(int maxVal = USHRT_MAX);
    static void gammaToLinear(QImage &img);
    static void linearToGamma(QImage &img);
    static void mapGammaTable(QImage &img, const QVector<uchar> &gammaTable);
    static bool normImage(QImage &img);
    static bool autoAdjustImage(QImage &img);
    static bool gaussianBlur(QImage &img, float sigma = 20.0f);
    static bool unsharpMask(QImage &img, float sigma = 20.0f, float weight = 1.5f);
    static bool alphaChannelUsed(const QImage &img);
    static QImage thresholdImage(const QImage &img, double thr, bool color = false);

    /**
     * Rotates the image clockwise by angle. See rotateImageFast().
     */
    static QImage rotateImage(const QImage &img, double angle);
    static QImage grayscaleImage(const QImage &img);
    static QPixmap colorizePixmap(const QPixmap &icon, const QColor &col, float opacity = 1.0f);
    static QPixmap loadIcon(const QString &filePath = QString(), const QSize &size = QSize(), const QColor &col = QColor());
    static QPixmap loadIcon(const QString &filePath, const QColor &col, const QSize &size = QSize());
    static QPixmap loadFromSvg(const QString &filePath, const QSize &size);
    static QImage createThumb(const QImage &img, const int maxSize = -1);
    static uchar findHistPeak(const int *hist, float quantile = 0.005f);
    static QPixmap makeSquare(const QPixmap &pm);
    static QPixmap merge(const QVector<QImage> &imgs);
    static QImage cropToImage(const QImage &src, const DkRotatingRect &rect, const QColor &fillColor = QColor());
    static QImage hueSaturation(const QImage &src, int hue, int sat, int brightness);
    static QImage exposure(const QImage &src, double exposure, double offset, double gamma);
    static QImage bgColor(const QImage &src, const QColor &col);
    static QByteArray extractImageFromDataStream(const QByteArray &ba,
                                                 const QByteArray &beginSignature = "‰PNG",
                                                 const QByteArray &endSignature = "END®B`‚",
                                                 bool debugOutput = false);
    static bool fixSamsungPanorama(QByteArray &ba);
    static int intFromByteArray(const QByteArray &ba, int pos);

#ifdef WITH_OPENCV
    static cv::Mat exposureMat(const cv::Mat &src, double exposure);
    static cv::Mat gammaMat(const cv::Mat &src, double gmma);
    static cv::Mat applyLUT(const cv::Mat &src, const cv::Mat &lut);
#endif // WITH_OPENCV
};

class DllCoreExport DkImageStorage : public QObject
{
    Q_OBJECT

public:
    DkImageStorage(const QImage &img = QImage());

    enum ComputeState {
        l_not_computed,
        l_computing,
        l_computed,
        l_empty,
        l_cancelled,

        l_end
    };

    bool isEmpty() const
    {
        return mImg.isNull();
    };

    QSize size() const
    {
        return mImg.size();
    };

    void setImage(const QImage &img);
    QImage imageConst() const;
    QImage image(const QSize &size = QSize());

public slots:
    void antiAliasingChanged(bool antiAliasing);
    void imageComputed();

signals:
    void imageUpdated() const;
    void infoSignal(const QString &msg) const;

protected:
    QImage mImg;
    QImage mScaledImg;

    QFutureWatcher<QImage> mFutureWatcher;

    ComputeState mComputeState = l_not_computed;

    void init();
    void compute(const QSize &size);
};

/**
 * Rotates the image clockwise by angle.
 *
 * Returns the original image if angle is 0.
 * Do matrix operations for angles 90, 180, or 270
 * to speed up the process.
 * Calls rotateImage() for other angles.
 *
 * The returned image might have a different format.
 */
QImage rotateImageFast(const QImage &img, double angle);

/**
 * Rotates the image clockwise by angle.
 *
 * Rotate the input image by painting on a new RGBA8888 QImage.
 */
QImage rotateImage(const QImage &img, double angle);
}
