#include "texture_jpeg.h"
#include "../driver/texture.h"
#include "../compile.h"

#ifdef texture_loader_jpeg_support
    #include <jpeglib.h>
    #include <exception>
#endif

namespace sleek
{
    namespace loader
    {
        #ifdef texture_loader_jpeg_support
            struct jpegErrorManager
            {
                struct jpeg_error_mgr pub;
            };
            void jpegErrorExit (j_common_ptr cinfo)
            {
                char jpegLastErrorMsg[JMSG_LENGTH_MAX];
                jpegErrorManager* myerr = (jpegErrorManager*) cinfo->err;
                /*(* (cinfo->err->output_message) ) (cinfo);*/
                ( *(cinfo->err->format_message) ) (cinfo, jpegLastErrorMsg);
                throw std::runtime_error( jpegLastErrorMsg );
            }
        #endif
        std::shared_ptr<driver::texture> textureloader_jpeg::read(io::filesystem *fs, const std::string &filename) const noexcept
        {
            #ifdef texture_loader_jpeg_support
                auto in = fs->read(filename);

                if(!in)
                {
                    printf("error: couldn't open \"%s\"!\n", filename.c_str());
                    return nullptr;
                }

                std::shared_ptr<driver::texture> img = nullptr;
                std::string data = in->readAll();

                jpeg_decompress_struct cinfo;

                jpegErrorManager jerr;
                cinfo.err = jpeg_std_error(&jerr.pub);
                jerr.pub.error_exit = jpegErrorExit;

                try
                {
                    jpeg_create_decompress(&cinfo);
                    jpeg_mem_src(&cinfo, (unsigned const char*)data.c_str(), data.size());

                    jpeg_read_header(&cinfo, TRUE);
                    jpeg_start_decompress(&cinfo);

                    img = std::make_shared<driver::texture>(
                        math::vec2i(cinfo.image_width, cinfo.image_height),
                        (driver::TextureFormat)cinfo.num_components
                    );

                    unsigned char* cdata = img->getBuffer();
                    unsigned char** p2 = &cdata;
                    int numlines = 0;

                    printf("Load texture: \"%s\"\n", filename.c_str());
                    while(cinfo.output_scanline < cinfo.output_height)
                    {
                        numlines = jpeg_read_scanlines(&cinfo, p2, 1);
                        *p2 += numlines * 3 * cinfo.output_width;
                    }
                }
                catch(std::exception e)
                {
                    printf("Texture error %s", e.what());
                }

                jpeg_destroy_decompress(&cinfo);
                return img;
            #else
                return nullptr;
            #endif
        }

        bool textureloader_jpeg::write(io::filesystem *fs, driver::texture*, const std::string&) const noexcept
        {
            return false;
        }

        bool textureloader_jpeg::match(const std::string &filename) const noexcept
        {
            return filename.substr(filename.find_last_of('.')+1, 3) == "jpg" ||
                   filename.substr(filename.find_last_of('.')+1, 4) == "jpeg";
        }
    }
}