--------------------------------------------------------------------------------
{-# LANGUAGE PatternGuards #-}


--------------------------------------------------------------------------------
module Console.Garepinoh.Utils
    ( step
    , value
    , appendFunction
    , function
    , append
    , apply
    , list
    ) where


--------------------------------------------------------------------------------
import Data.List


--------------------------------------------------------------------------------
import Console.Garepinoh.Types


--------------------------------------------------------------------------------
step :: Read t => Prelude t -> Result t -> String -> Result t
step _   (Left  ermsg) _ = Left ermsg
step fl (Right stack) word
    | Just x <- value word
    = append x stack
    | Just x <- appendFunction word fl
    = append (el Va Li (\f -> Fu (f { symb = NEL (tail word) [] })) x) stack
    | Just x <- applyFunction word fl
    = apply fl x stack
    | otherwise
    = Left $ "Invalid input " ++ show word ++ "."


--------------------------------------------------------------------------------
value :: Read t => String -> Maybe (El t)
value word = case reads word of
    [(v, "")] -> Just $ Va v
    _ -> Nothing


--------------------------------------------------------------------------------
appendFunction :: String -> Prelude t -> Maybe (El t)
appendFunction (',':word) = applyFunction word
appendFunction _          = const Nothing


--------------------------------------------------------------------------------
applyFunction :: String -> Prelude t -> Maybe (El t)
applyFunction word = fmap Fu . find (elem word . list . symb)


--------------------------------------------------------------------------------
append :: El t -> Stack t -> Result t
append e l = Right (e:l)


--------------------------------------------------------------------------------
apply :: Read t => Prelude t -> El t -> Stack t -> Result t
apply fl (Fu (Func _ fs)) stack = applyAll fs stack
  where
    applyAll []     s = Right s
    applyAll (g:gs) s = either Left (applyAll gs) $ function
                            (Right . (:s))
                            (step fl (Right s))
                            (\h -> h fl s)
                            g
apply _ _ _ = Left "Expecting a function to be applied."


--------------------------------------------------------------------------------
list :: NonEmptyList t -> [t]
list (NEL x xs) = x:xs


--------------------------------------------------------------------------------
-- |Destructor of 'El'.
el :: (t -> x) -> ([El t] -> x) -> (Func t -> x) -> El t -> x
el f _ _ (Va x) = f x
el _ f _ (Li x) = f x
el _ _ f (Fu x) = f x


--------------------------------------------------------------------------------
-- |Destructor of 'Function'.
function :: (El t -> x) -> (String -> x) -> ((Prelude t -> Stack t -> Either String (Stack t)) -> x) -> Function t -> x
function f _ _ (Ele x) = f x
function _ f _ (Ref x) = f x
function _ _ f (Fun x) = f x