// SPDX-FileCopyrightText: 2005 Dominik Seichter <domseichter@web.de>
// SPDX-FileCopyrightText: 2020 Francesco Pretto <ceztko@gmail.com>
// SPDX-License-Identifier: LGPL-2.0-or-later OR MPL-2.0

#ifndef PDF_IMAGE_H
#define PDF_IMAGE_H

#include "PdfXObject.h"

#include "PdfColorSpace.h"

#ifdef PODOFO_HAVE_JPEG_LIB
struct jpeg_decompress_struct;
#endif // PODOFO_HAVE_JPEG_LIB

struct png_struct_def;
struct png_info_def;

namespace PoDoFo {

class PdfDocument;
class InputStream;

enum class PdfImageOrientation : uint8_t
{
    Unknown = 0,
    TopLeft,
    TopRight,
    BottomRight,
    BottomLeft,
    LeftTop,
    RightTop,
    RightBottom,
    LeftBottom
};

struct PODOFO_API PdfImageInfo final
{
    unsigned Width = 0;
    unsigned Height = 0;
    nullable<PdfFilterList> Filters;
    unsigned char BitsPerComponent = 0;
    PdfColorSpaceInitializer ColorSpace;
    std::vector<double> DecodeArray;
    PdfImageOrientation Orientation = PdfImageOrientation::TopLeft;
};

enum class PdfImageLoadFlags
{
    None = 0,
    SkipTransform = 1,  ///< Skip applying orientation transform
};

struct PODOFO_API PdfImageLoadParams final
{
    unsigned ImageIndex = 0;
    PdfImageLoadFlags Flags = PdfImageLoadFlags::None;
};

/// A PdfImage object is needed when ever you want to embed an image
/// file into a PDF document.
/// The PdfImage object is embedded once and can be drawn as often
/// as you want on any page in the document using PdfPainter
///
/// @see GetImageReference
/// @see PdfPainter::DrawImage
class PODOFO_API PdfImage final : public PdfXObject
{
    friend class PdfXObject;
    friend class PdfDocument;

private:
    /// Construct a new PdfImage object
    /// This is an overloaded constructor.
    ///
    /// @param doc parent document
    PdfImage(PdfDocument& doc);

public:
    void DecodeTo(charbuff& buff, PdfPixelFormat format, int scanLineSize = -1) const;
    void DecodeTo(const bufferspan& buff, PdfPixelFormat format, int scanLineSize = -1) const;
    void DecodeTo(OutputStream& stream, PdfPixelFormat format, int scanLineSize = -1) const;

    charbuff GetDecodedCopy(PdfPixelFormat format);

    /// Try read image info, when available as read from internal image codecs
    /// (eg. the actual color space of a /DCTDecode image)
    bool TryFetchRawImageInfo(PdfImageInfo& info);

    /// Set a softmask for this image.
    /// @param softmask a PdfImage pointer to the image, which is to be set as softmask, must be 8-Bit-Grayscale
    ///
    void SetSoftMask(const PdfImage& softmask);

    /// Set the actual image data from a buffer
    ///
    /// @param buffer buffer supplying image data
    /// @param width width of the image in pixels
    /// @param height height of the image in pixels
    /// @param format pixel format of the bitmap
    /// @param rowSize length of the row, if negative the default is used
    ///
    void SetData(const bufferview& buffer, unsigned width, unsigned height,
        PdfPixelFormat format, int rowSize = -1);

    /// Set the actual image data from an input stream
    ///
    /// @param stream stream supplying raw image data
    /// @param width width of the image in pixels
    /// @param height height of the image in pixels
    /// @param format pixel format of the bitmap
    /// @param rowSize length of the row, if negative the default is used
    void SetData(InputStream& stream, unsigned width, unsigned height,
        PdfPixelFormat format, int rowSize = -1);

    /// Set the actual image encoded data from a buffer
    ///
    /// @param buffer buffer supplying image data
    /// @param info parameters describing the encoded image data
    void SetDataRaw(const bufferview& buffer, const PdfImageInfo& info);

    /// Set the actual image encoded data from an input stream.
    ///
    /// @param stream stream supplying encoded image data
    /// @param info parameters describing the encoded image data
    void SetDataRaw(InputStream& stream, const PdfImageInfo& info);

    /// Load the image data from bytes
    /// @param params parameters like index to be fed to multi image/page
    ///   formats (eg. TIFF)
    /// @returns image the information used to load the image, retrieved from codecs or inferred
    PdfImageInfo Load(const std::string_view& filepath, const PdfImageLoadParams& params = { });

