{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}

-- | HTk\'s /scale/ widget.
-- A simple slider in a through representing a range of numeric values.
module HTk.Widgets.Scale (

  ScaleValue(..),

  Scale,
  newScale,

  digits,
  getDigits,

  interval,
  getInterval,
  intervalTo,
  getIntervalTo,
  intervalFrom,
  getIntervalFrom,

  bigIncrement,
  getBigIncrement,

  showValue,
  getShowValue

) where

import Util.Computation

import Events.Synchronized
import Events.Destructible
import Reactor.ReferenceVariables

import HTk.Kernel.Core
import HTk.Kernel.BaseClasses(Widget)
import HTk.Kernel.Configuration
import HTk.Kernel.Resources
import HTk.Components.Slider
import HTk.Kernel.Packer
import HTk.Kernel.Tooltip


-- -----------------------------------------------------------------------
-- Scale type
-- -----------------------------------------------------------------------

-- | The @Scale@ datatype.
data Scale a = Scale GUIOBJECT (Ref Double)
-- the position should really be part of the kind attribute of the GUIObject


-- -----------------------------------------------------------------------
-- classes
-- -----------------------------------------------------------------------

-- | Values associated with a scale instansiate the
-- @class ScaleValue@.
class (Num a, GUIValue a) => ScaleValue a where
  toDouble :: a -> Double
  fromDouble :: Double -> a

-- | A double value is a scale value.
instance ScaleValue Double where
  toDouble = id
  fromDouble = id


-- -----------------------------------------------------------------------
-- Scale creation
-- -----------------------------------------------------------------------

-- | Constructs a new scale widget and returns a handler.
newScale :: (GUIValue a, ScaleValue a, Container par) =>
   par
   -- ^ the parent widget, which has to be a container widget.
   -> [Config (Scale a)]
   -- ^ the list of configuration options for this scale
   -- widget.
   -> IO (Scale a)
   -- ^ A scale widget.
newScale par cnf =
  do
    wid <- createGUIObject (toGUIObject par) SCALE scaleMethods
    ref <- newRef 0
    sc <- return (Scale wid ref)
    configure sc (interval (0,100) : cnf)


-- -----------------------------------------------------------------------
-- Configuration options: Instantiations
-- -----------------------------------------------------------------------

-- | Internal.
instance Eq (Scale a) where
  w1 == w2 = (toGUIObject w1) == (toGUIObject w2)

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

-- | A scale widget can be destroyed.
instance Destroyable (Scale a) where
  destroy = destroy . toGUIObject

-- | A scale widget has standard widget properties
-- (concerning focus, cursor).
instance Widget (Scale a)

-- | You can synchronize on a scale widget.
instance Synchronized (Scale a) where
  -- Synchronizes on a scale widget.
  synchronize = synchronize . toGUIObject

-- | A scale widget has a configureable border.
instance HasBorder (Scale a)

-- | A scale widget has a configureable foreground, background and
-- activebackground colour.
instance HasColour (Scale a) where
  legalColourID w "background" = True
  legalColourID w "foreground" = True
  legalColourID w "activebackground" = True
  legalColourID w _ = False

-- | A scale widget is a stateful widget, it can be enabled or disabled.
instance HasEnable (Scale a)

-- | A scale widget has a configureable font.
instance HasFont (Scale a)

-- | A scale widget has a configureable incrementation interval.
instance ScaleValue a => HasIncrement (Scale a) a where
  -- Sets the scale widget\'s incrementation interval.
  increment d w  = cset w "tickinterval" (toDouble d)
  -- Gets the scale widget\'s incrementation interval.
  getIncrement w = cget w "tickinterval" >>= return . fromDouble

-- | A scale widget\'s orientation can either be vertical or horizontal.
instance HasOrientation (Scale a)

-- | A scale widget has a configureable size.
instance HasSize (Scale a) where
  -- Sets the scale widget\'s length.
  height d w  = cset w "length" d
  -- Gets the scale widget\'s length.
  getHeight w = cget w "length"

-- | A scale widget has a configureable slider.
instance HasSlider (Scale a)

