#ifndef __LIBPSD_H_       
#define __LIBPSD_H_
#include <stdlib.h>
#include <stdio.h>
#include <vector>

namespace adsk { namespace libPSD { 
                                          
class PSDFile;

enum PSDBlendMode
{
    blendNORMAL = 0,
    blendMULTIPLY,
    blendDARKEN,
    blendDIFFERENCE,
    blendEXCLUSION,
    blendHARDLIGHT,
    blendCOLORDODGE,
    blendLIGHTEN,
    blendSCREEN,
    blendCOLORBURN,
    blendOVERLAY,
    blendHUE,
    blendSATURATION,
    blendLUMINOSITY,
    blendCOLOR,
    blendDISSOLVE,
    blendSOFTLIGHT,
    blendLINEARDODGE,
    blendVIVIDLIGHT,
    blendLINEARBURN,
    blendLINEARLIGHT,
    blendPINLIGHT,
    blendHARDMIX,
    blendUNKNOW
};

enum PSDError {
    PSD_SUCCESS = 0,
    PSD_FAIL,
    PSD_MEMORY_FAILURE,
    PSD_ERROR_OPENING_FILE,
    PSD_FOPEN_FAIL,
    PSD_FILE_NOT_PSD,
    PSD_READ_ERROR,
    PSD_WRONG_VERSION,
    PSD_NO_INSTANCE,
    PSD_INDEX_OUT_OF_RANGE,
    PSD_FILE_HEADER_NOT_VALID,
    PSD_PSDFILE_ERROR,
    PSD_INVALID_LAYER,
    PSD_CHANNEL_INDEX_OUT_OF_RANGE
};

enum CompressionMethod 
{
    PSD_COMPRESSION_NONE = -1,
    PSD_COMPRESSION_RAW, 
    PSD_COMPRESSION_RLE,        
    PSD_COMPRESSION_ZIP, 
    PSD_COMPRESSION_ZIP_PREDICTION 
};

struct BlendChannelInfo
{
    unsigned int  source;
    unsigned int  destination;
};

enum layerType { LAYER_NORMAL = 0, 
                 LAYER_SET_OPEN, 
                 LAYER_SET_CLOSED, 
                 LAYER_DIVIDER 
};

struct SectionDividerSetting
{
    unsigned int    layerType;
    unsigned int    signature;
    unsigned int    blendKey;
};

struct AdditionalLayerInfo
{
    unsigned int    signature;
    unsigned int    key;
    unsigned int    dataLength;
    unsigned char*  variableData;
};

typedef struct {
   unsigned int     hRes;         // Fixed-point number: pixels per inch
   unsigned short   hResUnit;     // 1=pixels per inch, 2=pixels per centimeter
   unsigned short   WidthUnit;    // 1=in, 2=cm, 3=pt, 4=picas, 5=columns
   unsigned int     vRes;         // Fixed-point number: pixels per inch
   unsigned short   vResUnit;     // 1=pixels per inch, 2=pixels per centimeter
   unsigned short   HeightUnit;   // 1=in, 2=cm, 3=pt, 4=picas, 5=columns 
} RESOLUTIONINFO;

typedef struct tagPSDLAYERSTATE {        
    unsigned short  state;        // 2 byte index of target layer (current layer) 0 = bottom.
} LAYERSTATE;

typedef struct {
    unsigned int    signature;
    unsigned short  key;
    char*           resName;
    unsigned int    resLength;
    char*           variableResData;
} IMAGERESOURCES;

typedef struct tagPSDFILEHEADER {
    int             signature;  //-- "8BPS"
    unsigned short  version;    //-- Always 1
    char            fileRes[6];
    unsigned short  channels;   //-- 1 to 24
    unsigned int    height;     //-- Height 
    unsigned int    width;      //-- Width
    unsigned short  depthValue; //-- 1, 8, or 16
    unsigned short  colorMode;  //-- PH_xxx Above
} PSD_FILE_HEADER;

class MBDLL_DECL LRect
{
public  :
    LRect(int left=0, int top=0, int right=0, int bottom=0);
    LRect& operator= (const LRect& LR);

