{-#LANGUAGE MultiParamTypeClasses
  , TypeSynonymInstances
  , FlexibleInstances
  , FlexibleContexts   
  , NoMonomorphismRestriction
  , OverlappingInstances
  , OverloadedStrings
  , UndecidableInstances
  , FunctionalDependencies
 #-}

module Text.Twine.Interpreter.Context (emptyContext, TemplateInterface (..)) where

import Data.ByteString.Char8 (ByteString)
import Data.Maybe
import Data.Monoid
import Text.Twine.Interpreter.Types
import qualified Data.ByteString.Char8 as C
import Control.Monad.Writer
import qualified Data.Map as M

class (Monad m) => TemplateInterface m a | a -> m where
    property      :: ByteString -> a -> m (TwineElement m)
    makeIterable :: a -> m [TwineElement m]
    makeString   :: a -> m String
    bind         :: (TemplateInterface m a) => a -> TwineElement m

    property _ _ = return TwineNull
    makeIterable _ = return []
    makeString   _ = return ""
    bind a = TwineObject $ Context {
      getContext  = (flip property a),
      getIterable = makeIterable a,
      getString   = makeString a
      }


instance (Monad m) => TemplateInterface m (TwineElement m) where
  bind = id
  makeString = return . show

instance (Monad m) => TemplateInterface m ([TwineElement m] -> m (TwineElement m)) where
  bind = TwineFunction

instance (Monad m) => TemplateInterface m EmptyContext 

instance (Monad m, TemplateInterface m a) => TemplateInterface m (Maybe a) where
  bind (Just a)  = bind a
  bind Nothing   = TwineNull

instance (Monad m) => TemplateInterface m [(ByteString,TwineElement m)] where
  property k = return . fromMaybe TwineNull . lookup k

instance (Monad m) => TemplateInterface m String where
  bind = TwineString . C.pack

instance (Monad m) => TemplateInterface m ByteString where
  bind a = TwineString a

instance (Monad m) => TemplateInterface m Bool where
  bind = TwineBool

instance (Monad m) => TemplateInterface m (M.Map ByteString (TwineElement m)) where
  property k = return . fromMaybe (TwineNull) . M.lookup k

emptyContext :: (Monad m) => TwineElement m
emptyContext = bind EmptyContext