/**
 * @file llimage.h
 * @brief Object for managing images and their textures.
 *
 * $LicenseInfo:firstyear=2000&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LL_LLIMAGE_H
#define LL_LLIMAGE_H

#include "lluuid.h"
#include "llstring.h"
#include "llpointer.h"
#include "lltrace.h"

constexpr S32 MIN_IMAGE_MIP =  2; // 4x4, only used for expand/contract power of 2
constexpr S32 MAX_IMAGE_MIP = 12; // 4096x4096

// *TODO : Use MAX_IMAGE_MIP as max discard level and modify j2c management so that the number
// of levels is read from the header's file, not inferred from its size.
constexpr S32 MAX_DISCARD_LEVEL = 5;

// JPEG2000 size constraints
// Those are declared here as they are germane to other image constraints used in the viewer
// and declared right here. Some come from the JPEG2000 spec, some conventions specific to SL.
constexpr S32 MAX_DECOMPOSITION_LEVELS = 32;    // Number of decomposition levels cannot exceed 32 according to jpeg2000 spec
constexpr S32 MIN_DECOMPOSITION_LEVELS = 5;     // the SL viewer will *crash* trying to decode images with fewer than 5 decomposition levels (unless image is small that is)
constexpr S32 MAX_PRECINCT_SIZE = 4096;         // No reason to be bigger than MAX_IMAGE_SIZE
constexpr S32 MIN_PRECINCT_SIZE = 4;            // Can't be smaller than MIN_BLOCK_SIZE
constexpr S32 MAX_BLOCK_SIZE = 64;              // Max total block size is 4096, hence 64x64 when using square blocks
constexpr S32 MIN_BLOCK_SIZE = 4;               // Min block dim is 4 according to jpeg2000 spec
constexpr S32 MIN_LAYER_SIZE = 2000;            // Size of the first quality layer (after header). Must be > to FIRST_PACKET_SIZE!!
constexpr S32 MAX_NB_LAYERS = 64;               // Max number of layers we'll entertain in SL (practical limit)

constexpr S32 MIN_IMAGE_SIZE = (1<<MIN_IMAGE_MIP); // 4, only used for expand/contract power of 2
constexpr S32 MAX_IMAGE_SIZE = (1<<MAX_IMAGE_MIP); // 4096
constexpr S32 MIN_IMAGE_AREA = MIN_IMAGE_SIZE * MIN_IMAGE_SIZE;
constexpr S32 MAX_IMAGE_AREA = MAX_IMAGE_SIZE * MAX_IMAGE_SIZE;
constexpr S32 MAX_IMAGE_COMPONENTS = 8;
constexpr S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //4096 * 4096 * 8 = 128 MB

// Note!  These CANNOT be changed without modifying simulator code
// *TODO: change both to 1024 when SIM texture fetching is deprecated
constexpr S32 FIRST_PACKET_SIZE = 600;
constexpr S32 MAX_IMG_PACKET_SIZE = 1000;
constexpr S32 HTTP_PACKET_SIZE = 1496;

// Base classes for images.
// There are two major parts for the image:
// The compressed representation, and the decompressed representation.

class LLImageFormatted;
class LLImageRaw;
class LLColor4U;
class LLColor3;

typedef enum e_image_codec
{
    IMG_CODEC_INVALID  = 0,
    IMG_CODEC_RGB  = 1,
    IMG_CODEC_J2C  = 2,
    IMG_CODEC_BMP  = 3,
    IMG_CODEC_TGA  = 4,
    IMG_CODEC_JPEG = 5,
    IMG_CODEC_DXT  = 6,
    IMG_CODEC_PNG  = 7,
    IMG_CODEC_EOF  = 8
} EImageCodec;

//============================================================================
// library initialization class
// LLImage is frequently used in threads so do not convert it to LLSingleton

class LLImage
{
public:
    static void initClass(bool use_new_byte_range = false, S32 minimal_reverse_byte_range_percent = 75);
    static void cleanupClass();

    static const std::string& getLastThreadError();
    static void setLastError(const std::string& message);

    static bool useNewByteRange() { return sUseNewByteRange; }
    static S32  getReverseByteRangePercent() { return sMinimalReverseByteRangePercent; }

protected:
    static thread_local std::string sLastThreadErrorMessage;
    static bool sUseNewByteRange;
    static S32  sMinimalReverseByteRangePercent;
};

//============================================================================
// Image base class

class LLImageBase
:   public LLThreadSafeRefCount
{
protected:
    virtual ~LLImageBase();

    virtual void deleteData();
    virtual U8* allocateData(S32 size = -1);
    virtual U8* reallocateData(S32 size = -1);

public:
    LLImageBase();

    enum
    {
        TYPE_NORMAL = 0,
        TYPE_AVATAR_BAKE = 1,
    };

    virtual void dump();
    virtual void sanityCheck();

    U16 getWidth() const        { return mWidth; }
    U16 getHeight() const       { return mHeight; }
    S8  getComponents() const   { return mComponents; }
    S32 getDataSize() const     { return mDataSize; }

    const U8 *getData() const   ;
    U8 *getData()               ;
    bool isBufferInvalid() const;

    void setSize(S32 width, S32 height, S32 ncomponents);
    U8* allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 size = -1); // setSize() + allocateData()
    void enableOverSize() {mAllowOverSize = true ;}
    void disableOverSize() {mAllowOverSize = false; }

protected:
    // special accessor to allow direct setting of mData and mDataSize by LLImageFormatted
    void setDataAndSize(U8 *data, S32 size);

public:
    static void generateMip(const U8 *indata, U8* mipdata, int width, int height, S32 nchannels);

    // Function for calculating the download priority for textures
    // <= 0 priority means that there's no need for more data.
    static F32 calc_download_priority(F32 virtual_size, F32 visible_area, S32 bytes_sent);

    static EImageCodec getCodecFromExtension(const std::string& exten);

    //static LLTrace::MemStatHandle sMemStat;

private:
    U8 *mData;
    S32 mDataSize;

    U16 mWidth;
    U16 mHeight;

    S8 mComponents;

    bool mBadBufferAllocation;
    bool mAllowOverSize;

private:
    mutable LLSharedMutex mDataMutex;

public:
    template<bool SHARED>
    class DataLock : LLSharedMutexLockTemplate<SHARED>
    {
    public:
        DataLock(const LLImageBase* image)
        : LLSharedMutexLockTemplate<SHARED>(image ? &image->mDataMutex : nullptr)
        {
        }
    };
};

using LLImageDataLock = LLImageBase::DataLock<false>;
using LLImageDataSharedLock = LLImageBase::DataLock<true>;

// Raw representation of an image (used for textures, and other uncompressed formats
class LLImageRaw : public LLImageBase
{
protected:
    /*virtual*/ ~LLImageRaw();

