
//          Copyright Michael D. Parker 2018.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

module bindbc.sdl.image;

version(BindSDL_Image):

import bindbc.sdl.bind.sdlerror : SDL_GetError, SDL_SetError;
import bindbc.sdl.bind.sdlrender : SDL_Renderer, SDL_Texture;
import bindbc.sdl.bind.sdlrwops : SDL_RWops;
import bindbc.sdl.bind.sdlsurface : SDL_Surface;
import bindbc.sdl.bind.sdlversion : SDL_version, SDL_VERSIONNUM;

alias IMG_SetError = SDL_SetError;
alias IMG_GetError = SDL_GetError;

enum SDLImageSupport {
    noLibrary,
    badLibrary,
    sdlImage200 = 200,
    sdlImage201,
    sdlImage202,
}

enum ubyte SDL_IMAGE_MAJOR_VERSION = 2;
enum ubyte SDL_IMAGE_MINOR_VERSION = 0;

version(SDL_Image_202) {
    enum sdlImageSupport = SDLImageSupport.sdlImage202;
    enum ubyte SDL_IMAGE_PATCHLEVEL = 2;
}
else version(SDL_Image_201) {
    enum sdlImageSupport = SDLImageSupport.sdlImage201;
    enum ubyte SDL_IMAGE_PATCHLEVEL = 1;
}
else {
    enum sdlImageSupport = SDLImageSupport.sdlImage200;
    enum ubyte SDL_IMAGE_PATCHLEVEL = 0;
}

@nogc nothrow void SDL_IMAGE_VERSION(SDL_version* X)
{
    X.major     = SDL_IMAGE_MAJOR_VERSION;
    X.minor     = SDL_IMAGE_MINOR_VERSION;
    X.patch     = SDL_IMAGE_PATCHLEVEL;
}

// These were implemented in SDL_image 2.0.2, but are fine for all versions.
enum SDL_IMAGE_COMPILEDVERSION = SDL_VERSIONNUM!(SDL_IMAGE_MAJOR_VERSION, SDL_IMAGE_MINOR_VERSION, SDL_IMAGE_PATCHLEVEL);
enum SDL_IMAGE_VERSION_ATLEAST(ubyte X, ubyte Y, ubyte Z) = SDL_IMAGE_COMPILEDVERSION >= SDL_VERSIONNUM!(X, Y, Z);

enum {
    IMG_INIT_JPG    = 0x00000001,
    IMG_INIT_PNG    = 0x00000002,
    IMG_INIT_TIF    = 0x00000004,
    IMG_INIT_WEBP   = 0x00000008,
}