    /// Load the image data from bytes
    /// @param params parameters like index to be fed to multi image/page
    ///   formats (eg. TIFF)
    /// @returns image the information used to load the image, retrieved from codecs or inferred
    PdfImageInfo LoadFromBuffer(const bufferview& buffer, const PdfImageLoadParams& params = { });

    void ExportTo(charbuff& buff, PdfExportFormat format, PdfArray args = {}) const;

    /// Set an color/chroma-key mask on an image.
    /// The masked color will not be painted, i.e. masked as being transparent.
    ///
    /// @param r red RGB value of color that should be masked
    /// @param g green RGB value of color that should be masked
    /// @param b blue RGB value of color that should be masked
    /// @param threshold colors are masked that are in the range [(r-threshold, r+threshold),(g-threshold, g+threshold),(b-threshold, b+threshold)]
    void SetChromaKeyMask(int64_t r, int64_t g, int64_t b, int64_t threshold = 0);

    /// Apply an interpolation to the image if the source resolution
    /// is lower than the resolution of the output device.
    /// Default is false.
    /// @param value whether the image should be interpolated
    void SetInterpolate(bool value);

    Rect GetRect() const override;

    /// Get the color space of the image
    /// @returns the color space of the image
    const PdfColorSpaceFilter& GetColorSpace() const { return *m_ColorSpace; }

    /// Get the width of the image when drawn in PDF units
    /// @returns the width in PDF units
    unsigned GetWidth() const { return m_Width; }

    /// Get the height of the image when drawn in PDF units
    /// @returns the height in PDF units
    unsigned GetHeight() const { return m_Height; }

protected:
    const PdfXObjectForm* GetForm() const override;

private:
    /// Construct an image from an existing PdfObject
    ///
    /// @param obj a PdfObject that has to be an image
    PdfImage(PdfObject& obj);

    void setDataRaw(InputStream& stream, const PdfImageInfo& info, PdfImageLoadFlags flags);

#ifdef PODOFO_HAVE_JPEG_LIB
    void loadFromJpegInfo(jpeg_decompress_struct& ctx, PdfImageInfo& info);
    void exportToJpeg(charbuff& buff, const PdfArray& args) const;
    /// Load the image data from a JPEG file
    /// @param filename
    void loadFromJpeg(const std::string_view& filename, PdfImageInfo& info);

    /// Load the image data from JPEG bytes
    /// @param data JPEG bytes
    /// @param len number of bytes
    void loadFromJpegData(const unsigned char* data, size_t len, PdfImageInfo& info);
#endif // PODOFO_HAVE_JPEG_LIB

#ifdef PODOFO_HAVE_TIFF_LIB
    void loadFromTiffHandle(void* handle, const PdfImageLoadParams& params, charbuff& buffer, PdfImageInfo& info);
    /// Load the image data from a TIFF file
    /// @param filename
    void loadFromTiff(const std::string_view& filename, const PdfImageLoadParams& params, charbuff& buffer, PdfImageInfo& info);

    /// Load the image data from TIFF bytes
    /// @param data TIFF bytes
    /// @param len number of bytes
    void loadFromTiffData(const unsigned char* data, size_t len, const PdfImageLoadParams& params, charbuff& buffer, PdfImageInfo& info);
#endif // PODOFO_HAVE_TIFF_LIB

#ifdef PODOFO_HAVE_PNG_LIB
    void loadFromPngHandle(FILE* stream, charbuff& buffer, PdfImageInfo& info);
    /// Load the image data from a PNG file
    /// @param filename
    void loadFromPng(const std::string_view& filename, charbuff& buffer, PdfImageInfo& info);

    /// Load the image data from PNG bytes
    /// @param data PNG bytes
    /// @param len number of bytes
    void loadFromPngData(const unsigned char* data, size_t len, charbuff& buffer, PdfImageInfo& info);

    void loadFromPngContent(png_struct_def* png, png_info_def* pngInfo, charbuff& buffer, PdfImageInfo& info);
#endif // PODOFO_HAVE_PNG_LIB

    unsigned getBufferSize(PdfPixelFormat format) const;

    std::unique_ptr<PdfXObjectForm> getTransformation(PdfImageOrientation orientation);

private:
    PdfColorSpaceFilterPtr m_ColorSpace;
    unsigned m_Width;
    unsigned m_Height;
    unsigned char m_BitsPerComponent;
    std::unique_ptr<PdfXObjectForm> m_Transformation;
};

};

ENABLE_BITMASK_OPERATORS(PoDoFo::PdfImageLoadFlags);

#endif // PDF_IMAGE_H
