{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ExistentialQuantification #-}
module LLVM.DSL.Render.Argument (
   T(Cons),
   Creator,
   unit,
   primitive,
   pair,
   triple,
   newDispose,
   ) where

import qualified LLVM.DSL.Expression as Expr
import LLVM.DSL.Expression (Exp)

import qualified LLVM.Extra.Multi.Value.Marshal as Marshal

import Data.Tuple.Strict (mapPair, mapTriple)

import Prelude2010
import Prelude ()



type Creator p = IO (p, IO ())


{- |
Transfer 'a' to 'adsl' with 'al' as transit stop.
-}
data T a adsl =
   forall al. Marshal.C al =>
   Cons (Exp al -> adsl) (a -> Creator al)


primitiveCreator :: a -> Creator a
primitiveCreator a = return (a, return ())

unit :: T () ()
unit = Cons (\ _unit -> ()) primitiveCreator

primitive :: (Marshal.C a) => T a (Exp a)
primitive = Cons id primitiveCreator


pair :: T a ad -> T b bd -> T (a,b) (ad,bd)
pair (Cons passA createA) (Cons passB createB) =
   Cons
      (mapPair (passA,passB) . Expr.unzip)
      (\(a,b) -> do
         (pa,finalA) <- createA a
         (pb,finalB) <- createB b
         return ((pa,pb), finalB>>finalA))

triple :: T a ad -> T b bd -> T c cd -> T (a,b,c) (ad,bd,cd)
triple (Cons passA createA) (Cons passB createB) (Cons passC createC) =
   Cons
      (mapTriple (passA,passB,passC) . Expr.unzip3)
      (\(a,b,c) -> do
         (pa,finalA) <- createA a
         (pb,finalB) <- createB b
         (pc,finalC) <- createC c
         return ((pa,pb,pc), finalC>>finalB>>finalA))


newDispose ::
   (Marshal.C handle) =>
   (a -> IO handle) -> (handle -> IO ()) ->
   (Exp handle -> ad) -> T a ad
newDispose new dispose fetch =
   Cons fetch
      (\x -> do
         it <- new x
         return (it, dispose it))