{-# LANGUAGE CPP, FlexibleInstances, RankNTypes,
  TypeSynonymInstances #-}
{-# OPTIONS -Wall #-}
-- | An extension module of building blocks. Contains booleans, comparison operations, branchings.

module Language.Paraiso.OM.Builder.Boolean
  (eq, ne, lt, le, gt, ge, select) where

import Data.Dynamic (Typeable, typeOf)
import qualified Language.Paraiso.OM.Arithmetic as A
import Language.Paraiso.OM.Builder.Internal
import Language.Paraiso.OM.DynValue as DVal
import Language.Paraiso.OM.Graph
import Language.Paraiso.OM.Realm as Realm
import Language.Paraiso.OM.Value as Val
import NumericPrelude 


-- | generate a binary operator that returns Bool results.
mkOp2B :: (TRealm r, Typeable c) => 
          A.Operator                   -- ^The operation to be performed
       -> (Builder v g a (Value r c))    -- ^The first argument
       -> (Builder v g a (Value r c))    -- ^The second argument
       -> (Builder v g a (Value r Bool)) -- ^The result
mkOp2B op builder1 builder2 = do
  v1 <- builder1
  v2 <- builder2
  let 
      r1 = Val.realm v1
  n1 <- valueToNode v1
  n2 <- valueToNode v2
  n0 <- addNodeE [n1, n2] $ NInst (Arith op) 
  n01 <- addNodeE [n0]    $ NValue (toDyn v1){typeRep = typeOf True} 
  return $ FromNode r1 True n01


type CompareOp =  (TRealm r, Typeable c) => 
    (Builder v g a (Value r c)) -> (Builder v g a (Value r c)) -> (Builder v g a (Value r Bool))

-- | Equal
eq :: CompareOp
eq = mkOp2B A.EQ
-- | Not equal
ne :: CompareOp
ne = mkOp2B A.NE
-- | Less than
lt :: CompareOp
lt = mkOp2B A.LT
-- | Less than or equal to
le :: CompareOp
le = mkOp2B A.LE
-- | Greater than
gt :: CompareOp
gt = mkOp2B A.GT
-- | Greater than or equal to
ge :: CompareOp
ge = mkOp2B A.GE

-- | selects either the second or the third argument based 
select ::(TRealm r, Typeable c) => 
         (Builder v g a (Value r Bool)) -- ^The 'Bool' condition
      -> (Builder v g a (Value r c))    -- ^The value chosen when the condition is 'True'
      -> (Builder v g a (Value r c))    -- ^The value chosen when the condition is 'False'
      -> (Builder v g a (Value r c))    -- ^The result
select builderB builder1 builder2 = do
  vb <- builderB
  v1 <- builder1
  v2 <- builder2
  nb <- valueToNode vb
  n1 <- valueToNode v1
  n2 <- valueToNode v2
  n0 <- addNodeE [nb, n1, n2] $ NInst (Arith A.Select) 
  n01 <- addNodeE [n0] $ NValue (toDyn v1) 
  let 
      r1 = Val.realm v1
      c1 = Val.content v1
  return $ FromNode r1 c1 n01