module Erb.Evaluate (rubyEvaluate) where

import qualified Data.Map as Map
import Puppet.Interpreter.Types
import Erb.Ruby
import Data.Maybe (catMaybes)
import Data.Either (rights)
import Control.Monad.Error
import Data.Char

rubyEvaluate :: Map.Map String GeneralValue -> String -> [RubyStatement] -> Either String String
rubyEvaluate vars ctx = foldl (evalruby vars ctx) (Right "")

evalruby :: Map.Map String GeneralValue -> String -> Either String String -> RubyStatement -> Either String String
evalruby _  _   (Left err)     _        = Left err
evalruby mp ctx (Right curstr) (Puts e) = case evalExpression mp ctx e of
    Left err -> Left err
    Right ex -> Right (curstr ++ ex)

evalExpression :: Map.Map String GeneralValue -> String -> Expression -> Either String String
evalExpression mp ctx (LookupOperation varname varindex) = do
    rvname <- evalExpression mp ctx varname
    rvindx <- evalExpression mp ctx varindex
    varvalue <- getVariable mp ctx rvname
    case varvalue of
        ResolvedArray arr -> do
            case (a2i rvindx) of
                Nothing -> throwError $ "Can't convert index to integer when resolving " ++ rvname ++ "[" ++ rvindx ++ "]"
                Just  i -> if length arr <= i
                    then throwError $ "Array out of bound " ++ rvname ++ "[" ++ rvindx ++ "]"
                    else return $ arr !! i
            return "x"
        ResolvedHash hs   -> case (filter (\(a,_) -> a == rvindx) hs) of
            []      -> throwError $ "No index " ++ rvindx ++ " for variable " ++ show varname
            (_,x):_ -> evalValue $ Right x
        x                 -> throwError $ "Can't index variable " ++ show varname ++ ", it is " ++ show x
evalExpression _  _   (Value (Literal x))          = Right x
evalExpression mp ctx (Object (Value (Literal x))) = getVariable mp ctx x >>= evalValue . Right
evalExpression _  _   x = Left $ "Can't evaluate " ++ show x

getVariable :: Map.Map String GeneralValue -> String -> String -> Either String ResolvedValue
getVariable mp ctx rvname =
    let vars = map (\x -> Map.lookup x mp) [rvname, ctx ++ "::" ++ rvname, "::" ++ rvname]
        jsts = catMaybes vars
        rghts = rights jsts
    in do
        when (null jsts) (throwError $ "Can't resolve variable " ++ rvname)
        when (null rghts) (throwError $ "Variable " ++ rvname ++ " value is not resolved")
        return (head rghts)
        

evalValue :: GeneralValue -> Either String String
evalValue (Left _) = Left $ "Can't evaluate a value"
evalValue (Right (ResolvedString x)) = Right x
evalValue (Right (ResolvedInt x))    = Right $ show x
evalValue (Right x) = Right $ show x

a2i :: String -> Maybe Int
a2i x | all isDigit x = read x
      | otherwise = Nothing