module Text.Domplate.Context where
import qualified Data.Text as T
import qualified Data.HashMap.Strict as M
import Data.ByteString (ByteString)
import Data.Yaml
import Data.Monoid

-- | A key to be read from the context.
type Key = T.Text

-- | An Unplate context. A simple mapping from keys to values.
newtype Context = Ctx (M.HashMap Key Value)
  deriving Show

instance Monoid Context where
  mempty = empty
  mappend (Ctx a) (Ctx b) = Ctx $ M.union a b
  mconcat = Ctx . M.unions . map (\(Ctx ctx) -> ctx)

typeOf :: Value -> String
typeOf (String _) = "text"
typeOf (Number _) = "number"
typeOf (Bool _)   = "boolean"
typeOf (Array _)  = "array"
typeOf (Object _) = "object"
typeOf (Null)     = "null"

-- | Create a context from a JSON object. Throws an error if the value is not
--   an object.
fromJSON :: Value -> Context
fromJSON (Object o) = Ctx o
fromJSON _          = error "Tried to make a context out of a non-object!"

-- | Create a context from a list mapping keys to values.
fromList :: [(Key, Value)] -> Context
fromList = Ctx . M.fromList

-- | Create a JSON object from a context.
toValue :: Context -> Value
toValue (Ctx ctx) = Object ctx

-- | Add a new value to the given context. If the value already exists, it is
--   overwritten.
add :: Key -> Value -> Context -> Context
add k v (Ctx ctx) = Ctx $ M.insert k v ctx

-- | Remove a value from the given context.
remove :: Key -> Context -> Context
remove k (Ctx ctx) = Ctx $ M.delete k ctx

-- | An empty context.
empty :: Context
empty = Ctx M.empty

-- | Look up a value in the top level context.
lookup :: Key -> Context -> Maybe Value
lookup key (Ctx m) = M.lookup key m

-- | Get the size of the context. Nested contexts count a single element,
--   regardless of their size.
size :: Context -> Int
size (Ctx m) = M.size m

-- | Parse a context from a YAML-formatted 'ByteString'.
parseContext :: ByteString -> Either String Context
parseContext bs = do
  val <- decodeEither bs
  case val of
    Object ctx -> return $ Ctx ctx
    _          -> Left "Decoded value was not an object!"