module Language.Lean.Env
  ( Env
  , TrustLevel
  , trustHigh
    
  , standardEnv
  , hottEnv
  , envAddUniv
  , envAddDecl
  , envReplaceAxiom
    
  , envTrustLevel
  , envContainsProofIrrelProp
  , envIsImpredicative
  , envContainsDecl
  , envLookupDecl
  , envContainsUniv
  , envIsDescendant
    
  , envForget
  , envDecls
  , forEnvDecl_
  , envUnivs
  , forEnvUniv_
  ) where
import Control.Exception (bracket)
import Control.Lens
import Control.Monad
import Data.IORef
import Foreign
import Foreign.C
import System.IO.Unsafe
import Language.Lean.Internal.Decl
import Language.Lean.Internal.Exception
import Language.Lean.Internal.Exception.Unsafe
import Language.Lean.Internal.Name
type WrapLeanVisitFn p = (p -> IO ()) -> IO (FunPtr (p -> IO ()))
type LeanFoldFn s p = s -> FunPtr (p -> IO ()) -> OutExceptionPtr -> IO Bool
runLeanFold :: IsLeanValue a p
            => WrapLeanVisitFn p
            -> LeanFoldFn s p
            -> Fold s a
runLeanFold wrapFn foldFn h e = unsafePerformIO $ do
  
  ref <- newIORef $! (coerce $! pure ())
  let g d_ptr = do
        d <- mkLeanValue d_ptr
        cur_val <- readIORef ref
        let hd = h d
        let chd = coerce hd
        seq hd $ seq chd $ do
        writeIORef ref $! cur_val *> chd
  
  bracket (wrapFn g) freeHaskellFunPtr $ \g_ptr -> do
    alloca $ \ex_ptr -> do
      success <- foldFn e g_ptr ex_ptr
      if success then
        readIORef ref
      else
        throwLeanException =<< peek ex_ptr
safeRunLeanFold :: IsLeanValue a p
                => WrapLeanVisitFn p
                -> LeanFoldFn s p
                -> (a -> IO ())
                -> (s -> IO ())
safeRunLeanFold wrapFn foldFn f s = do
  let g = mkLeanValue >=> f
  
  bracket (wrapFn g) freeHaskellFunPtr $ \g_ptr -> do
    alloca $ \ex_ptr -> do
      success <- foldFn s g_ptr ex_ptr
      unless success $ do
        throwLeanException =<< peek ex_ptr
newtype TrustLevel = TrustLevel { _trustValue :: Word32 }
  deriving (Eq, Ord, Num, Show)