    int  top;
    int  left;
    int  bottom;
    int  right;
} ;

inline LRect::LRect(int inLeft, int inTop, int inRight, int inBottom)
{
    left    = inLeft ;
    right   = inRight ;
    top     = inTop ;
    bottom  = inBottom ;
}

inline  LRect& LRect::operator= (const LRect& LR)
{
    if (this != &LR) {
        this->bottom = LR.bottom;
        this->left   = LR.left;
        this->right  = LR.right;
        this->top    = LR.top;
    }
    return *this;
}

// Layer information structures
class  MBDLL_DECL ChannelInfo
{
public :
        ChannelInfo();

        short               channelID;
        int                 channelDataLen;
        CompressionMethod   channelCompression;
        unsigned int        channelPos;
} ;

inline ChannelInfo::ChannelInfo()
    : channelID(0)
    ,channelDataLen(0)
    ,channelCompression(PSD_COMPRESSION_RAW)
    ,channelPos(0)
{ }

//------------------------------------------------------------------------------
/**
*  The PSDLayerMeta object contains informations about a layer. The
*  information varies based on the layer type. the meta data of a layer is
*  anything else beside the rasterized image data of that layer. 
*/
class  MBDLL_DECL PSDLayerMeta
{
friend class PSDFile;

public :
    PSDLayerMeta();
    ~PSDLayerMeta();

    LRect                     m_layerRect;
    unsigned short            m_numChannels;
    std::vector<ChannelInfo>  m_channelInfo;

    unsigned int              m_blendModeSig;
    unsigned int              m_blendModeKey;
    unsigned char             m_baseClipping;
    unsigned char             m_filler;
    unsigned int              m_extraDataField;

    unsigned int              m_layerMaskSize;
    LRect                     m_vectorMaskRect;
    char                      m_vectorMaskColor;
    char                      m_vectorMaskFlags;

    LRect                     m_layerMaskRect;
    char                      m_layerMaskFlags;
    char                      m_userMaskBgColor;
    unsigned int              m_blendRangeSize;
    unsigned int              m_grayBlendSource;
    unsigned int              m_grayBlendDest;
    BlendChannelInfo*         m_BlendChannelRange; 

    unsigned int              m_width;
    unsigned int              m_height;
    unsigned int              m_nbPixels;

    unsigned int              m_numAdditionalLayers;
    AdditionalLayerInfo*      m_listAddLayerInfo;

    // for layerType, see "lsct" Section Divider Setting 
    //  on page 39 of File Format pdf 
    SectionDividerSetting     m_sectionDivider; // lsct data
    unsigned int              m_layerId;        // Layer ID which needs to be read and updated
                                                // to the resources section.

public:
    const char*         getName() const { return m_layerName; }
    void                setName(const char* layerName);

    // 0.0 is completely transparent, 1.0 is completely opaque
    float               getOpacity() const { return m_opacity/255.0f; }
    void                setOpacity(float opacity);

    // locked
    bool                getTransparencyProtected() const{return (bool)(m_optionFlags & 0x01);}
    void                setTransparencyProtected(bool trsProtected);

    bool                getLocked() const             { return m_bLocked;   }
    void                setLocked(bool locked = true) { m_bLocked = locked; }
    
    // visibility flag
    bool                getVisibility() const {return (bool)!(m_optionFlags & 0x02);}
    void                setVisibility(bool visFlag);


    PSDBlendMode        getBlendingType() const;
    void                setBlendingType(PSDBlendMode blendMode);

    void                setNbChannel(unsigned short nbChannel) {m_numChannels=nbChannel;}
    void                setLayerRect(int top, int left, int bottom, int right);
    void                setChannelInfo(CompressionMethod method, 
                                       unsigned short nbChannel = 4);

private :
    void                readEachLayerRecord(PSDFile*       psdFile, 
                                            unsigned int & actualSize);

    PSDLayerMeta&       operator=   (const PSDLayerMeta *LM);

