{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- | This module provides support for embedding javascript generated by jmacro into HSX.
--
-- It provides the following instances:
--
-- > instance (XMLGenerator m, IntegerSupply m) => EmbedAsChild m JStat
-- > instance (IntegerSupply m, IsName n, EmbedAsAttr m (Attr Name String)) => EmbedAsAttr m (Attr n JStat)
-- 
-- In order to ensure that each embedded 'JStat' block has unique
-- variable names, the monad must supply a source of unique
-- names. This is done by adding an instance of 'IntegerSupply' for
-- the monad being used with 'XMLGenerator'.
--
-- For example, an 'IntegerSupply' for 'ServerPartT':
--
-- > instance IntegerSupply (ServerPartT (StateT Integer IO)) where
-- >     nextInteger = nextInteger'
--
-- This variation avoids the use of an extra monad transformer:
--
-- > instance IntegerSupply (ServerPartT IO) where
-- >     nextInteger = fmap (fromIntegral . (`mod` 1024) . hashUnique) (liftIO newUnique)
--
module HSX.JMacro where

import Control.Monad.Trans             (lift)
import Control.Monad.State             (MonadState(get,put))
import HSX.XMLGenerator                (XMLGenerator(..), XMLGen(..), EmbedAsChild(..), EmbedAsAttr(..), IsName(..), Attr(..), Name)
import Language.Javascript.JMacro      (JStat(..), jsToDoc, jsSaturate, renderJs)
import Text.PrettyPrint.HughesPJ       (Style(..), Mode(..), render, renderStyle, style)

class IntegerSupply m where 
    nextInteger :: m Integer

-- | This help function allows you to easily create an 'IntegerSupply'
-- instance for monads that have a 'MonadState' 'Integer' instance.
--
-- For example:
--
-- > instance IntegerSupply (ServerPartT (StateT Integer IO)) where
-- >     nextInteger = nextInteger'
nextInteger' :: (MonadState Integer m) => m Integer
nextInteger' =
    do i <- get
       put (succ i)
       return i

instance (XMLGenerator m, IntegerSupply m) => EmbedAsChild m JStat where
  asChild jstat = 
      do i <- lift nextInteger
         asChild $ genElement (Nothing, "script")
                    [asAttr ("type" := "text/javascript")]
                    [asChild (render $ jsToDoc $ jsSaturate (Just ('i' : show i)) jstat)]


instance (IntegerSupply m, IsName n, EmbedAsAttr m (Attr Name String)) => EmbedAsAttr m (Attr n JStat) where
  asAttr (n := jstat) = 
      do i <- lift nextInteger
         asAttr $ (toName n := (renderStyle lineStyle $ jsToDoc $ jsSaturate (Just ('i' : show i)) jstat))
      where
        lineStyle = style { mode= OneLineMode }