#ifndef AVIFILE_IMAGE_H
#define AVIFILE_IMAGE_H

#include <default.h>
#include <formats.h>
#include <cstdlib> //abs
#include <cstring>

/**
 * Structure that hides some details of proper BITMAPINFOHEADER
 * initialization methods.
 */
struct BitmapInfo : public BITMAPINFOHEADER
{
    int m_iColors[3];

    BitmapInfo() {}
    BitmapInfo(int width, int height, int bpp);
    BitmapInfo(const BITMAPINFOHEADER & hdr);
    BitmapInfo(const BitmapInfo & bi);
    BitmapInfo(const BitmapInfo * bi);
    static int BitCount(int csp);
    void SetBitFields16();
    void SetBitFields15();
    void SetRGB();
    void SetBits(int bits);
    void SetSpace(int csp);
    int Bpp() const;
    bool operator== (const BitmapInfo & bi) const
    {
	return (biWidth == bi.biWidth) && (biHeight == bi.biHeight)
	    && (biCompression == bi.biCompression) && (Bpp() == bi.Bpp());
    }
    bool IsRGB() const
    {
	return biCompression == 0 || biCompression == 3;
    }
    void Print() const;
};

/**
 * This class describes an uncompressed image in one of several supported
 * formats. These formats currently include RGB ( 15, 16, 24 and 32 bits )
 * and packed YUV ( non-subsampled 24-bit YUV and 16-bit YUY2 formats ).
 * It is capable of converting between all supported formats. Some of
 * conversions are optimized, but it is generally not recommended to use
 * these conversions in time-critical code.
 */

/**
 * mplayer compatible surface declaration
 * used to allow development of common conversion routines
 */
struct ci_surface_t {
    int m_iWidth, m_iHeight;
    int x, y;
    unsigned char* m_pcData[3];
    int stride[3];
    int format;
    int bpp;
};

class CImage : public ci_surface_t
{
    /* Copying disabled */
    CImage& operator= (CImage& e) { return *this; }
    CImage(const CImage& e) {}
protected:
    BitmapInfo* m_pInfo;
    unsigned int m_iBytes[3];
    int m_iDepth;
    int m_iBpl;
    int m_iBpp;
    int m_iPixels;
    float m_fQuality;
    void* m_pUserData;
    mutable int m_iRefcount;
    bool m_bDataOwner;

    void fillMembers();
public:
    //       non-conversion constructors
    /**
     *  Creates new image in format 'header' from specified memory area.
     *  Either allocates its own memory area & copies src data into it, or reuses
     *  parent data.
     */
    CImage(const BitmapInfo* header, uint8_t* data = 0, bool copy = true);
    CImage(const BitmapInfo* header, uint8_t* strides[3], bool copy = true);

    /* Creates 24-bit RGB image from 24-bit RGB 'data' */
    CImage(const uint8_t* data, int width, int height);

    /* Creates a copy of image 'im' */
    CImage(const CImage* im);

    // Conversion constructors

    /* Creates RGB image in depth 'depth' from image 'im' */
    CImage(const CImage* im, int depth);

    /*  Creates new image in format 'header' from image 'im' */
    CImage(const CImage* im, const BitmapInfo* header);

    virtual ~CImage();
    void AddRef() const { m_iRefcount++; }
    void Release() const;
    uint8_t *Data(unsigned int idx = 0) { return m_pcData[idx]; }
    const uint8_t *Data(unsigned int idx = 0) const { return m_pcData[idx]; }
    // use to copy data from separate planes
    uint8_t *At(int i) { return Data() + i * m_iBpl; }
    const uint8_t *At(int i) const { return Data() + i * m_iBpl; }
    uint8_t *Offset(int i) { return Data() + i * m_iBpp; }
    const uint8_t *Offset(int i) const { return Data() + i * m_iBpp; }
    uint8_t *At(int i, int j) { return Offset(i) + j * m_iBpl; }
    int Width() const { return m_iWidth; }
    int Height() const { return m_iHeight; }
    int Bpp() const { return m_iBpp; }
    int Bpl() const { return m_iBpl; }
    int Depth() const { return m_iDepth; }
    unsigned int Bytes(unsigned int idx = 0) const { return m_iBytes[idx]; }
    int Pixels() const { return m_iPixels; }
    bool Direction() const;
    /**
     *  Is it in format 'hdr'?
     */
    bool IsFmt(const BitmapInfo * hdr) const { return (*m_pInfo == *hdr); }
    BitmapInfo *GetFmt();
    const BitmapInfo *GetFmt() const;

    /**
     * Translations 24-bit RGB <-> Non-subsampled packed 24-bit YUV
     * ( for image processing purposes )
     */
    void ToYUV();
    void ToRGB();
    void ToFormat(unsigned int fmt) {}

    /**
     * Fast MMX blur filter ( blur over the square with 2^range*2^range dimensions )
     * Blur(x); Blur(y, x) has the same effect as Blur(y)
     */
    void Blur(int range, int from = 0);

    /**
     * Writes 24-bit uncompressed bitmap with name 'filename'.
     */
    void Dump(const char *filename);

    /**
     * RGB<->BGR translation for 24-bit images
     */
    void ByteSwap();

    float GetQuality() const { return m_fQuality; }
    void SetQuality(float q) { m_fQuality = q; }
    void SetUserData(void* userData) { m_pUserData = userData; }
    void* GetUserData() const { return m_pUserData; }

    static bool Supported(const BITMAPINFOHEADER & bi);
    static bool Supported(int csp, int bitcount);
    static int UnknownColorSpace(int csp);

