-----------------------------------------------------------------------------
--
-- Module      :  Graphics.GPipe.Texture.Load
-- Copyright   :  Tobias Bexelius
-- License     :  BSD3
--
-- Maintainer  :  Tobias Bexelius
-- Stability   :  Experimental
-- Portability :
--
-- |
-- This module provides the means to load all kinds of textures from file.
-- It's based on the stb-image package, and inherits its strengths and limitations.
-- Specifically, it handles a subset of the JPEG, PNG, TGA, BMP and PSD file formats, but is not
-- entirely thread safe. See <http://hackage.haskell.org/package/stb-image> for more information.
--
-----------------------------------------------------------------------------

module Graphics.GPipe.Texture.Load (
    LoadableTexture(..),
    LoadableTexture3D(..),
) where

import Graphics.GPipe
import Codec.Image.STB
import Data.Either
import Foreign.Ptr (plusPtr)

-- | Provides the general way of loading any kind of textures. A 3D texture is assumed to be an array of square images
-- tiled vertically in the image file. Cube textures are assumed to be composed of 6 equally sized images
-- tiled vertically. No additional mipmaps are loaded.
--
-- Filesystem errors or bad texture dimensions (e.g. loading a cube texture from a file where the height
-- is not a multiple of 6) are thrown as 'IOError's.
class Texture t => LoadableTexture t where
    loadTexture :: TextureFormat t
                -> FilePath
                -> IO t

-- | Provides an alternative way of loading 'Texture3D's that are arrays of non-square images tiled vertically. No additional mipmaps are loaded.
--
-- Filesystem errors or bad texture dimensions (i.e. the height of the image is not a multiple of the supplied depth)
-- are thrown as 'IOError's.
class LoadableTexture3D f where
    loadTexture3D :: Int -- ^ The depth of the resulting 'Texture3D'
                  -> f
                  -> FilePath
                  -> IO (Texture3D f)

loadTexture' comp io path = do image <- loadImage' path comp
                               either
                                  (ioError . userError)
                                  (flip withImage io)
                                  image

texture3DFromImage cpufmt fmt path ptr s@(w,h) comp =
    case quotRem h w of
        (q, 0) -> newTexture cpufmt fmt (w:.w:.q:.()) [ptr]
        _      -> ioError $ userError ("loadTexture: Bad 3D image size " ++ show s ++ " in " ++ show path)
texture2DFromImage cpufmt fmt ptr (w,h) comp = newTexture cpufmt fmt (w:.h:.()) [ptr]
texture1DFromImage cpufmt fmt ptr (w,h) comp = newTexture cpufmt fmt (w*h) [ptr]
textureCubeFromImage cpufmt fmt path ptr s@(w,h) comp =
    case quotRem h 6 of
        (q, 0) -> newTexture cpufmt fmt (w:.q:.()) [ptr `plusPtr` (off*w*q) | off <- [0..5]]
        _      -> ioError $ userError ("loadTexture: Bad cube image size " ++ show s ++ " in " ++ show path)
texture3DFromImage' d cpufmt fmt path ptr s@(w,h) comp =
    case quotRem h d of
        (q, 0) -> newTexture cpufmt fmt (w:.q:.d:.()) [ptr]
        _      -> ioError $ userError ("loadTexture: Bad 3D image size " ++ show s ++ " in " ++ show path)

instance LoadableTexture (Texture3D AlphaFormat) where
    loadTexture fmt path = loadTexture' 1 (texture3DFromImage UnsignedByteFormat fmt path) path
instance LoadableTexture (Texture3D LuminanceFormat) where
    loadTexture fmt path = loadTexture' 1 (texture3DFromImage UnsignedByteFormat fmt path) path
instance LoadableTexture (Texture3D LuminanceAlphaFormat) where
    loadTexture fmt path = loadTexture' 2 (texture3DFromImage (PerComp2 UnsignedByteFormat) fmt path) path
instance LoadableTexture (Texture3D RGBFormat) where
    loadTexture fmt path = loadTexture' 3 (texture3DFromImage (PerComp3 UnsignedByteFormat) fmt path) path
