#include <cassert>
#include <cstring>

#include "loadtga.h"

namespace ogl2d
{
    
namespace Utilities
{
typedef unsigned char CHAR;
typedef unsigned short WORD;
typedef unsigned long DWORD;

enum {
    TGA_RGB     =  2,        // This tells us it's a normal RGB (really BGR) file
    TGA_A       =  3,        // This tells us it's a ALPHA file
    TGA_RLE     = 10,        // This tells us that the targa is Run-Length Encoded (RLE)
};
#ifndef NULL
#define NULL 0
#endif


static bool LoadTgaFormat(const CHAR* in, CHAR* out, int imageType, int bits, int width, int height)
{
    int channels = 0;                    // The channels of the image (3 = RGA : 4 = RGBA)
    int stride = 0;                      // The stride (channels * width)
    int i = 0;                           // A counter
    CHAR* Ptr  = out;
    const CHAR* cPtr = in;
    
    if(imageType != TGA_RLE)
    {
        
        if(bits == 32)
        {
            channels = 4;
            stride = channels * width;
            //for(int y = 0; y <height; y++)
            for(int y = height-1; y >=0; y--) // down - top
            {
                CHAR *pLine = &(Ptr[stride * y]);
                // memcpy(pLine,cPtr,stride);
                for (int x = 0; x<width<<2; x+=4)
                {
                    pLine[x+0] = cPtr[x+2];
                    pLine[x+1] = cPtr[x+1];
                    pLine[x+2] = cPtr[x+0];
                    pLine[x+3] = cPtr[x+3];
                }
                cPtr += stride;
            }        
        }
        /* belows are not support yet
        else  if(bits == 24)
        {
            channels = bits / 8;
            stride = channels * width;
            int pad=stride%4;
            if(pad)pad=4-pad;
            //ImageData = new unsigned char[stride * m_height];
            
            //for(int y = m_height-1; y >=0; y--)
            for(int y = 0; y<height; y++)
            {
                CHAR *pLine = &(Ptr[(stride+pad) * y]);
                memcpy(pLine,cPtr,stride);
                cPtr += channels * width;
                //fread(pLine, stride, 1, pFile);
            }
        }
        else if(bits == 16)
        {
            unsigned short pixels = 0;
            int r=0, g=0, b=0;
            
            channels = 3;
            stride = channels * width;
            //ImageData = new unsigned char[stride * m_height];
            
            for(int i = 0; i < width*height; i++)
            {
                memcpy(&pixels,cPtr,sizeof(unsigned short));
                cPtr+=sizeof(unsigned short);
                //fread(&pixels, sizeof(unsigned short), 1, pFile);
                b = (pixels & 0x1f) << 3;
                g = ((pixels >> 5) & 0x1f) << 3;
                r = ((pixels >> 10) & 0x1f) << 3;
                
                Ptr[i * 3 + 0] = r;
                Ptr[i * 3 + 1] = g;
                Ptr[i * 3 + 2] = b;
            }
        }    //*/
        else
        {
            // MessageBox(NULL, "loading TGA File error!", "Error", MB_OK);
            return false;
        }
    }
    // Else, it must be Run-Length Encoded (RLE)
    else
    {
        CHAR rleID = 0;
        int colorsRead = 0;
        channels = bits / 8;
        stride = channels * width;
        
        //ImageData = new unsigned char[stride * m_height];
        CHAR *pColors = new CHAR [channels];
        
        while(i < width*height)
        {
            memcpy(&rleID,cPtr,sizeof(CHAR));
            cPtr+=sizeof(CHAR);
            //fread(&rleID, sizeof(byte), 1, pFile);
            
            if(rleID < 128)
            {
                rleID++;
                
                while(rleID)
                {
                    memcpy(pColors,cPtr,sizeof(CHAR) * channels);
                    cPtr+=sizeof(CHAR) * channels;
                    //fread(pColors, sizeof(byte) * channels, 1, pFile);
                    
                    Ptr[colorsRead + 0] = pColors[2];
                    Ptr[colorsRead + 1] = pColors[1];
                    Ptr[colorsRead + 2] = pColors[0];
                    
                    if(bits == 32)
                        Ptr[colorsRead + 3] = pColors[3];
                    
                    i++;
                    rleID--;
                    colorsRead += channels;
                }
            }
            else
            {
                rleID -= 127;
                
                memcpy(pColors,cPtr,sizeof(CHAR) * channels);
                cPtr+=sizeof(CHAR) * channels;
                //fread(pColors, sizeof(byte) * channels, 1, pFile);
                
                while(rleID)
                {
                    Ptr[colorsRead + 0] = pColors[2];
                    Ptr[colorsRead + 1] = pColors[1];
                    Ptr[colorsRead + 2] = pColors[0];
                    
                    if(bits == 32)
                        Ptr[colorsRead + 3] = pColors[3];
                    
                    i++;
                    rleID--;
                    colorsRead += channels;
                }
                
            }
            
        }
    }
    return true;
} // LoadTgaFormat

int DecodeTgaBufferToGeneralBuffer(
    const unsigned char* dataBuf,
    unsigned long  len,
    unsigned short* width,
    unsigned short* height,
    unsigned short* nchannels,
    unsigned char** buffer)
{
    CHAR* cPtr    = (CHAR*)dataBuf;
    DWORD padding = 0; 
    DWORD offset  = 0; 
    DWORD imageSize = 0;
    unsigned short nc = 0;
    int imageType = 0;
    /*
    if (s_buffer != NULL)
    {
        assert(s_buffer == NULL);
        return false;
    }
    */
    assert(width && height && nchannels && buffer);

    DWORD length = dataBuf[0];
    imageType = dataBuf[2];
    WORD *p = (WORD *)&dataBuf[12];
    int bits = dataBuf[12+sizeof(WORD)*2];
    switch(bits)
    {
        case 24:
            nc = 3;
            break;
        case 32:
            nc = 4;
            break;
        case 16:
        default:
            assert(0 && "this tga format not supported");
            return false;
    }
    
    // Determine the size in bytes of the image data.
    padding = p[0] & 0x03;
    if(padding > 0)
    {
        padding = 4 - padding;
    }

    imageSize = DWORD(p[0]+padding) * nc * p[1];
    if (imageSize == 0)
        return false;

    //allocate raw memory to read in image
    unsigned char *buf  = new unsigned char[imageSize];
    if (buf  == NULL)
        return false;
    //if lower than 16,24,32 bit colour, read in the palette.
    if(bits <= 8)
    {
        assert(0 && "sorry! 256 color TGA not surport here.");
    }

    switch(imageType)
    {
        case 2://  -  未压缩的，rgb 图像
        case 3://  -  未压缩的，黑白图像
        case 10:// -  runlength 编码的 rgb 图像
            if(!LoadTgaFormat((const CHAR *)dataBuf+18+length, buf, imageType, bits, p[0], p[1]))
            {
                CleanupTgaGeneralBuffer(&buf);
                return false;
            }
            break;
        default:
            CleanupTgaGeneralBuffer(&buf);
            return false;
    }
    *width = p[0];
    *height = p[1];
    *nchannels = nc;
    *buffer = buf; 
    return true;
}
/// CleanupTgaGeneralBuffer
/// @note 释放由DecodeTgaFileToGeneralBuffer分配的内存空间
/// @param  unsigned char** buffer      内存块地址
void CleanupTgaGeneralBuffer(unsigned char** buffer)
{
    assert(buffer);

    if (*buffer != NULL)
    {
        delete [] *buffer;
        *buffer = NULL;
    }
} // CleanupJPGGeneralBuffer()

} // Utilities

} // ogl2d