    /**
     * General way of converting is as follows.
     * a) There's one function for converting anything to 24-bit RGB and back.
     * b) There is a number of 'shortcuts' between formats, e.g. YUY2<->16-bit
     *  RGB.
     * c) If there's no shortcut between particular two formats, the conversion
     * is done in two steps through temporary image.
     */
    void Convert(const CImage* from_img);
    void Convert(const uint8_t* from_data, const BitmapInfo* from_fmt);

 protected:

     /* make it compatible with conversion routines used by mlib & mplayer */
#define X_STDCONV_PARAMS \
    uint8_t image, const uint8_t* py,\
    const uint8_t* pu, const uint8_t* pv, \
    const uint32_t h_size, const uint32_t v_size,\
    uint32_t rgb_stride, uint32_t y_stride, uint32_t uv_stride

     // possible better solution - pass just structure pointer
     struct convparams {
	 uint8_t* out[3];
	 const int strideout[3];
	 const uint8_t* in[3];
	 const int stridin[3];
	 int width;
	 int height;
         int flip; // bool would not work with C compiler
     };

#define STDCONV_PARAMS \
    uint8_t* to, const uint8_t* from, int width, int height, bool flip_dir

    static void anyFromRgb24(uint8_t* to, const uint8_t* from, const BitmapInfo * from_fmt, bool flip_dir);
    static void anyToRgb24(uint8_t* to, const uint8_t* from, const BitmapInfo * to_fmt, bool flip_dir);

    static void rgb24ToRgb15(STDCONV_PARAMS);
    static void rgb24ToRgb16(STDCONV_PARAMS);
    static void rgb24ToRgb32(STDCONV_PARAMS);
    static void rgb24ToYuv(STDCONV_PARAMS);
    static void rgb24ToYuy2(STDCONV_PARAMS);
    static void rgb24ToYv12(STDCONV_PARAMS);

    static void rgb15ToRgb24(STDCONV_PARAMS);
    static void rgb16ToRgb24(STDCONV_PARAMS);
    static void rgb32ToRgb24(STDCONV_PARAMS);
    static void yuvToRgb24(STDCONV_PARAMS);
    static void yuy2ToRgb24(STDCONV_PARAMS);
    static void yv12ToRgb24(STDCONV_PARAMS);

    /* Fills memory area of 16-bit RGB image from pointer to 15-bit RGB data
     * Has MMX & non-MMX versions
     */
    static void rgb15ToRgb16(STDCONV_PARAMS);
    static void yv12ToYuy2(STDCONV_PARAMS);
    static void yuy2ToYv12(STDCONV_PARAMS);
};

struct yuv;

struct lookuptable
{
    int m_plY[256];
    int m_plRV[256];
    int m_plGV[256];
    int m_plGU[256];
    int m_plBU[256];
    lookuptable()
    {
        for(int i=0; i<256; i++)
        {
    	    m_plY[i]=298*(i-16);
	    m_plRV[i]=408*(i-128);
    	    m_plGV[i]=-208*(i-128);
	    m_plGU[i]=-100*(i-128);
    	    m_plBU[i]=517*(i-128);
        }
    }
};

/**
 * Structure that represents 24-bit RGB pixel.
 */
struct col
{
    static lookuptable t;
    uint8_t b,g,r;
    col(){}
    col(uint8_t _b, uint8_t _g, uint8_t _r)
	:b(_b),g(_g),r(_r)
    {}
    inline col(yuv YUV);
    int CREF() const
    {
	return (int(b) << 16) + (int(g) << 8) + int(r);
    }
    unsigned int Y() const
    {
	int _Y=(16853l * r) + (33055l * g) + (6392l * b) + 0x8000;
	return (_Y>>16)+16;
    }
};

/**
 * Structure that represents 24-bit ( 888 ) YUV pixel.
 */
struct yuv
{
    uint8_t Y, 
		 Cb,/* aka U */
		 Cr;/* aka V */
    yuv() {}
    yuv(uint8_t _Y, uint8_t _Cb, uint8_t _Cr)
	:Y(_Y), Cb(_Cb), Cr(_Cr) {}
    yuv(col Col)
    {
	int _Y,_Cb,_Cr;
	_Y=66*Col.r+129*Col.g+25*Col.b+0x1000;
	_Cb=-38*Col.r-74*Col.g+112*Col.b+0x8000;
	_Cr=112*Col.r-94*Col.g-18*Col.b+0x8000;
	if(_Y<0x1000)_Y=0x1000;
	else if(_Y>0xef00)_Y=0xef00;
	if(_Cb<0x1000)_Cb=0x1000;
	else if(_Cb>0xef00)_Cb=0xef00;
	if(_Cr<0x1000)_Cr=0x1000;
        else if(_Cr>0xef00)_Cr=0xef00;

	Y=_Y>>8;
	Cb=_Cb>>8;
	Cr=_Cr>>8;
    }
};

inline col::col(yuv YUV)
{
    int R=t.m_plY[YUV.Y]+t.m_plRV[YUV.Cr];
    int G=t.m_plY[YUV.Y]+t.m_plGV[YUV.Cr]+t.m_plGU[YUV.Cb];
    int B=t.m_plY[YUV.Y]+t.m_plBU[YUV.Cb];
    if(R<0)R=0;
    else if(R>0xff00)R=0xff00;
    if(B<0)B=0;
    else if(B>0xff00)B=0xff00;
    if(G<0)G=0;
    else if(G>0xff00)G=0xff00;
    r=R>>8;
    g=G>>8;
    b=B>>8;
}

#endif // AVIFILE_IMAGE_H