public:
    LLImageRaw();
    LLImageRaw(U16 width, U16 height, S8 components);
    LLImageRaw(const U8* data, U16 width, U16 height, S8 components);
    LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy = false);
    // Construct using createFromFile (used by tools)
    //LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only = false);

    /*virtual*/ void deleteData();
    /*virtual*/ U8* allocateData(S32 size = -1);
    /*virtual*/ U8* reallocateData(S32 size);

    // use in conjunction with "no_copy" constructor to release data pointer before deleting
    // so that deletion of this LLImageRaw will not free the memory at the "data" parameter
    // provided to "no_copy" constructor
    void releaseData();


    bool resize(U16 width, U16 height, S8 components);

    //U8 * getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const;
    bool setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height,
                     const U8 *data, U32 stride = 0, bool reverse_y = false);

    void clear(U8 r=0, U8 g=0, U8 b=0, U8 a=255);

    void verticalFlip();

    // Returns true if the image is not fully opaque
    bool checkHasTransparentPixels();
    // if the alpha channel is all 100% opaque, delete it
    // returns true if alpha channel was deleted
    bool optimizeAwayAlpha();
    // Create an alpha channel if this image doesn't have one
    bool makeAlpha();

    static S32 biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
    static S32 expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
    static S32 contractDimToPowerOfTwo(S32 curr_dim, S32 min_dim = MIN_IMAGE_SIZE);
    void expandToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, bool scale_image = true);
    void contractToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, bool scale_image = true);
    void biasedScaleToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE);
    bool scale(S32 new_width, S32 new_height, bool scale_image = true);
    LLPointer<LLImageRaw> scaled(S32 new_width, S32 new_height);

    // Fill the buffer with a constant color
    void fill( const LLColor4U& color );

    // Multiply this raw image by the given color
    void tint( const LLColor3& color );

    // Copy operations

    //duplicate this raw image if refCount > 1.
    LLPointer<LLImageRaw> duplicate();

    // Src and dst can be any size.  Src and dst can each have 3 or 4 components.
    void copy( const LLImageRaw* src );

    // Src and dst are same size.  Src and dst have same number of components.
    void copyUnscaled( const LLImageRaw* src );

    // Src and dst are same size.  Src has 4 components.  Dst has 3 components.
    void copyUnscaled4onto3( const LLImageRaw* src );

    // Src and dst are same size.  Src has 3 components.  Dst has 4 components.
    void copyUnscaled3onto4( const LLImageRaw* src );

    // Src and dst are same size.  Src has 1 component.  Dst has 4 components.
    // Alpha component is set to source alpha mask component.
    // RGB components are set to fill color.
    void copyUnscaledAlphaMask( const LLImageRaw* src, const LLColor4U& fill);

    // Src and dst can be any size.  Src and dst have same number of components.
    void copyScaled( const LLImageRaw* src );


    // Composite operations

    // Src and dst can be any size.  Src and dst can each have 3 or 4 components.
    void composite( const LLImageRaw* src );

    // Emissive operations used by minimap
    // Roughly emulates GLTF emissive texture, but is not GLTF-compliant
    // *TODO: Remove in favor of shader
    void addEmissive(LLImageRaw* src);
    void addEmissiveScaled(LLImageRaw* src);
    void addEmissiveUnscaled(LLImageRaw* src);
