"""Image loaders."""
from .common import SDLError
from .compat import UnsupportedError, byteify
from .. import endian, surface, pixels

_HASPIL = True
try:
    from PIL import Image
except ImportError:
    _HASPIL = False

_HASSDLIMAGE = True
try:
    from .. import sdlimage
except ImportError:
    _HASSDLIMAGE = False

__all__ = ["get_image_formats", "load_image"]


def get_image_formats():
    """Gets the formats supported by mule in the default
    installation.
    """
    return ("bmp", "cur", "gif", "ico", "jpg", "lbm", "pbm", "pcx", "pgm",
            "png", "pnm", "ppm", "tga", "tif", "webp", "xcf", "xpm")


def load_image(fname, enforce=None):
    """Creates a SDL_Surface from an image file.

    This function makes use of the Python Imaging Library, if it is available
    on the target execution environment. The function will try to load the
    file via mule.sdlimage first. If the file could not be loaded, it will
    try to load it via PIL.

    You can force the function to use only one of them, by passing the enforce
    as either "PIL" or "SDL".

    Note: This will call mule.sdlimage.init() implicitly with the
    default arguments, if the module is available.
    """
    if enforce is not None and enforce not in ("PIL", "SDL"):
        raise ValueError("enforce must be either 'PIL' or 'SDL', if set")

    if not _HASPIL and not _HASSDLIMAGE:
        raise UnsupportedError(load_image,
                               "cannot use PIL or SDL for image loading")
    if enforce == "PIL" and not _HASPIL:
        raise UnsupportedError(load_image, "cannot use PIL (not found)")
    if enforce == "SDL" and not _HASSDLIMAGE:
        raise UnsupportedError(load_image, "cannot use SDL_image (not found)")

    imgsurface = None
    if enforce != "PIL" and _HASSDLIMAGE:
        sdlimage.IMG_Init(sdlimage.IMG_INIT_JPG | sdlimage.IMG_INIT_PNG |
                          sdlimage.IMG_INIT_TIF | sdlimage.IMG_INIT_WEBP)
        imgsurface = sdlimage.IMG_Load(byteify(fname, "utf-8"))
        if not imgsurface:
            # An error occured - if we do not try PIL, break out now
            if not _HASPIL or enforce == "SDL":
                raise SDLError(sdlimage.IMG_GetError())
        else:
            imgsurface = imgsurface.contents

    if enforce != "SDL" and _HASPIL and not imgsurface:
        image = Image.open(fname)
        mode = image.mode
        width, height = image.size
        rmask = gmask = bmask = amask = 0
        if mode in ("1", "L", "P"):
            # 1 = B/W, 1 bit per byte
            # "L" = greyscale, 8-bit
            # "P" = palette-based, 8-bit
            pitch = width
            depth = 8
        elif mode == "RGB":
            # 3x8-bit, 24bpp
            if endian.SDL_BYTEORDER == endian.SDL_LIL_ENDIAN:
                rmask = 0x0000FF
                gmask = 0x00FF00
                bmask = 0xFF0000
            else:
                rmask = 0xFF0000
                gmask = 0x00FF00
                bmask = 0x0000FF
            depth = 24
            pitch = width * 3
        elif mode in ("RGBA", "RGBX"):
            # RGBX: 4x8-bit, no alpha
            # RGBA: 4x8-bit, alpha
            if endian.SDL_BYTEORDER == endian.SDL_LIL_ENDIAN:
                rmask = 0x000000FF
                gmask = 0x0000FF00
                bmask = 0x00FF0000
                if mode == "RGBA":
                    amask = 0xFF000000
            else:
                rmask = 0xFF000000
                gmask = 0x00FF0000
                bmask = 0x0000FF00
                if mode == "RGBA":
                    amask = 0x000000FF
            depth = 32
            pitch = width * 4
        else:
            # We do not support CMYK or YCbCr for now
            raise TypeError("unsupported image format")

        pxbuf = image.tostring()
        imgsurface = surface.SDL_CreateRGBSurfaceFrom(pxbuf, width, height,
                                                      depth, pitch, rmask,
                                                      gmask, bmask, amask)
        if not imgsurface:
            raise SDLError()
        imgsurface = imgsurface.contents

        if mode == "P":
            # Create a SDL_Palette for the SDL_Surface
            def _chunk(seq, size):
                for x in range(0, len(seq), size):
                    yield seq[x:x + size]

            rgbcolors = image.getpalette()
            sdlpalette = pixels.SDL_AllocPalette(len(rgbcolors) // 3)
            if not sdlpalette:
                raise SDLError()
            sdlpalette = sdlpalette.contents
            SDL_Color = pixels.SDL_Color
            for idx, (r, g, b) in enumerate(_chunk(rgbcolors, 3)):
                sdlpalette.colors[idx] = SDL_Color(r, g, b)
            ret = surface.SDL_SetSurfacePalette(imgsurface, sdlpalette)
            # This will decrease the refcount on the palette, so it gets
            # freed properly on releasing the SDL_Surface.
            pixels.SDL_FreePalette(sdlpalette)
            if ret != 0:
                raise SDLError()

    return imgsurface