version(BindSDL_Static) {
    extern(C) @nogc nothrow {
        int IMG_Init(int);
        int IMG_Quit();
        const(SDL_version)* IMG_Linked_Version();
        SDL_Surface* IMG_LoadTyped_RW(SDL_RWops*,int,const(char)*);
        SDL_Surface* IMG_Load(const(char)*);
        SDL_Surface* IMG_Load_RW(SDL_RWops*,int);

        SDL_Texture* IMG_LoadTexture(SDL_Renderer*,const(char)*);
        SDL_Texture* IMG_LoadTexture_RW(SDL_Renderer*,SDL_RWops*,int);
        SDL_Texture* IMG_LoadTextureTyped_RW(SDL_Renderer*,SDL_RWops*,int,const(char)*);

        int IMG_isICO(SDL_RWops*);
        int IMG_isCUR(SDL_RWops*);
        int IMG_isBMP(SDL_RWops*);
        int IMG_isGIF(SDL_RWops*);
        int IMG_isJPG(SDL_RWops*);
        int IMG_isLBM(SDL_RWops*);
        int IMG_isPCX(SDL_RWops*);
        int IMG_isPNG(SDL_RWops*);
        int IMG_isPNM(SDL_RWops*);
        int IMG_isTIF(SDL_RWops*);
        int IMG_isXCF(SDL_RWops*);
        int IMG_isXPM(SDL_RWops*);
        int IMG_isXV(SDL_RWops*);
        int IMG_isWEBP(SDL_RWops*);

        SDL_Surface* IMG_LoadICO_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadCUR_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadBMP_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadGIF_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadJPG_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadLBM_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadPCX_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadPNG_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadPNM_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadTGA_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadTIF_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadXCF_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadXPM_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadXV_RW(SDL_RWops*);
        SDL_Surface* IMG_LoadWEBP_RW(SDL_RWops*);

        SDL_Surface* IMG_ReadXPMFromArray(char**);

        int IMG_SavePNG(SDL_Surface*,const(char)*);
        int IMG_SavePNG_RW(SDL_Surface*,SDL_RWops*,int);

        static if(sdlImageSupport >= SDLImageSupport.sdlImage202) {
            int IMG_isSVG(SDL_RWops*);
            SDL_Surface* IMG_LoadSVG(SDL_RWops*);
            int IMG_SaveJPG(SDL_Surface*,const(char)*,int);
            int IMG_SaveJPG_RW(SDL_Surface*,SDL_RWops*,int,int);
        }
    }
}
else {
    import bindbc.loader;

    extern(C) @nogc nothrow {
        alias pIMG_Init = int function(int);
        alias pIMG_Quit = int function();
        alias pIMG_Linked_Version = const(SDL_version)* function();
        alias pIMG_LoadTyped_RW = SDL_Surface* function(SDL_RWops*,int,const(char)*);
        alias pIMG_Load = SDL_Surface* function(const(char)*);
        alias pIMG_Load_RW = SDL_Surface* function(SDL_RWops*,int);

        alias pIMG_LoadTexture = SDL_Texture* function(SDL_Renderer*,const(char)*);
        alias pIMG_LoadTexture_RW = SDL_Texture* function(SDL_Renderer*,SDL_RWops*,int);
        alias pIMG_LoadTextureTyped_RW = SDL_Texture* function(SDL_Renderer*,SDL_RWops*,int,const(char)*);

        alias pIMG_isICO = int function(SDL_RWops*);
        alias pIMG_isCUR = int function(SDL_RWops*);
        alias pIMG_isBMP = int function(SDL_RWops*);
        alias pIMG_isGIF = int function(SDL_RWops*);
        alias pIMG_isJPG = int function(SDL_RWops*);
        alias pIMG_isLBM = int function(SDL_RWops*);
        alias pIMG_isPCX = int function(SDL_RWops*);
        alias pIMG_isPNG = int function(SDL_RWops*);
        alias pIMG_isPNM = int function(SDL_RWops*);
        alias pIMG_isTIF = int function(SDL_RWops*);
        alias pIMG_isXCF = int function(SDL_RWops*);
        alias pIMG_isXPM = int function(SDL_RWops*);
        alias pIMG_isXV = int function(SDL_RWops*);
        alias pIMG_isWEBP = int function(SDL_RWops*);

        alias pIMG_LoadICO_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadCUR_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadBMP_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadGIF_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadJPG_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadLBM_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadPCX_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadPNG_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadPNM_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadTGA_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadTIF_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadXCF_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadXPM_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadXV_RW = SDL_Surface* function(SDL_RWops*);
        alias pIMG_LoadWEBP_RW = SDL_Surface* function(SDL_RWops*);

        alias pIMG_ReadXPMFromArray = SDL_Surface* function(char**);

        alias pIMG_SavePNG = int function(SDL_Surface*,const(char)*);
        alias pIMG_SavePNG_RW = int function(SDL_Surface*,SDL_RWops*,int);
    }

    __gshared {
        pIMG_Init IMG_Init;
        pIMG_Quit IMG_Quit;
        pIMG_Linked_Version IMG_Linked_Version;
        pIMG_LoadTyped_RW IMG_LoadTyped_RW;
        pIMG_Load IMG_Load;
        pIMG_Load_RW IMG_Load_RW;
        pIMG_LoadTexture IMG_LoadTexture;
        pIMG_LoadTexture_RW IMG_LoadTexture_RW;
        pIMG_LoadTextureTyped_RW IMG_LoadTextureTyped_RW;
        pIMG_isICO IMG_isICO;
        pIMG_isCUR IMG_isCUR;
        pIMG_isBMP IMG_isBMP;
        pIMG_isGIF IMG_isGIF;
        pIMG_isJPG IMG_isJPG;
        pIMG_isLBM IMG_isLBM;
        pIMG_isPCX IMG_isPCX;
        pIMG_isPNG IMG_isPNG;
        pIMG_isPNM IMG_isPNM;
        pIMG_isTIF IMG_isTIF;
        pIMG_isXCF IMG_isXCF;
        pIMG_isXPM IMG_isXPM;
        pIMG_isXV IMG_isXV;
        pIMG_isWEBP IMG_isWEBP;
        pIMG_LoadICO_RW IMG_LoadICO_RW;
        pIMG_LoadCUR_RW IMG_LoadCUR_RW;
        pIMG_LoadBMP_RW IMG_LoadBMP_RW;
        pIMG_LoadGIF_RW IMG_LoadGIF_RW;
        pIMG_LoadJPG_RW IMG_LoadJPG_RW;
        pIMG_LoadLBM_RW IMG_LoadLBM_RW;
        pIMG_LoadPCX_RW IMG_LoadPCX_RW;
        pIMG_LoadPNG_RW IMG_LoadPNG_RW;
        pIMG_LoadPNM_RW IMG_LoadPNM_RW;
        pIMG_LoadTGA_RW IMG_LoadTGA_RW;
        pIMG_LoadTIF_RW IMG_LoadTIF_RW;
        pIMG_LoadXCF_RW IMG_LoadXCF_RW;
        pIMG_LoadXPM_RW IMG_LoadXPM_RW;
        pIMG_LoadXV_RW IMG_LoadXV_RW;
        pIMG_LoadWEBP_RW IMG_LoadWEBP_RW;
        pIMG_ReadXPMFromArray IMG_ReadXPMFromArray;
        pIMG_SavePNG IMG_SavePNG;
        pIMG_SavePNG_RW IMG_SavePNG_RW;
    }

    static if(sdlImageSupport >= SDLImageSupport.sdlImage202) {
        extern(C) @nogc nothrow {
            alias pIMG_isSVG = int function(SDL_RWops*);
            alias pIMG_LoadSVG_RW = SDL_Surface* function(SDL_RWops*);
            alias pIMG_SaveJPG = int function(SDL_Surface*,const(char)*,int);
            alias pIMG_SaveJPG_RW = int function(SDL_Surface*,SDL_RWops*,int,int);
        }

        __gshared {
            pIMG_isSVG IMG_isSVG;
            pIMG_LoadSVG_RW IMG_LoadSVG;
            pIMG_SaveJPG IMG_SaveJPG;
            pIMG_SaveJPG_RW IMG_SaveJPG_RW;
        }
    }

    private {
        SharedLib lib;
        SDLImageSupport loadedVersion;
    }

    void unloadSDLImage()
    {
        if(lib != invalidHandle) {
            lib.unload();
        }
    }

    SDLImageSupport loadedSDLImageVersion() { return loadedVersion; }

    bool isSDLImageLoaded()
    {
        return  lib != invalidHandle;
    }


    SDLImageSupport loadSDLImage()
    {
        version(Windows) {
            const(char)[][1] libNames = ["SDL2_image.dll"];
        }
        else version(OSX) {
            const(char)[][6] libNames = [
                "libSDL2_image.dylib",
                "/usr/local/lib/libSDL2_image.dylib",
                "../Frameworks/SDL2_image.framework/SDL2_image",
                "/Library/Frameworks/SDL2_image.framework/SDL2_image",
                "/System/Library/Frameworks/SDL2_image.framework/SDL2_image",
                "/opt/local/lib/libSDL2_image.dylib"
            ];
        }
        else version(Posix) {
            const(char)[][6] libNames = [
                "libSDL2_image.so",
                "/usr/local/lib/libSDL2_image.so",
                "libSDL2_image-2.0.so",
                "/usr/local/lib/libSDL2_image-2.0.so",
                "libSDL2_image-2.0.so.0",
                "/usr/local/lib/libSDL2_image-2.0.so.0"
            ];
        }
        else static assert(0, "bindbc-sdl is not yet supported on this platform.");

        SDLImageSupport ret;
        foreach(name; libNames) {
            ret = loadSDLImage(name.ptr);
            if(ret != SDLImageSupport.noLibrary) break;
        }
        return ret;
    }

    SDLImageSupport loadSDLImage(const(char)* libName)
    {
        lib = load(libName);
        if(lib == invalidHandle) {
            return SDLImageSupport.noLibrary;
        }

        auto errCount = errorCount();
        loadedVersion = SDLImageSupport.badLibrary;

        lib.bindSymbol(cast(void**)&IMG_Init,"IMG_Init");
        lib.bindSymbol(cast(void**)&IMG_Quit,"IMG_Quit");
        lib.bindSymbol(cast(void**)&IMG_Linked_Version,"IMG_Linked_Version");
        lib.bindSymbol(cast(void**)&IMG_LoadTyped_RW,"IMG_LoadTyped_RW");
        lib.bindSymbol(cast(void**)&IMG_Load,"IMG_Load");
        lib.bindSymbol(cast(void**)&IMG_Load_RW,"IMG_Load_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadTexture,"IMG_LoadTexture");
        lib.bindSymbol(cast(void**)&IMG_LoadTexture_RW,"IMG_LoadTexture_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadTextureTyped_RW,"IMG_LoadTextureTyped_RW");
        lib.bindSymbol(cast(void**)&IMG_isICO,"IMG_isICO");
        lib.bindSymbol(cast(void**)&IMG_isCUR,"IMG_isCUR");
        lib.bindSymbol(cast(void**)&IMG_isBMP,"IMG_isBMP");
        lib.bindSymbol(cast(void**)&IMG_isGIF,"IMG_isGIF");
        lib.bindSymbol(cast(void**)&IMG_isJPG,"IMG_isJPG");
        lib.bindSymbol(cast(void**)&IMG_isLBM,"IMG_isLBM");
        lib.bindSymbol(cast(void**)&IMG_isPCX,"IMG_isPCX");
        lib.bindSymbol(cast(void**)&IMG_isPNG,"IMG_isPNG");
        lib.bindSymbol(cast(void**)&IMG_isPNM,"IMG_isPNM");
        lib.bindSymbol(cast(void**)&IMG_isTIF,"IMG_isTIF");
        lib.bindSymbol(cast(void**)&IMG_isXCF,"IMG_isXCF");
        lib.bindSymbol(cast(void**)&IMG_isXPM,"IMG_isXPM");
        lib.bindSymbol(cast(void**)&IMG_isXV,"IMG_isXV");
        lib.bindSymbol(cast(void**)&IMG_isWEBP,"IMG_isWEBP");
        lib.bindSymbol(cast(void**)&IMG_LoadICO_RW,"IMG_LoadICO_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadCUR_RW,"IMG_LoadCUR_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadBMP_RW,"IMG_LoadBMP_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadGIF_RW,"IMG_LoadGIF_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadJPG_RW,"IMG_LoadJPG_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadLBM_RW,"IMG_LoadLBM_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadPCX_RW,"IMG_LoadPCX_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadPNG_RW,"IMG_LoadPNG_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadPNM_RW,"IMG_LoadPNM_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadTGA_RW,"IMG_LoadTGA_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadTIF_RW,"IMG_LoadTIF_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadXCF_RW,"IMG_LoadXCF_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadXPM_RW,"IMG_LoadXPM_RW");
        lib.bindSymbol(cast(void**)&IMG_LoadXV_RW,"IMG_LoadXV_RW");
        lib.bindSymbol(cast(void**)&IMG_isXV,"IMG_isXV");
        lib.bindSymbol(cast(void**)&IMG_LoadWEBP_RW,"IMG_LoadWEBP_RW");
        lib.bindSymbol(cast(void**)&IMG_SavePNG,"IMG_SavePNG");
        lib.bindSymbol(cast(void**)&IMG_SavePNG_RW,"IMG_SavePNG_RW");

        if(errorCount() != errCount) return SDLImageSupport.badLibrary;
        else loadedVersion = SDLImageSupport.sdlImage200;

        static if(sdlImageSupport >= SDLImageSupport.sdlImage202) {
            lib.bindSymbol(cast(void**)&IMG_isSVG,"IMG_isSVG");
            lib.bindSymbol(cast(void**)&IMG_LoadSVG,"IMG_LoadSVG_RW");
            lib.bindSymbol(cast(void**)&IMG_SaveJPG,"IMG_SaveJPG");
            lib.bindSymbol(cast(void**)&IMG_SaveJPG_RW,"IMG_SaveJPG_RW");

            if(errorCount() != errCount) return SDLImageSupport.badLibrary;
            else loadedVersion = SDLImageSupport.sdlImage202;
        }

        return loadedVersion;
    }
}
