{-# LANGUAGE PackageImports, PatternGuards, ExplicitForAll  #-} 

-- | Reading and writing arrays as uncompressed 24 or 32 bit Windows BMP files.
module Data.Array.Repa.IO.BMP
	( readImageFromBMP
        , writeImageToBMP)
where
import Data.Array.Repa				as R
import Data.Array.Repa.Repr.ForeignPtr		as R
import Data.Array.Repa.Repr.ByteString		as R
import Data.Vector.Unboxed                      as U
import Prelude					as P
import Foreign.ForeignPtr
import Foreign.Marshal.Alloc
import Data.ByteString.Unsafe                   as B
import Codec.BMP
import Data.Word

-- NOTE: We set most of these functions as NOINLINE so it's easier to understand
--       what's going on in the core programs. The top-level IO functions are
--       only called a few times each, so it doesn't matter if they're not
--       worker/wrappered etc.
	
-- Read -------------------------------------------------------------------------------------------
-- | Read RGB components from a BMP file.
readImageFromBMP
	:: FilePath
	-> IO (Either Error (Array U DIM2 (Word8, Word8, Word8)))

{-# NOINLINE readImageFromBMP #-}
readImageFromBMP filePath
 = do   ebmp	<- readBMP filePath

	case ebmp of
	 Left err	-> return $ Left err
	 Right bmp	
	  -> do arr     <- readImageFromBMP' bmp
	        return  $ Right arr

readImageFromBMP' bmp
 = do	let (width, height) = bmpDimensions bmp

	let arr		= R.fromByteString (Z :. height :. width * 4)
			$ unpackBMPToRGBA32 bmp

	let shapeFn _ 	= Z :. height :. width

        -- Read out the components into their own arrays, 
        -- skipping the alpha channel.
	vecRed         <- now $ computeP 
                        $ traverse arr shapeFn
                	       (\get (sh :. x) -> get (sh :. (x * 4)))

	vecGreen       <- now $ computeP 
                        $ traverse arr shapeFn
                		(\get (sh :. x) -> get (sh :. (x * 4 + 1)))

	vecBlue        <- now $ computeP
                        $ traverse arr shapeFn
                		(\get (sh :. x) -> get (sh :. (x * 4 + 2)))

	-- O(1). zip the components together
	let vecRGB     = U.zip3 (toUnboxed vecRed)
                                (toUnboxed vecGreen)
                                (toUnboxed vecBlue)
	
        return $ fromUnboxed (Z :. height :. width) vecRGB



-- | Write RGB components to a BMP file.
writeImageToBMP
	:: FilePath
	-> Array U DIM2 (Word8, Word8, Word8)
	-> IO ()

{-# NOINLINE writeImageToBMP #-}
writeImageToBMP fileName arrRGB
 = do   let sh@(Z :. height :. width)
                        = extent arrRGB

        -- O(1). unzip the components
	let (vecRed, vecGreen, vecBlue)
	        = U.unzip3 $ toUnboxed arrRGB


        -- Create a bytestring with all the data
        ptr     <- mallocBytes (height * width * 4)
        fptr    <- newForeignPtr finalizerFree ptr

        computeIntoP fptr 
         $ interleave4 
	        (fromUnboxed sh vecRed)
	        (fromUnboxed sh vecGreen)
	        (fromUnboxed sh vecBlue)
	        (fromFunction sh (\_ -> 255))

        -- Pack the data into a BMP file and write it out.
        withForeignPtr fptr
         $ \ptr' -> do
                bs      <- unsafePackCStringFinalizer ptr' (width * height * 4) (return ())
                let bmp = packRGBA32ToBMP width height bs
                writeBMP fileName bmp


{-
-- Normalise --------------------------------------------------------------------------------------
-- | Normalise a matrix to to [0 .. 1], discarding negative values.
--	If the maximum value is 0 then return the array unchanged.
normalisePositive01
	:: (Shape sh, Fractional a, Ord a)
	=> Array sh a
	-> Array sh a

{-# INLINE normalisePositive01 #-}
normalisePositive01 arr	
 = let	mx		= foldAll max 0 arr
   	elemFn x
	 | x >= 0	= x / mx
	 | otherwise	= x
   in	mx `seq`
	 if mx == 0 
	  then arr
	  else R.map elemFn arr

-}