protected:
    // Src and dst can be any size.  Src has 4 components.  Dst has 3 components.
    void compositeScaled4onto3( const LLImageRaw* src );

    // Src and dst are same size.  Src has 4 components.  Dst has 3 components.
    void compositeUnscaled4onto3( const LLImageRaw* src );

    // Src and dst can be any size.  Src has 3 components.  Dst has 4 components.
    void copyScaled3onto4( const LLImageRaw* src );

    // Src and dst can be any size.  Src has 4 components.  Dst has 3 components.
    void copyScaled4onto3( const LLImageRaw* src );

    // Create an image from a local file (generally used in tools)
    //bool createFromFile(const std::string& filename, bool j2c_lowest_mip_only = false);

    void copyLineScaled( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step );
    void compositeRowScaled4onto3( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len );

    static U8 fastFractionalMult(U8 a, U8 b);

    void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ;

public:
    static S32 sRawImageCount;

private:
    static bool validateSrcAndDst(std::string func, const LLImageRaw* src, const LLImageRaw* dst);
};

// Compressed representation of image.
// Subclass from this class for the different representations (J2C, bmp)
class LLImageFormatted : public LLImageBase
{
public:
    static LLImageFormatted* createFromType(S8 codec);
    static LLImageFormatted* loadFromMemory(const U8* data, U32 size, std::string_view mimetype);
    static LLImageFormatted* createFromExtension(const std::string& instring);
    static LLImageFormatted* createFromMimeType(std::string_view mimetype);
    static S8 getCodecFromMimeType(std::string_view mimetype);

protected:
    /*virtual*/ ~LLImageFormatted();

public:
    LLImageFormatted(S8 codec);

    // LLImageBase
    /*virtual*/ void deleteData();
    /*virtual*/ U8* allocateData(S32 size = -1);
    /*virtual*/ U8* reallocateData(S32 size);

    /*virtual*/ void dump();
    /*virtual*/ void sanityCheck();

    // New methods
    // subclasses must return a prefered file extension (lowercase without a leading dot)
    virtual std::string getExtension() = 0;
    // calcHeaderSize() returns the maximum size of header;
    //   0 indicates we don't have a header and have to read the entire file
    virtual S32 calcHeaderSize() { return 0; };
    // calcDataSize() returns how many bytes to read to load discard_level (including header)
    virtual S32 calcDataSize(S32 discard_level);
    // calcDiscardLevelBytes() returns the smallest valid discard level based on the number of input bytes
    virtual S32 calcDiscardLevelBytes(S32 bytes);
    // getRawDiscardLevel() by default returns mDiscardLevel, but may be overridden (LLImageJ2C)
    virtual S8  getRawDiscardLevel() { return mDiscardLevel; }

    bool load(const std::string& filename, int load_size = 0);
    bool save(const std::string& filename);

    virtual bool updateData() = 0; // pure virtual
    void setData(U8 *data, S32 size);
    void appendData(U8 *data, S32 size);

    // Loads first 4 channels.
    virtual bool decode(LLImageRaw* raw_image, F32 decode_time) = 0;
    // Subclasses that can handle more than 4 channels should override this function.
    virtual bool decodeChannels(LLImageRaw* raw_image, F32 decode_time, S32 first_channel, S32 max_channel);

    virtual bool encode(const LLImageRaw* raw_image, F32 encode_time) = 0;

    S8 getCodec() const;
    bool isDecoding() const { return mDecoding; }
    bool isDecoded()  const { return mDecoded; }
    void setDiscardLevel(S8 discard_level) { mDiscardLevel = discard_level; }
    S8 getDiscardLevel() const { return mDiscardLevel; }
    S8 getLevels() const { return mLevels; }
    void setLevels(S8 nlevels) { mLevels = nlevels; }

    // setLastError needs to be deferred for J2C images since it may be called from a DLL
    virtual void resetLastError();
    virtual void setLastError(const std::string& message, const std::string& filename = std::string());

protected:
    bool copyData(U8 *data, S32 size); // calls updateData()

protected:
    S8 mCodec;
    S8 mDecoding;
    S8 mDecoded;  // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC
    S8 mDiscardLevel;   // Current resolution level worked on. 0 = full res, 1 = half res, 2 = quarter res, etc...
    S8 mLevels;         // Number of resolution levels in that image. Min is 1. 0 means unknown.

public:
    static S32 sGlobalFormattedMemory;
};

#endif