---------------------------------------------------------------------------
-- |
-- Module      :  Main
-- Copyright   :  (c) alpheccar, 2007
-- License     :  BSD-style 
-- 
-- Maintainer  :  misc@alpheccar.org
-- Stability   :  experimental
-- Portability :  portable 
--
-- Description
--
--	  Portable Pixel Map 
--
-----------------------------------------------------------------------------

module Graphics.IFS.Ppm (
   -- * Color formats
   Color(..)
   -- * Picture Creation
 , createPict
   -- * Some standard colors
 , red
 , green
 , blue
 , white
 , black
   -- * Coloring functions
 , ColorizeFunction
 , binaryColor
 , densityColor
 )
 where

import Data.Word
import Control.Monad
import Data.Array.Unboxed
import System.IO(hPutStr,Handle,openBinaryFile,IOMode(..),hClose)
import Control.Exception(bracket)
import qualified Data.ByteString as B
import Graphics.IFS(drawIFS,IFS)


-- | Image encoded as an unidimensional array and indexed colors.
-- The meaning of the index color is dependent on the choice of a coloring function 
type Image = UArray Int Word8

-- | RGB Color
data Color = RGB Word8 Word8 Word8 deriving(Eq,Ord)

-- | Red color
red :: Color
red = RGB 255 0 0 

-- | Gree color
green :: Color
green = RGB 0 255 0 

-- | Blue color
blue :: Color
blue = RGB 0 0 255

-- | Black color
black :: Color
black = RGB 0 0 0

-- | white color
white :: Color
white = RGB 255 255 255

withFile :: String -> (Handle -> IO a) -> IO a
withFile name = bracket (openBinaryFile name WriteMode) hClose


-- | The type of a coloring functions.
-- The first argument is an index value and the second argument is a list of RGB value.
-- The function is assumed to concatenate a new triple of RGB value to the list
type ColorizeFunction = Word8 -> [Word8] -> [Word8]
	
-- | Binary coloring
binaryColor :: Color -- ^ Background 'Color' for index null
            -> Color -- ^ Foreground 'Color' for index not null
            -> Word8 -- ^ Index
            -> [Word8] -- ^ List of RGB values
            -> [Word8] -- ^ List of RGB values
binaryColor (RGB br bg bb) (RGB r g b) a n | a /= 0 = r:g:b:n
                                           | otherwise = br:bg:bb:n 

-- |  Density coloring with linear interpolation
densityColor :: Int -- ^ Scaling factor
             -> Color -- ^ Background 'Color' for index null
             -> Color -- ^ Foreground 'Color' for index not null
             -> Word8 -- ^ Index
	         -> [Word8] -- ^ List of RGB values
	         -> [Word8] -- ^ List of RGB values
densityColor m (RGB br bg bb) (RGB r g b) a n | a /= 0 = (scale r):(scale g):(scale b):n                                            
                                              | otherwise = br:bg:bb:n  
 where
    scale r = fromInteger $ floor $ (min ((fromIntegral a) / (fromIntegral m))  (1.0::Double)) * (fromIntegral r) 

-- | Create a PPM picture.
createPict :: String -- ^ Name of file
           -> Int -- ^ Picture width
           -> Int -- ^ Picture height
           -> Int -- ^ Nb of pixels to compute
           -> ColorizeFunction -- ^ How to colorize the result
           -> IFS Double -- ^ IFS
           -> IO() -- ^ Output action
createPict name width height nb colorize ifs = do
   withFile name $ \h -> do
        hPutStr h ("P6 " ++ show width ++ " " ++ show height ++ " 255\n")
        B.hPut h $ B.pack $ foldr colorize [] $ elems $ (accumArray (+) 0 (0,width*height) $ drawIFS width height nb ifs :: Image)