{-# LANGUAGE FlexibleInstances #-}

-- | This module provides access to image resources from files or base64
-- encoded strings.
module HTk.Components.Image (

  HasPhoto(..),
  Image,
  newImage,

  intToImage,
  imageToInt,

  Format(..),
  imgData,
  imgGamma,
  imgPalette
) where

import HTk.Kernel.Core
import HTk.Kernel.BaseClasses(Widget)
import HTk.Kernel.Configuration
import Util.Computation
import Events.Synchronized
import Events.Destructible

-- -----------------------------------------------------------------------
-- class image
-- -----------------------------------------------------------------------

-- | Image containers instantiate the @class HasPhoto@.
class GUIObject w => HasPhoto w where
  -- Associates an image container (e.g. a label) with the given image.
  photo           :: Image -> Config w
  -- Gets the image associated with the given image container.
  getPhoto        :: w -> IO (Maybe Image)
  photo img w     = imageToInt img >>= cset w "image"
  getPhoto w      = cget w "image" >>= intToImage


-- -----------------------------------------------------------------------
-- type image
-- -----------------------------------------------------------------------

-- | The @Image@ datatype.
newtype Image = Image GUIOBJECT deriving Eq


-- -----------------------------------------------------------------------
-- constructor
-- -----------------------------------------------------------------------

-- | Constructs a new image object and returns a handler.
-- The image object can be packed like a widget, then it is implicitely
-- displayed inside a label widget.
newImage :: [Config Image]
   -- ^ the list of configuration options for this image
   -- object.
   -> IO Image
   -- ^ An image object.
newImage cnf =
  do
    w <- createWidget ROOT LABEL
    configure (Image w) cnf

-- | Sets the image data from a base64 encoded string.
imgData :: Format -> String  -> Config Image
imgData f str w =
    execTclScript [tkImageCreateFromData no f str] >> cset w "image" no
  where no = getObjectNo (toGUIObject w)

-- | The @Format@ datatype - represents the format of a base64
-- encoded image (see @Image.imgData@).
data Format = GIF | PPM | PGM

formatToString :: Format -> String
formatToString f =
  case f of
    GIF -> "GIF"
    PPM -> "PPM"
    _   -> "PGM"


-- | The @gamma@ correction factor. Values less than one
-- darken the image, values greater than one brighten up the image.
imgGamma :: Double -> Config Image
imgGamma g = tkImgConfig ("-gamma "++ show g)

-- | The colour palette specifies a private palette for this image.
-- You can either specify a grayscale palette (of n shades of grey), or an
-- RGB triple.
class PaletteSpec p where
  -- Internal function only
  tkShowPalette :: p-> String

instance PaletteSpec Int where
  tkShowPalette p = show p

instance PaletteSpec (Int, Int, Int) where
  tkShowPalette (r, g, b) = show r ++ "/"++ show g++ "/"++ show b

imgPalette :: PaletteSpec p=> p-> Config Image
imgPalette p = tkImgConfig ("-palette "++ tkShowPalette p)


-- We leave the  getImgGamma and getImgPalette functions as exercises
-- to the interested reader of this source code.





-- -----------------------------------------------------------------------
-- instantiations
-- -----------------------------------------------------------------------

-- | Internal.
instance GUIObject Image where
  toGUIObject (Image w) = w
  cname _ = "Image"

-- | An image object can be destroyed.
instance Destroyable Image where
  -- Destroys an image object.
  destroy = destroy . toGUIObject

-- | An image object has standard widget properties
-- (concerning focus, cursor \/ if implicitely displayed inside a label
-- widget).
instance Widget Image

-- | An image object has a configureable border (if implicitely displayed
-- inside a label widget).
instance HasBorder Image

-- | An image object has a configureable foreground and background colour
-- (if implicitely displayed inside a label widget).
instance HasColour Image where
  legalColourID = hasForeGroundColour

-- | You can specify the size of the containing label, if the image is
-- implicitely displayed inside a label widget.
instance HasSize Image

-- | Images can be read from files.
instance HasFile Image where
  -- Specifies the image file path.
  filename str w =
    execTclScript [tkImageCreate no str] >> cset w "image" no
    where no = getObjectNo (toGUIObject w)
  -- Gets the image\'s file name.
  getFileName w = evalTclScript [tkGetImageFile no]
    where no = getObjectNo (toGUIObject w)

-- | You can synchronize on an image object.
instance Synchronized Image where
  -- Synchronizes on an image object.
  synchronize = synchronize . toGUIObject


-- -----------------------------------------------------------------------
-- auxiliary functions
-- -----------------------------------------------------------------------

-- | Internal.
intToImage :: Int -> IO (Maybe Image)
intToImage 0 = return Nothing
intToImage no = lookupGUIObject (ObjectID no) >>= return . Just . Image

{- this function converts the Tk representation of an image to the HTK
   representation. Needed by several other image retrieval function.
-}

-- | Internal.
imageToInt :: Image -> IO Int
imageToInt = return . getObjectNo . toGUIObject


-- -----------------------------------------------------------------------
-- Tk Commands
-- -----------------------------------------------------------------------

tkImageCreate :: Int -> String -> String
tkImageCreate no file = "image create photo " ++ show no ++ " -file " ++ show file
{-# INLINE tkImageCreate #-}

tkGetImageFile :: Int -> String
tkGetImageFile no = (show no) ++ " cget -file "
{-# INLINE tkGetImageFile #-}

tkImageCreateFromData :: Int -> Format -> String -> String
tkImageCreateFromData no f dat = "image create photo " ++ show no ++ " -data " ++ show dat ++ " -format " ++ show (formatToString f)

tkImgConfig :: String-> Config Image
tkImgConfig cstr w =
  do execTclScript [show no++ " configure "++ cstr]
     return w
  where no = getObjectNo (toGUIObject w)
{-# INLINE tkImgConfig #-}