trustFromUInt :: CUInt -> TrustLevel
trustFromUInt = TrustLevel . fromIntegral
trustUInt :: TrustLevel -> CUInt
trustUInt (TrustLevel u) = fromIntegral u
trustHigh :: TrustLevel
trustHigh = TrustLevel 100000
standardEnv :: TrustLevel -> Env
standardEnv lvl = tryGetLeanValue $ lean_env_mk_std lvl
lean_env_mk_std :: (TrustLevel) -> (OutEnvPtr) -> (OutExceptionPtr) -> IO ((Bool))
lean_env_mk_std a1 a2 a3 =
  let {a1' = trustUInt a1} in 
  let {a2' = id a2} in 
  let {a3' = id a3} in 
  lean_env_mk_std'_ a1' a2' a3' >>= \res ->
  let {res' = toBool res} in
  return (res')
hottEnv :: TrustLevel -> Env
hottEnv lvl = tryGetLeanValue $ lean_env_mk_hott lvl
lean_env_mk_hott :: (TrustLevel) -> (OutEnvPtr) -> (OutExceptionPtr) -> IO ((Bool))
lean_env_mk_hott a1 a2 a3 =
  let {a1' = trustUInt a1} in 
  let {a2' = id a2} in 
  let {a3' = id a3} in 
  lean_env_mk_hott'_ a1' a2' a3' >>= \res ->
  let {res' = toBool res} in
  return (res')
envAddUniv :: Name -> Env -> Env
envAddUniv u e = tryGetLeanValue $ lean_env_add_univ e u
lean_env_add_univ :: (Env) -> (Name) -> (OutEnvPtr) -> (OutExceptionPtr) -> IO ((Bool))
lean_env_add_univ a1 a2 a3 a4 =
  (withEnv) a1 $ \a1' -> 
  (withName) a2 $ \a2' -> 
  let {a3' = id a3} in 
  let {a4' = id a4} in 
  lean_env_add_univ'_ a1' a2' a3' a4' >>= \res ->
  let {res' = toBool res} in
  return (res')
envAddDecl :: CertDecl -> Env -> Env
envAddDecl d e = tryGetLeanValue $ lean_env_add e d
lean_env_add :: (Env) -> (CertDecl) -> (OutEnvPtr) -> (OutExceptionPtr) -> IO ((Bool))
lean_env_add a1 a2 a3 a4 =
  (withEnv) a1 $ \a1' -> 
  (withCertDecl) a2 $ \a2' -> 
  let {a3' = id a3} in 
  let {a4' = id a4} in 
  lean_env_add'_ a1' a2' a3' a4' >>= \res ->
  let {res' = toBool res} in
  return (res')
envReplaceAxiom :: CertDecl -> Env -> Env
envReplaceAxiom d e = tryGetLeanValue $ lean_env_replace e d
lean_env_replace :: (Env) -> (CertDecl) -> (OutEnvPtr) -> (OutExceptionPtr) -> IO ((Bool))
lean_env_replace a1 a2 a3 a4 =
  (withEnv) a1 $ \a1' -> 
  (withCertDecl) a2 $ \a2' -> 
  let {a3' = id a3} in 
  let {a4' = id a4} in 
  lean_env_replace'_ a1' a2' a3' a4' >>= \res ->
  let {res' = toBool res} in
  return (res')
envTrustLevel :: Env -> TrustLevel
envTrustLevel = lean_env_trust_level
lean_env_trust_level :: (Env) -> (TrustLevel)
lean_env_trust_level a1 =
  unsafePerformIO $
  (withEnv) a1 $ \a1' -> 
  let {res = lean_env_trust_level'_ a1'} in
  let {res' = trustFromUInt res} in
  return (res')
envContainsProofIrrelProp :: (Env) -> (Bool)
envContainsProofIrrelProp a1 =
  unsafePerformIO $
  (withEnv) a1 $ \a1' -> 
  let {res = envContainsProofIrrelProp'_ a1'} in
  let {res' = toBool res} in
  return (res')
envIsImpredicative :: (Env) -> (Bool)
envIsImpredicative a1 =
  unsafePerformIO $
  (withEnv) a1 $ \a1' -> 
  let {res = envIsImpredicative'_ a1'} in
  let {res' = toBool res} in
  return (res')
envContainsUniv :: (Env) -> (Name) -> (Bool)
envContainsUniv a1 a2 =
  unsafePerformIO $
  (withEnv) a1 $ \a1' -> 
  (withName) a2 $ \a2' -> 
  let {res = envContainsUniv'_ a1' a2'} in
  let {res' = toBool res} in
  return (res')
envContainsDecl :: (Env) -> (Name) -> (Bool)
envContainsDecl a1 a2 =
  unsafePerformIO $
  (withEnv) a1 $ \a1' -> 
  (withName) a2 $ \a2' -> 
  let {res = envContainsDecl'_ a1' a2'} in
  let {res' = toBool res} in
  return (res')
envLookupDecl :: Name -> Env -> Maybe Decl
envLookupDecl nm e =
  if envContainsDecl e nm then
    Just (tryGetLeanValue $ lean_env_get_decl e nm)
  else
    Nothing
lean_env_get_decl :: (Env) -> (Name) -> (OutDeclPtr) -> (OutExceptionPtr) -> IO ((Bool))
lean_env_get_decl a1 a2 a3 a4 =
  (withEnv) a1 $ \a1' -> 
  (withName) a2 $ \a2' -> 
  let {a3' = id a3} in 
  let {a4' = id a4} in 
  lean_env_get_decl'_ a1' a2' a3' a4' >>= \res ->
  let {res' = toBool res} in
  return (res')
envIsDescendant :: Env -> Env -> Bool
envIsDescendant = lean_env_is_descendant
lean_env_is_descendant :: (Env) -> (Env) -> (Bool)
lean_env_is_descendant a1 a2 =
  unsafePerformIO $
  (withEnv) a1 $ \a1' -> 
  (withEnv) a2 $ \a2' -> 
  let {res = lean_env_is_descendant'_ a1' a2'} in
  let {res' = toBool res} in
  return (res')
envForget :: Env -> Env
envForget x = tryGetLeanValue $ lean_env_forget x
lean_env_forget :: (Env) -> (OutEnvPtr) -> (OutExceptionPtr) -> IO ((Bool))
lean_env_forget a1 a2 a3 =
  (withEnv) a1 $ \a1' -> 
  let {a2' = id a2} in 
  let {a3' = id a3} in 
  lean_env_forget'_ a1' a2' a3' >>= \res ->
  let {res' = toBool res} in
  return (res')
envDecls :: Fold Env Decl
envDecls = runLeanFold wrapDeclVisitFn lean_env_for_each_decl
forEnvDecl_ :: Env -> (Decl -> IO ()) -> IO ()
forEnvDecl_ e f = safeRunLeanFold wrapDeclVisitFn lean_env_for_each_decl f e
foreign import ccall "wrapper" wrapDeclVisitFn :: WrapLeanVisitFn DeclPtr
lean_env_for_each_decl :: (Env) -> (FunPtr (DeclPtr -> IO ())) -> (OutExceptionPtr) -> IO ((Bool))
lean_env_for_each_decl a1 a2 a3 =
  (withEnv) a1 $ \a1' -> 
  let {a2' = id a2} in 
  let {a3' = id a3} in 
  lean_env_for_each_decl'_ a1' a2' a3' >>= \res ->
  let {res' = toBool res} in
  return (res')
envUnivs :: Fold Env Name
envUnivs = runLeanFold wrapNameVisitFn lean_env_for_each_univ
forEnvUniv_ :: Env -> (Name -> IO ()) -> IO ()
forEnvUniv_ e f = safeRunLeanFold wrapNameVisitFn lean_env_for_each_univ f e
foreign import ccall "wrapper" wrapNameVisitFn :: WrapLeanVisitFn NamePtr
lean_env_for_each_univ :: (Env) -> (FunPtr (NamePtr -> IO ())) -> (OutExceptionPtr) -> IO ((Bool))
lean_env_for_each_univ a1 a2 a3 =
  (withEnv) a1 $ \a1' -> 
  let {a2' = id a2} in 
  let {a3' = id a3} in 
  lean_env_for_each_univ'_ a1' a2' a3' >>= \res ->
  let {res' = toBool res} in
  return (res')
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_mk_std"
  lean_env_mk_std'_ :: (CUInt -> ((OutEnvPtr) -> ((OutExceptionPtr) -> (IO CInt))))
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_mk_hott"
  lean_env_mk_hott'_ :: (CUInt -> ((OutEnvPtr) -> ((OutExceptionPtr) -> (IO CInt))))
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_add_univ"
  lean_env_add_univ'_ :: ((EnvPtr) -> ((NamePtr) -> ((OutEnvPtr) -> ((OutExceptionPtr) -> (IO CInt)))))
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_add"
  lean_env_add'_ :: ((EnvPtr) -> ((CertDeclPtr) -> ((OutEnvPtr) -> ((OutExceptionPtr) -> (IO CInt)))))
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_replace"
  lean_env_replace'_ :: ((EnvPtr) -> ((CertDeclPtr) -> ((OutEnvPtr) -> ((OutExceptionPtr) -> (IO CInt)))))
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_trust_level"
  lean_env_trust_level'_ :: ((EnvPtr) -> CUInt)
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_proof_irrel"
  envContainsProofIrrelProp'_ :: ((EnvPtr) -> CInt)
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_impredicative"
  envIsImpredicative'_ :: ((EnvPtr) -> CInt)
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_contains_univ"
  envContainsUniv'_ :: ((EnvPtr) -> ((NamePtr) -> CInt))
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_contains_decl"
  envContainsDecl'_ :: ((EnvPtr) -> ((NamePtr) -> CInt))
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_get_decl"
  lean_env_get_decl'_ :: ((EnvPtr) -> ((NamePtr) -> ((OutDeclPtr) -> ((OutExceptionPtr) -> (IO CInt)))))
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_is_descendant"
  lean_env_is_descendant'_ :: ((EnvPtr) -> ((EnvPtr) -> CInt))
foreign import ccall unsafe "Language/Lean/Env.chs.h lean_env_forget"
  lean_env_forget'_ :: ((EnvPtr) -> ((OutEnvPtr) -> ((OutExceptionPtr) -> (IO CInt))))
foreign import ccall safe "Language/Lean/Env.chs.h lean_env_for_each_decl"
  lean_env_for_each_decl'_ :: ((EnvPtr) -> ((FunPtr ((DeclPtr) -> (IO ()))) -> ((OutExceptionPtr) -> (IO CInt))))
foreign import ccall safe "Language/Lean/Env.chs.h lean_env_for_each_univ"
  lean_env_for_each_univ'_ :: ((EnvPtr) -> ((FunPtr ((NamePtr) -> (IO ()))) -> ((OutExceptionPtr) -> (IO CInt))))