instance LoadableTexture (Texture3D RGBAFormat) where
    loadTexture fmt path = loadTexture' 4 (texture3DFromImage (PerComp4 UnsignedByteFormat) fmt path) path

instance LoadableTexture (Texture2D AlphaFormat) where
    loadTexture fmt = loadTexture' 1 $ texture2DFromImage UnsignedByteFormat fmt
instance LoadableTexture (Texture2D LuminanceFormat) where
    loadTexture fmt = loadTexture' 1 $ texture2DFromImage UnsignedByteFormat fmt
instance LoadableTexture (Texture2D LuminanceAlphaFormat) where
    loadTexture fmt = loadTexture' 2 $ texture2DFromImage (PerComp2 UnsignedByteFormat) fmt
instance LoadableTexture (Texture2D RGBFormat) where
    loadTexture fmt = loadTexture' 3 $ texture2DFromImage (PerComp3 UnsignedByteFormat) fmt
instance LoadableTexture (Texture2D RGBAFormat) where
    loadTexture fmt = loadTexture' 4 $ texture2DFromImage (PerComp4 UnsignedByteFormat) fmt

instance LoadableTexture (Texture1D AlphaFormat) where
    loadTexture fmt = loadTexture' 1 $ texture1DFromImage UnsignedByteFormat fmt
instance LoadableTexture (Texture1D LuminanceFormat) where
    loadTexture fmt = loadTexture' 1 $ texture1DFromImage UnsignedByteFormat fmt
instance LoadableTexture (Texture1D LuminanceAlphaFormat) where
    loadTexture fmt = loadTexture' 2 $ texture1DFromImage (PerComp2 UnsignedByteFormat) fmt
instance LoadableTexture (Texture1D RGBFormat) where
    loadTexture fmt = loadTexture' 3 $ texture1DFromImage (PerComp3 UnsignedByteFormat) fmt
instance LoadableTexture (Texture1D RGBAFormat) where
    loadTexture fmt = loadTexture' 4 $ texture1DFromImage (PerComp4 UnsignedByteFormat) fmt

instance LoadableTexture (TextureCube AlphaFormat) where
    loadTexture fmt path = loadTexture' 1 (textureCubeFromImage UnsignedByteFormat fmt path) path
instance LoadableTexture (TextureCube LuminanceFormat) where
    loadTexture fmt path = loadTexture' 1 (textureCubeFromImage UnsignedByteFormat fmt path) path
instance LoadableTexture (TextureCube LuminanceAlphaFormat) where
    loadTexture fmt path = loadTexture' 2 (textureCubeFromImage (PerComp2 UnsignedByteFormat) fmt path) path
instance LoadableTexture (TextureCube RGBFormat) where
    loadTexture fmt path = loadTexture' 3 (textureCubeFromImage (PerComp3 UnsignedByteFormat) fmt path) path
instance LoadableTexture (TextureCube RGBAFormat) where
    loadTexture fmt path = loadTexture' 4 (textureCubeFromImage (PerComp4 UnsignedByteFormat) fmt path) path

instance LoadableTexture3D AlphaFormat where
    loadTexture3D d fmt path = loadTexture' 1 (texture3DFromImage' d UnsignedByteFormat fmt path) path
instance LoadableTexture3D LuminanceFormat where
    loadTexture3D d fmt path = loadTexture' 1 (texture3DFromImage' d UnsignedByteFormat fmt path) path
instance LoadableTexture3D LuminanceAlphaFormat where
    loadTexture3D d fmt path = loadTexture' 2 (texture3DFromImage' d (PerComp2 UnsignedByteFormat) fmt path) path
instance LoadableTexture3D RGBFormat where
    loadTexture3D d fmt path = loadTexture' 3 (texture3DFromImage' d (PerComp3 UnsignedByteFormat) fmt path) path
instance LoadableTexture3D RGBAFormat where
    loadTexture3D d fmt path = loadTexture' 4 (texture3DFromImage' d (PerComp4 UnsignedByteFormat) fmt path) path