    char*               m_layerName;
    unsigned char       m_opacity;
    unsigned char       m_optionFlags;
    bool                m_bWroteToFile;
    bool                m_bLocked;

};

//------------------------------------------------------------------------------
/**
*  The PSDFileMeta object contains meta data about the psd file, such header
*  information, the number of layers, etc...
*
*  This is a self managed class, where it's data will be filled on PSDOpen().
*/
class  MBDLL_DECL PSDFileMeta {

    friend class PSDFile;
    // PSD File header block gets filled here.
    struct VersionInfo {    
        unsigned int    version;
        bool            hasRealMergedData;
        int             writerNameL;
        char*           writerName;
        int             readerNameL;
        char*           readerName;
        unsigned int    fileVersion;
    };


    class ThumbnailResource
    {
        unsigned int    format;         // 1 = kJpegRGB . Also supports kRawRGB (0).
        unsigned int    width;          // Width of thumbnail in pixels.
        unsigned int    height;         // Height of thumbnail in pixels.
        unsigned int    width_bytes;    // Padded row bytes = (width * bits per pixel + 31) / 32 * 4.
        unsigned int    total_size;     // Total size = widthbytes * height * planes
        unsigned int    size_after_compression; // Used for consistency check.
        unsigned int    bits_per_pixel;   // 24
        unsigned int    number_of_planes; // 1
        unsigned char*  jfif_data;
        unsigned int*   thumbnail_data;
    };

private:
    void DestroyVI();
    void DestroyThumbnail();

    RESOLUTIONINFO*     m_pResolutionInfo;
    unsigned int        m_imageResourcesSize;
    unsigned char*      m_colorData;
    unsigned int        m_colorDataSize;
    LAYERSTATE*         m_pLayerState;
    PSDFileMeta&        operator= (const PSDFileMeta &FM); 

public: 
    PSDFileMeta();
    ~PSDFileMeta();
    /*
    * Get/Set the X Resolution in Pixel per Inch.
    */
    float               GetXResolution() const;
    void                SetXResolution(float xres);
    /*
    * Get/Set the Y Resolution in Pixel per Inch.
    */
    float               GetYResolution() const;
    void                SetYResolution(float yres);
    /*
    * Get the resolution unit 
    * @Return value short 0=none, 1=pixels per inch, 2=pixels per centimeter
    */
    unsigned short      GetResUnit() const;
    void                SetResUnit(unsigned short resUnit);
    bool                SetFileHeaderStruct(unsigned int inWidth, unsigned int inHeight, 
                                            unsigned short inColorDepth, 
                                            unsigned short inChannels);
    /*
    * Get/Set the number of layer.
    *
    */
    unsigned int        GetNbLayers() const                { return m_numLayers; }
    void                SetNbLayers(unsigned int nbLayers) { m_numLayers = nbLayers; }

    // ColorModeData
    bool                SetColorModeData(unsigned char* colorModeData, 
                                         unsigned int   colorModeDataSize);

    // Get/Set the layer state.
    unsigned short      GetLayerState() const { return m_pLayerState->state; }
    void                SetLayerState(unsigned short layerState);
    
    // Synch File Resources
    void                Synch();