-- | A scale widget has a text label.
instance GUIValue v => HasText (Scale a) v where
  -- Sets the text of the scale widget\'s label.
  text s w  = cset w  "label" s
  -- Gets the text of the scale widget\'s label.
  getText w = cget w "label"

-- | A scale widget can have a tooltip.
instance HasTooltip (Scale a)


-- -----------------------------------------------------------------------
--  Scale specific config options
-- -----------------------------------------------------------------------

-- | Sets the number of significant values in the scale widget.
digits :: Int -> Config (Scale a)
digits d w = cset w "digits" d

-- | Gets the number of significant values in the scale widget.
getDigits :: Scale a -> IO Int
getDigits w = cget w "digits"

-- | Sets the maximum value of the scale widget.
intervalTo :: ScaleValue a => a -> Config (Scale a)
intervalTo v w = cset w "to" (toDouble v)

-- | Gets the maximum value of the scale widget.
getIntervalTo :: ScaleValue a => Scale a -> IO a
getIntervalTo w = cget w "to" >>= return . fromDouble

-- | Sets the minimum value of the scale widget.
intervalFrom :: ScaleValue a => a -> Config (Scale a)
intervalFrom v w = cset w "from" (toDouble v)

-- | Gets the minimum value of the scale widget.
getIntervalFrom :: ScaleValue a => Scale a -> IO a
getIntervalFrom w = cget w "from" >>= return . fromDouble

-- | Sets the scale widgets maximum and minumum value.
interval :: ScaleValue a => (a, a) -> Config (Scale a)
interval (b,e) w =
        synchronize w (do{
                cset w "to" (toDouble b);
                cset w "from" (toDouble e)
                })

-- | Gets the scale widgets maximum and minumum value.
getInterval :: ScaleValue a => Scale a -> IO (a,a)
getInterval w =
        synchronize w (do {
                cget w "to" >>= \b ->
                cget w "from" >>= \e ->
                return (fromDouble b,fromDouble e)
                })


-- -----------------------------------------------------------------------
-- Slider specific config options
-- -----------------------------------------------------------------------

-- | A scale\'s slider has a configureable resulution.
instance ScaleValue a => HasIncrement (Slider (Scale a)) a where
        -- Sets the slider\'s resolution.
        increment d w   = cset w "resolution" (toDouble d)
        -- Gets the slider\'s resolution.
        getIncrement w  = cget w "resolution" >>= return . fromDouble

-- | A scale\'s slider has a configureable size.
instance HasSize (Slider (Scale a)) where
        -- Sets the sliders width.
        width d w       = cset w "width" d
        -- Gets the sliders width.
        getWidth w      = cget w "width"
        -- Sets the sliders height.
        height d w      = cset w "sliderlength" d
        -- Gets the sliders height.
        getHeight w     = cget w "sliderlength"

-- | Sets the coarse grain slider adjustment value.
bigIncrement ::  ScaleValue a => a -> Config (Slider (Scale a))
bigIncrement d w = cset w "bigincrement" (toDouble d)

-- | Gets the coarse grain slider adjustment value.
getBigIncrement :: ScaleValue a => (Slider (Scale a)) -> IO a
getBigIncrement w = cget w "bigincrement" >>= return . fromDouble

-- | Shows the sliders value when set.
showValue :: Toggle -> Config (Slider (Scale a))
showValue d w = cset w "showvalue" d

-- | Gets the current showvalue setting.
getShowValue :: (Slider (Scale a)) -> IO Toggle
getShowValue w = cget w "showvalue"


-- -----------------------------------------------------------------------
-- Scale methods
-- -----------------------------------------------------------------------

scaleMethods :: Methods
scaleMethods = defMethods


-- -----------------------------------------------------------------------
-- Tk intrinsics
-- -----------------------------------------------------------------------

tkScaleCmd :: ObjectID -> TclCmd
tkScaleCmd (ObjectID i) = "Scaled " ++ show i
{-# INLINE tkScaleCmd #-}

tkPackScale  _ _ name opts oid binds =
  ("pack " ++ (show name) ++ " " ++ (showConfigs opts))