{-# LANGUAGE ExistentialQuantification, FlexibleContexts, ImpredicativeTypes, RankNTypes #-}
{-# OPTIONS -Wall #-}

{- | Parameters to functions creating 'GenWid'-s. 

If a 'GenWid' has sub-'GenWid' the parameters will be passed on to those.

-}
module Graphics.UI.WxGeneric.GenericWidget.Parameters
    ( Parms
    , subParms
    , getParent, setParent
    , getJoinLayout, transformLabel
    -- * Initial parameters
    , GenWidParameters(..), defaultParms, toParms
    -- * Labels
    , TransformLabel, mkTransformLabel, idLabel, greedyShortcuts
    )
where

import Graphics.UI.WX
import qualified Graphics.UI.WxGeneric.Layout as L
import qualified Data.Set                     as Set
import qualified Data.Char                    as Char
import Control.Monad.State                    as St

data GenWidParameters = GenWidParameters
    { joinLayout     :: L.JoinLayout
    , labelTransform :: TransformLabel
    }

defaultParms :: (GenWidParameters -> GenWidParameters) -> GenWidParameters
defaultParms f = f (GenWidParameters L.smartLayout (greedyShortcuts []))

-- | Initial parameters. Should only be called at the top-level. If
-- already in a 'GenWid' function then use 'subParms'.
toParms :: Window w -> GenWidParameters -> IO (Parms w)
toParms w parms = do lblTrans <- varCreate (labelTransform parms)
                     return $ Parms w True (joinLayout parms) L.oneColumnLayout lblTrans

data Parms w = Parms
    { pickParent         :: Window w
    , pickIsTop          :: Bool
    , pickTopLayout      :: L.JoinLayout
    , pickLayout         :: L.JoinLayout
    , pickLabelTransform :: Var TransformLabel
    }

-- | When a 'GenWid' has sub-'GenWid' use this function to update
-- 'GenWidParms'.
-- 
-- Currently, only GenericClass.singleConstr uses this function. But,
-- in principle, it should be called by functions using sub-widget.
subParms :: Parms w -> Parms w
subParms parms
    = parms { pickIsTop  = False }

setParent :: Window w -> Parms w' -> Parms w
setParent w parms
    = parms { pickParent = w }

-- | Use this parent when constructing new widgets.
getParent :: Parms w -> Window w
getParent = pickParent

getJoinLayout :: Parms w -> L.JoinLayout
getJoinLayout p =
    case pickIsTop p of
      True  -> pickTopLayout p
      False -> pickLayout p

-- *** Label handling

transformLabel :: Parms w -> String -> IO String
transformLabel parms lbl
    = do lblTrans <- varGet $ pickLabelTransform parms
         case lblTrans of
           TL s f -> do let (newLbl, newSt) = runState (f lbl) s
                        varSet (pickLabelTransform parms) $ TL newSt f
                        return newLbl

data TransformLabel = forall s. TL s (String -> State s String)

mkTransformLabel :: s -> (String -> State s String) -> TransformLabel
mkTransformLabel = TL

idLabel :: TransformLabel
idLabel = mkTransformLabel () return

greedyShortcuts :: [Char] -> TransformLabel
greedyShortcuts usedShortcutLetters = mkTL
    where 
      mkTL = mkTransformLabel (Set.fromList usedShortcutLetters) (State . newLbl)
      newLbl lbl used 
          = case span (\x -> Set.member (Char.toLower x) used || (not $ Char.isAlpha x)) lbl of
              (_, [])               -> (lbl, used) -- no available shortcut letter
              (alreadyUsed, (x:xs)) -> (alreadyUsed ++ ('&':x:xs), Set.insert (Char.toLower x) used)