    PSD_FILE_HEADER     m_hdr;
    VersionInfo         m_vi;
    ThumbnailResource*  m_pThumbnail;
    unsigned int        m_numLayers; // to obtain the layer count, we need to 
                                     // read the layer&mask section.
    std::vector<IMAGERESOURCES*> m_vImageRes;
};


//------------------------------------------------------------------------------
/**
 * Open a Photoshop(PSD) file and fill internally meta data 
 *                       (file meta and layers meta data).
 * 
 * @param utf8Name      the file name of the PSD file.
 * 
 * @param write         set to true to open the file for writing
 * 
 * @return PSDFile      the private object represending this open PSD file. 
 *                      Threat this as a pointer to blind data. Do NOT directly
 *                      access any data members or attempt to dereference this
 *                      pointer.
 */

PSDFile   MBDLL_DECL    *PSDOpen(const char *utfName, bool write = false);

//------------------------------------------------------------------------------
/**
 * Obtain the last status of libPSD.
 * 
 * @param psdFile       the private object represending this open PSD file.
 * 
 * @return PSDError     the current error/warning status.
 *         
 */
PSDError   MBDLL_DECL   PSDGetLastError(PSDFile *psdFile);

//------------------------------------------------------------------------------
/**
 * Get the file metadata of a PSD file
 * 
 * @param psdFile       the private object represending this open PSD file.
 * 
 * @return PSDFileMeta  the file meta data that contains basic information
 *                      like the number of layers, etc...
 *         
 */
const MBDLL_DECL  PSDFileMeta   *PSDGetFileMeta(const PSDFile *psdFile);

//------------------------------------------------------------------------------
/**
 * Set the file metadata of psdFile 
 * 
 * @param psdFile       the private object represending this open PSD file.
 * 
 * @param psdFileMeta   the file meta data that contains basic information
 *                      like the number of layers, etc...
 *                      This information must be filly filled in before calling
 *                      this function -- it causes the PSD file header to
 *                      be written
 *         
 */
bool  MBDLL_DECL        PSDSetFileMeta(PSDFile *psdFile, 
                                       const PSDFileMeta *psdFileMeta);

//------------------------------------------------------------------------------
/**
 * Get the layer meta data for layerIndex of psdFile object.
 * 
 * @param psdFile       the private object represending this open PSD file.
 * 
 * @param layerIndex    the layer index.
 * 
 * @return PSDLayerMeta the layer meta data that contains all layers data, 
 *                      like number of channels, etc... 
 *         
 */
const MBDLL_DECL  PSDLayerMeta     *PSDGetLayerMeta(const PSDFile *psdFile, 
                                        unsigned int   layerIndex);


//------------------------------------------------------------------------------
/**
 * Set the layer meta data for layerIndex of psdFile object.
 * 
 * @param psdFile       the private object represending this open PSD file.
 * 
 * @param layerIndex    the layer index.
 * 
 * @param psdLayerMeta  the layer meta data that contains all layers data, 
 *                      like number of channels, etc... 
 *         
 */
void  MBDLL_DECL        PSDSetLayerMeta(PSDFile *psdFile, unsigned int layerIndex, 
                                        PSDLayerMeta *psdLayerMeta);


//------------------------------------------------------------------------------
/**
 * Read the channel image data for this layer and this channel of psdFile object.
 * 
 * @param psdFile       the private object represending this open PSD file.
 * 
 * @param layerIndex    the layer index value.
 * 
 * @param channelIndex  the channel index value.
 * 
 * @param buffer        the buffer will be filled with image data. 
 *                      Caller responsible for memory management of this buffer.
 * 
 * @param bufferSize    the size of the buffer. 
 *                      ( layer's width * layer's height * sizeof(char) )
 * 
 * @return bool         true, if the call succeeded. False otherwise 
 *                      ( check with PSDGetLastError() for reason)
 *         
 */
bool MBDLL_DECL PSDReadChannelImageData(PSDFile         *psdFile, 
                                        unsigned int     layerIndex, 
                                        unsigned int     channelIndex, 
                                        void            *buffer, 
                                        size_t           bufferSize);
                            
bool MBDLL_DECL PSDWriteChannelImageData(PSDFile        *psdFile, 
                                         unsigned int    layerIndex, 
                                         unsigned int    channelIndex, 
                                         void           *buffer, 
                                         size_t          bufferSize);
                             
bool MBDLL_DECL PSDReadChannelImageData(PSDFile         *psdFile, 
                                        void            *buffer, 
                                        size_t           bufferSize);

bool MBDLL_DECL PSDReadCompositeImageData(PSDFile       *psdFile, 
                                          unsigned int   imageWidth, 
                                          unsigned int   imageHeight, 
                                          void          *buffer, 
                                          size_t         bufferSize);     
                              
bool MBDLL_DECL PSDWriteCompositeImageData(PSDFile      *psdFile, 
                                           unsigned int  imageWidth, 
                                           unsigned int  imageHeight, 
                                           CompressionMethod compressMethod, 
                                           void         *buffer, 
                                           size_t        bufferSize);
     
bool MBDLL_DECL PSDWrite4ChanCompositeImageData(PSDFile     *psdFile, 
                                                unsigned int imageWidth, 
                                                unsigned int imageHeight, 
                                                CompressionMethod compressMethod, 
                                                void **ARGBPixels);
 
//------------------------------------------------------------------------------
/**
 * Close the psdFile object and free all private data. Regardless of error 
 * status, this function needs to be called when done with a file, otherwise 
 * memory will leak. 
 * 
 * @param psdFile       the private object represending this open PSD file.
 *    
 */
void  MBDLL_DECL        PSDClose(PSDFile *psdFile);



// Helper/utility functions

//-----------------------------------------------------------------------------
/**
 * BlendLayers is a helper function that blend 2 layers based on the src's
 * blend mode. In order to this to work, you need pass a Src and Dst RGBA
 * channels.
 * 
 * 
 * @param rSrc The Red channel source.
 * @param gSrc The green channel source.
 * @param bSrc The blue channel source.
 * @param aSrc The alpha channel source.
 * @param glbOpacity The Opacity of the layer that will blend Src with
 * @param inRectLayer1 The Rect of the Src Layer
 * @param blendMode The Blend mode of Src Layer
 * @param inWidth The Width of the image
 * @param inHeight The Height of the image
 * @param rDst The red channel destination
 * @param gDst The green channel destination
 * @param bDst The blue channel destination
 * @param aDst The alpha channel destination, if NULL, it will be allocated
 *             by the function, use free(aDst) when done.
 *
 */
bool  MBDLL_DECL PSDBlendLayers(unsigned char* rSrc, unsigned char* gSrc,
                    unsigned char* bSrc, unsigned char* aSrc, 
                    float glbOpacity,    LRect          inRectLayer1, 
                    int blendMode, unsigned int inWidth, unsigned int inHeight,
                    unsigned char* &rDst, unsigned char* &gDst,
                    unsigned char* &bDst, unsigned char* &aDst);

//-----------------------------------------------------------------------------
/**
 * InterleaveImageData takes the 4 seperate RGBA planes and interleaves them
 * into a single RGBA image -- optionally multiplying the RGB by the alpha.
 * 
 * @param nPixels  The count of the pixels to be processed
 * @param r        Pointer to the Red plane
 * @param g        Pointer to the Green plane
 * @param b        Pointer to the Blue plane
 * @param a        Pointer to the Alpha plane (can be null -- then all alphas
 *                 are assumed to be opaque)
 * @param target   Pointer to the target RGBA image
 * @param multAlpha true if this function is to multiply the aAlpha into the
 *                  RGB channels (defaults to true)
 *
 * @note on little endian machines the resulting channel order will be RGBA in
 * memory (and therefore ABGR in a register -- ie. A is in the most significant
 * 8 bits). On big endian machines, the order will be ABGR in both memory and
 * register.
 */
void  MBDLL_DECL PSDInterleaveImageData(int nPixels, const unsigned char *r, const unsigned char *g, 
                                         const unsigned char *b, const unsigned char *a, 
                                         unsigned int *target, bool multAlpha=true);

//-----------------------------------------------------------------------------
/**
 * InterleaveImageData16 takes the 4 separate 16 bit RGBA planes and
 * interleaves them into a single 16 bit per channel RGBA image -- optionally
 * multiplying the RGB by the alpha.
 * 
 * @param nPixels  The count of the pixels to be processed
 * @param r        Pointer to the Red plane
 * @param g        Pointer to the Green plane
 * @param b        Pointer to the Blue plane
 * @param a        Pointer to the Alpha plane (can be null -- then all alphas
 *                 are assumed to be opaque)
 * @param target   Pointer to the target RGBA image @param multAlpha true if
 *                 this function is to multiply the aAlpha into the RGB
 *                 channels (defaults to true)
 *
 * @note on little endian machines the resulting channel order will be RGBA in
 * memory (and therefore ABGR in a register -- ie. A is in the most significant
 * 8 bits). On big endian machines, the order will be ABGR in both memory and
 * register.
 */
void  MBDLL_DECL PSDInterleaveImageData16_16(int nPixels, 
                                 const unsigned short *r, const unsigned short *g, 
                                 const unsigned short *b, const unsigned short *a, 
                                 unsigned short *target, bool multAlpha=true);


//-----------------------------------------------------------------------------
/**
 * InterleaveImageData32 takes the 4 separate 32 bit float RGBA planes and
 * interleaves them into a single 32 bit float per channel RGBA image --
 * optionally multiplying the RGB by the alpha.
 * 
 * @param nPixels  The count of the pixels to be processed
 * @param r        Pointer to the Red plane
 * @param g        Pointer to the Green plane
 * @param b        Pointer to the Blue plane
 * @param a        Pointer to the Alpha plane (can be null -- then all alphas
 *                 are assumed to be opaque)
 * @param target   Pointer to the target RGBA image @param multAlpha true if
 *                 this function is to multiply the aAlpha into the RGB
 *                 channels (defaults to true)   
 *
 * @note on little endian machines the resulting channel order will be RGBA in
 * memory (and therefore ABGR in a register -- ie. A is in the most significant
 * 8 bits). On big endian machines, the order will be ABGR in both memory and
 * register.                                                                    
 */
void  MBDLL_DECL PSDInterleaveImageData32_32(int nPixels, const float *r, const float *g, 
                                              const float *b, const float *a, 
                                              float *target,  bool multAlpha=true);


//-----------------------------------------------------------------------------
/**
 * InterleaveImageData16 takes the 4 separate 16 bit RGBA planes and
 * interleaves them into a single 8 bit per channel RGBA image -- optionally
 * multiplying the RGB by the alpha.
 * 
 * @param nPixels  The count of the pixels to be processed
 * @param r        Pointer to the Red plane
 * @param g        Pointer to the Green plane
 * @param b        Pointer to the Blue plane
 * @param a        Pointer to the Alpha plane (can be null -- then all alphas
 *                 are assumed to be opaque)
 * @param target   Pointer to the target RGBA image @param multAlpha true if
 *                 this function is to multiply the aAlpha into the RGB
 *                 channels (defaults to true)
 *
 * @note on little endian machines the resulting channel order will be RGBA in
 * memory (and therefore ABGR in a register -- ie. A is in the most significant
 * 8 bits). On big endian machines, the order will be ABGR in both memory and
 * register.
 */
void  MBDLL_DECL PSDInterleaveImageData16(int nPixels, const unsigned short *r, const unsigned short *g, 
                                           const unsigned short *b, const unsigned short *a, 
                                           unsigned int *target, bool multAlpha=true);

//-----------------------------------------------------------------------------
/**
 * InterleaveImageData32 takes the 4 separate 32 bit float RGBA planes and
 * interleaves them into a single 8 bit per channel RGBA image -- optionally
 * multiplying the RGB by the alpha.
 * 
 * @param nPixels  The count of the pixels to be processed
 * @param r        Pointer to the Red plane
 * @param g        Pointer to the Green plane
 * @param b        Pointer to the Blue plane
 * @param a        Pointer to the Alpha plane (can be null -- then all alphas
 *                 are assumed to be opaque)
 * @param target   Pointer to the target RGBA image @param multAlpha true if
 *                 this function is to multiply the aAlpha into the RGB
 *                 channels (defaults to true)
 *
 * @note on little endian machines the resulting channel order will be RGBA in
 * memory (and therefore ABGR in a register -- ie. A is in the most significant
 * 8 bits). On big endian machines, the order will be ABGR in both memory and
 * register.
 */
void  MBDLL_DECL PSDInterleaveImageData32(int nPixels, const float *r, const float *g, 
                                           const float *b, const float *a, 
                                           unsigned int *target, bool multAlpha=true);


};}; //namespace Adsk;libPSD 
#endif


