module PuppetDB.TestDB (initTestDBFunctions) where

import PuppetDB.Query
import Puppet.Interpreter.Types
import Puppet.DSL.Types hiding (Value)

import Data.Aeson
import qualified Data.Map as Map
import Control.Concurrent.MVar
import qualified Data.Text as T

type ExportedResources = Map.Map T.Text (FinalCatalog, EdgeMap, FinalCatalog)

initTestDBFunctions :: (T.Text -> Query -> IO (Either String Value)) -> IO (T.Text -> Query -> IO (Either String Value), T.Text -> (FinalCatalog, EdgeMap, FinalCatalog) -> IO ())
initTestDBFunctions defaultquery = do
    v <- newMVar Map.empty
    return (queryPDB v defaultquery, updatePDB v)

updatePDB :: MVar ExportedResources -> T.Text -> (FinalCatalog, EdgeMap, FinalCatalog) -> IO ()
updatePDB v node res = do
    ex <- takeMVar v
    let ex' = Map.insert node res ex
    putMVar v ex'

toBool :: T.Text -> Either String Bool
toBool "true"  = Right True
toBool "false" = Right False
toBool x       = Left ("Is not a boolean " ++ T.unpack x) 

evaluateQueryResource :: Query -> Bool -> T.Text -> ResIdentifier -> RResource -> Either String Bool
evaluateQueryResource (Query OAnd lst) e n rid rr = fmap and (mapM (\x -> evaluateQueryResource x e n rid rr) lst)
evaluateQueryResource (Query OOr  lst) e n rid rr = fmap or  (mapM (\x -> evaluateQueryResource x e n rid rr) lst)
evaluateQueryResource (Query OEqual [Terms ["node","active"], Term bool]) _ _ _ _ = toBool bool
evaluateQueryResource (Query OEqual [Terms ["node","name"], Term hname]) _ n _ _ = Right (n == hname)
evaluateQueryResource (Query OEqual [Term "type",Term ctype]) _ _ _ rr = Right (capitalizeResType (rrtype rr) == ctype)
evaluateQueryResource (Query OEqual [Term "exported",Term expo]) exported _ _ _ = toBool expo >>= \x -> return (x == exported)
evaluateQueryResource (Query OEqual [Term "tag",Term tag]) _ _ _ rr    =
    let tags = Map.findWithDefault (ResolvedArray []) "tag" (rrparams rr)
        stringEqual y (ResolvedString x) = (x == y)
        stringEqual _ _ = False
    in  case tags of
            ResolvedArray lst -> Right (any (stringEqual tag) lst)
            _ -> Right (stringEqual tag tags)
evaluateQueryResource (Query OEqual [Term "title", Term ttl]) _ _ _ rr = Right (rrname rr == ttl)
evaluateQueryResource (Query ONot [q]) e n rid rr = fmap not (evaluateQueryResource q e n rid rr)
evaluateQueryResource q _ _ _ _ = Left ("Not interpreted: " ++ show q)

queryPDB :: MVar ExportedResources -> (T.Text -> Query -> IO (Either String Value)) -> T.Text -> Query -> IO (Either String Value)
queryPDB v _ "resources" query = do
    ex <- readMVar v
    let isSelected = evaluateQueryResource query
        sortResources :: Either String ([(T.Text,ResIdentifier,RResource)], [(T.Text,ResIdentifier,RResource)])
                      -> T.Text
                      -> (FinalCatalog, EdgeMap, FinalCatalog)
                      -> Either String ([(T.Text,ResIdentifier,RResource)], [(T.Text,ResIdentifier,RResource)])
        sortResources (Left rr) _ _ = Left rr
        sortResources (Right (curnormal, curexported)) nodename (fnormal, _, fexported) =
            let newnormal   = Map.foldlWithKey' (sortResources' False nodename) (Right curnormal  ) fnormal
                newexported = Map.foldlWithKey' (sortResources' True  nodename) (Right curexported) fexported
            in case (newnormal, newexported) of
                   (Left r1, _)       -> Left r1
                   (_, Left r2)       -> Left r2
                   (Right n, Right e) -> Right (n,e)
        sortResources' :: Bool -> T.Text -> Either String [(T.Text,ResIdentifier,RResource)] -> ResIdentifier -> RResource -> Either String [(T.Text,ResIdentifier,RResource)]
        sortResources' _ _ (Left rr) _ _ = Left rr
        sortResources' e nodename (Right curlist) resid rr = case isSelected e nodename resid rr of
                                                                 Right False -> Right curlist
                                                                 Right True  -> Right ((nodename,resid,rr) : curlist)
                                                                 Left  err   -> Left err
        jsonize :: (T.Text,ResIdentifier,RResource) -> Value
        jsonize (h,_,r) = rr2json h r
    case Map.foldlWithKey' sortResources (Right ([], [])) ex of
        Right (n,e) -> return $ Right $ toJSON $ map jsonize ( n ++ e )
        Left rr     -> return (Left rr)

queryPDB _ _ querytype query = error (show (querytype, query))