{-# OPTIONS_GHC -fglasgow-exts -cpp -fvia-C -fno-warn-unused-binds -fno-warn-unused-imports -fallow-overlapping-instances #-}

module Pugs.Run.Perl5 () where

#ifdef PUGS_HAVE_PERL5

import Pugs.Internals
import Pugs.AST
import Pugs.Prim.Eval
import Pugs.Embed.Perl5
import Pugs.Types
import Foreign
import Foreign.C.Types
import Foreign.C.String
import Foreign.Marshal.Array

foreign export ccall "pugs_Eval"
    pugs_eval :: CString -> IO PugsVal

foreign export ccall "pugs_Apply"
    pugs_apply :: PugsVal -> PugsVal -> Ptr PugsVal -> CInt -> IO PerlSV

foreign export ccall "pugs_ValToSv"
    valToSv :: PugsVal -> IO PerlSV

foreign export ccall "pugs_ValToIv"
    valToIv :: PugsVal -> IO CInt

foreign export ccall "pugs_ValToNv"
    valToNv :: PugsVal -> IO CDouble

foreign export ccall "pugs_ValToPv"
    valToPv :: PugsVal -> IO CString

foreign export ccall "pugs_MkSvRef"
    mkSvRef :: PerlSV -> IO PugsVal

foreign export ccall "pugs_IvToVal"
    ivToVal :: CInt -> IO PugsVal

foreign export ccall "pugs_NvToVal"
    nvToVal :: CDouble -> IO PugsVal

foreign export ccall "pugs_PvnToVal"
    pvnToVal :: CString -> CInt -> IO PugsVal

foreign export ccall "pugs_UndefVal"
    undefVal :: IO PugsVal

askPerl5Env :: IO Env
askPerl5Env = deEnv =<< pugs_getenv

pugs_eval :: CString -> IO PugsVal
pugs_eval cstr = do
    str <- peekCString cstr
    env <- askPerl5Env
    val <- runEvalIO env $ opEval quiet "<eval>" str
    mkVal val
    where
    quiet = MkEvalStyle
        { evalResult = EvalResultLastValue
        , evalError = EvalErrorUndef
        }

pugs_apply :: PugsVal -> PugsVal -> Ptr PugsVal -> CInt -> IO PerlSV
pugs_apply subPtr invPtr argsPtr cxt = do
    env     <- askPerl5Env
    sub     <- deVal subPtr
    inv     <- deValMaybe invPtr
    args    <- mapM deVal =<< peekArray0 nullVal argsPtr
    let subExp = case sub of
            VStr name@('&':_)   -> _Var name
            VStr name           -> _Var ('&':name)
            _                   -> Val sub
    -- warn "Applying:" (subExp, inv, args, envLexical env)
    val <- runEvalIO env $
        evalExp (Ann (Cxt (cxtEnum cxt)) $
            App subExp (fmap Val inv) (map Val args))
    newSVval val

deVal :: PugsVal -> IO Val
deVal ptr = deRefStablePtr ptr

deEnv :: PugsEnv -> IO Env
deEnv ptr = do
    env <- deRefStablePtr ptr
    return env{ envDebug = Nothing }

nullVal :: PugsVal
nullVal = castPtrToStablePtr nullPtr

deValMaybe :: PugsVal -> IO (Maybe Val)
deValMaybe ptr | castStablePtrToPtr ptr == nullPtr = return Nothing
deValMaybe ptr = fmap Just (deVal ptr)

valToSv :: PugsVal -> IO PerlSV
valToSv ptr = do
    val <- deVal ptr
    newSVval val

valToIv :: PugsVal -> IO CInt
valToIv ptr = do
    val     <- deVal ptr
    env     <- askPerl5Env
    VInt x  <- runEvalIO env $ fmap VInt (fromVal val)
    return $ fromInteger x

valToNv :: PugsVal -> IO CDouble
valToNv ptr = do
    val     <- deVal ptr
    env     <- askPerl5Env
    VRat x  <- runEvalIO env $ fmap VInt (fromVal val)
    return $ fromRational x

valToPv :: PugsVal -> IO CString
valToPv ptr = do
    val     <- deVal ptr
    env     <- askPerl5Env
    VStr x  <- runEvalIO env $ fmap VInt (fromVal val)
    newCString x

mkSvRef :: PerlSV -> IO PugsVal
mkSvRef = mkVal . PerlSV

ivToVal :: CInt -> IO PugsVal
ivToVal = mkVal . VInt . fromIntegral

nvToVal :: CDouble -> IO PugsVal
nvToVal = mkVal . VNum . realToFrac

pvnToVal :: CString -> CInt -> IO PugsVal
pvnToVal cstr len = do
    str <- peekCStringLen (cstr, fromEnum len)
    ptr <- mkVal $ VStr (decodeUTF8 str)
    return ptr

undefVal :: IO PugsVal
undefVal = mkVal VUndef

#endif
