#include "texture_bmp.h"
#include "../driver/texture.h"
#include "../compile.h"
#include <stdlib.h>

namespace sleek
{
    namespace loader
    {
        #define CTOI(C) (*(int*)&C)

        std::shared_ptr<driver::texture> textureloader_bmp::read(io::filesystem *fs, const std::string &file) const noexcept
        {
            #ifdef texture_loader_bmp_support
            unsigned char header[0x36];
            int colorMode, imagePos;
            long imageSize, imageIdx, m_width, m_height;
            unsigned char m_bitCount;

            auto in = fs->read(file);
            if(!in)
            {
                printf("error: couldn't open \"%s\"!\n", file.c_str());
                return nullptr;
            }

            in->read(header, 0x36);
            if(header[0]!='B' || header[1]!='M')
            {
                printf(
                    "error: Can't find BMP headler in \"%s\"!\n",
                    file.c_str()
                );
                return nullptr;
            }

            if(CTOI(header[0x1E]) < 0 || CTOI(header[0x1E]) > 3)
            {
                printf(
                    "error: \"%s\" unsuported compressed mode %d!\n",
                    file.c_str(),
                    CTOI(header[0x1E])
                );
                return nullptr;
            }

            printf("Load texture: \"%s\" compressed mode %d\n", file.c_str(), CTOI(header[0x1E]));

            m_bitCount = CTOI(header[0x1C]);
            if(m_bitCount == 16) colorMode = driver::TXFMT_RGB;
            else if(m_bitCount == 24) colorMode = driver::TXFMT_RGB;
            else if(m_bitCount == 32) colorMode = driver::TXFMT_RGBA;
            else colorMode = m_bitCount;

            imagePos = CTOI(header[0x0A]);
            imageSize = CTOI(header[0x22]);

            m_width = CTOI(header[0x12]);
            m_height = CTOI(header[0x16]);

            if(!imageSize) imageSize = m_height* m_width*(m_bitCount/8);
            if(!imagePos) imagePos = 0x36;

            in->seek(imagePos, false);

            auto bmp = std::make_shared<driver::texture>(
                math::vec2i(m_width, m_height),
                (driver::TextureFormat)colorMode
            );

            if(!bmp->getBuffer())
            {
                printf("texture: alocation fail\n");
                return nullptr;
            }

            int pos = in->pos();
            in->read(bmp->getBuffer(), imageSize);
            auto buff = bmp->getBuffer();
//            if(in->pos() - pos != imageSize)
//                return nullptr;

            switch(CTOI(header[0x1E]))
            {
                case 0:
                    switch(m_bitCount)
                    {
                        case 16:
                            for (imageIdx = 0; imageIdx < imageSize; imageIdx += colorMode) // bgr >> rgb
                            {
                                unsigned char colorSwap = buff[imageIdx];
                                buff[imageIdx]   = buff[imageIdx+2];//2
                                buff[imageIdx+1] = buff[imageIdx+1];//1
                                buff[imageIdx+2] = colorSwap;
                            }
                        break;
                        case 24:
                            for (imageIdx = 0; imageIdx < imageSize; imageIdx += colorMode) // bgr >> rgb
                            {
                                unsigned char colorSwap = buff[imageIdx];
                                buff[imageIdx]   = buff[imageIdx+2];//2
                                buff[imageIdx+1] = buff[imageIdx+1];//1
                                buff[imageIdx+2] = colorSwap;
                            }
                        break;
                        case 32:
                            for (imageIdx = 0; imageIdx < imageSize; imageIdx += colorMode) // bgr >> rgb
                            {
                                unsigned char colorSwap = buff[imageIdx];
                                buff[imageIdx]   = buff[imageIdx+2];//2
                                buff[imageIdx+1] = buff[imageIdx+1];//1
                                buff[imageIdx+3] = buff[imageIdx+3];//3
                                buff[imageIdx+2] = colorSwap;
                            }
                    }
                break;
                case 1:
                    //decompress8BitRLE(buff, imageSize, m_width, m_height, colorMode);
                break;
                case 2:
                    //decompress4BitRLE(buff, imageSize, m_width, m_height, colorMode);
                break;
                case 3:
                    switch(m_bitCount)
                    {
                        case 32:
                            for (imageIdx = 0; imageIdx < imageSize; imageIdx += colorMode) // xrgb -> rgb(a = 255)
                            {
                                unsigned char colorSwap = buff[imageIdx+2];
                                buff[imageIdx]   = buff[imageIdx+3];
                                buff[imageIdx+2] = buff[imageIdx+1];
                                buff[imageIdx+1] = colorSwap;
                                buff[imageIdx+3] = 255;
                            }
                        break;
                        default: break;
                    }
                break;
            }
            //printf("byte count %d\n", m_bitCount);

            bmp->flipVertical();

            return bmp;
            #else
                return nullptr;
            #endif
        }

        bool textureloader_bmp::write(io::filesystem *fs, driver::texture *img, const std::string &file) const noexcept
        {
            #ifdef texture_loader_bmp_support
            if(!img)
                return false;

            auto out = fs->write(file);

            if(!out)
                return false;

            unsigned char header[0x36];
                header[0] = 'B';
                header[1] = 'M';
                header[0x1E] = 1;
                header[0x1C] = img->getPitch()*8;
            out->write(header,0x36);

            return true;
            #else
                return false;
            #endif
        }

        bool textureloader_bmp::match(const std::string &filename) const noexcept
        {
//            #ifdef texture_loader_bmp
//            FILE *filePtr;
//            unsigned char header[2];
//            if (!(filePtr = fopen(filename.c_str(), "rb"))){  printf("error: couldn't open \"%s\"!\n", filename.c_str()); return 0; }
//            if (fread(header,1,2,filePtr)!=2) { fclose(filePtr); return 0; }
//            if (header[0]!='B' || header[1]!='M') { fclose(filePtr); return 0; }
//            return true;
//            #else
//                return false;
//            #endif
//            std::cout << filename << std::endl;
//            std::cout << filename.substr(filename.find_last_of('.'), 3) << std::endl;
            return filename.substr(filename.find_last_of('.')+1, 3) == "bmp";
        }
    }
}