-------------------------------------------------------------------------------
-- |
-- Module      :  System.Hardware.Haskino.Test.ExprWord8
-- Copyright   :  (c) University of Kansas
-- License     :  BSD3
-- Stability   :  experimental
--
-- Quick Check tests for Expressions returning a Expr Word8
-------------------------------------------------------------------------------

{-# LANGUAGE GADTs #-}

module System.Hardware.Haskino.Test.ExprWord8 where

import Prelude hiding 
  ( quotRem, divMod, quot, rem, div, mod, properFraction, fromInteger, toInteger, (<*) )
import qualified Prelude as P
import System.Hardware.Haskino
import Data.Boolean
import Data.Boolean.Numbers
import Data.Boolean.Bits
import Data.Int
import Data.Word
import qualified Data.Bits as DB
import Test.QuickCheck hiding ((.&.))
import Test.QuickCheck.Monadic

litEval8 :: Expr Word8 -> Word8
litEval8 (LitW8 w) = w

litEvalB :: Expr Bool -> Bool
litEvalB (LitB w) = w

prop_neg :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> Property
prop_neg c r x = monadicIO $ do
    let local = negate x
    remote <- run $ send c $ do
        writeRemoteRefE r $ negate (lit x)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_sign :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> Property
prop_sign c r x = monadicIO $ do
    let local = signum x
    remote <- run $ send c $ do
        writeRemoteRefE r $ signum (lit x)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_add :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> Word8 -> Property
prop_add c r x y = monadicIO $ do
    let local = x + y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) + (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_sub :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> Word8 -> Property
prop_sub c r x y = monadicIO $ do
    let local = x - y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) - (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_mult :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> Word8 -> Property
prop_mult c r x y = monadicIO $ do
    let local = x * y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) * (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_div :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> NonZero Word8 -> Property
prop_div c r x (NonZero y) = monadicIO $ do
    let local = x `P.div` y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) `div` (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_rem :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> NonZero Word8 -> Property
prop_rem c r x (NonZero y) = monadicIO $ do
    let local = x `P.rem` y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) `rem` (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_quot :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> NonZero Word8 -> Property
prop_quot c r x (NonZero y) = monadicIO $ do
    let local = x `P.quot` y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) `quot` (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_mod :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> NonZero Word8 -> Property
prop_mod c r x (NonZero y) = monadicIO $ do
    let local = x `P.mod` y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) `mod` (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_comp :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> Property
prop_comp c r x = monadicIO $ do
    let local = DB.complement x
    remote <- run $ send c $ do
        writeRemoteRefE r $ complement (lit x) 
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_and :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> Word8 -> Property
prop_and c r x y = monadicIO $ do
    let local = x DB..&. y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) .&. (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_or :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> Word8 -> Property
prop_or c r x y = monadicIO $ do
    let local = x DB..|. y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) .|. (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_xor :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> Word8 -> Property
prop_xor c r x y = monadicIO $ do
    let local = x `DB.xor` y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) `xor` (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_shiftL :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> NonNegative Int -> Property
prop_shiftL c r x (NonNegative y) = monadicIO $ do
    let local = x `DB.shiftL` y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) `shiftL` (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_shiftR :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> NonNegative Int -> Property
prop_shiftR c r x (NonNegative y) = monadicIO $ do
    let local = x `DB.shiftR` y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) `shiftR` (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_setBit :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> NonNegative Int -> Property
prop_setBit c r x (NonNegative y) = monadicIO $ do
    let local = x `DB.setBit` y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) `setBit` (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_clearBit :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> NonNegative Int -> Property
prop_clearBit c r x (NonNegative y) = monadicIO $ do
    let local = x `DB.clearBit` y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) `clearBit` (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_testBit :: ArduinoConnection -> RemoteRef Bool -> Word8 -> NonNegative Int -> Property
prop_testBit c r x (NonNegative y) = monadicIO $ do
    let local = x `DB.testBit` y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) `testBit` (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEvalB remote)

prop_bit :: ArduinoConnection -> RemoteRef Word8 -> NonNegative Int -> Property
prop_bit c r (NonNegative x) = monadicIO $ do
    let local = DB.bit x
    remote <- run $ send c $ do
        writeRemoteRefE r $ bit (lit x)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_from16 :: ArduinoConnection -> RemoteRef Word8 -> Word16 -> Property
prop_from16 c r x = monadicIO $ do
    let local = fromIntegral x
    remote <- run $ send c $ do
        writeRemoteRefE r $ fromIntegralB (lit x)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_from32 :: ArduinoConnection -> RemoteRef Word8 -> Word32 -> Property
prop_from32 c r x = monadicIO $ do
    let local = fromIntegral x
    remote <- run $ send c $ do
        writeRemoteRefE r $ fromIntegralB (lit x)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_fromI8 :: ArduinoConnection -> RemoteRef Word8 -> Int8 -> Property
prop_fromI8 c r x = monadicIO $ do
    let local = fromIntegral x
    remote <- run $ send c $ do
        writeRemoteRefE r $ fromIntegralB (lit x)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_fromI16 :: ArduinoConnection -> RemoteRef Word8 -> Int16 -> Property
prop_fromI16 c r x = monadicIO $ do
    let local = fromIntegral x
    remote <- run $ send c $ do
        writeRemoteRefE r $ fromIntegralB (lit x)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_fromI32 :: ArduinoConnection -> RemoteRef Word8 -> Int32 -> Property
prop_fromI32 c r x = monadicIO $ do
    let local = fromIntegral x
    remote <- run $ send c $ do
        writeRemoteRefE r $ fromIntegralB (lit x)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_ifb :: ArduinoConnection -> RemoteRef Word8 -> Bool -> Word8 -> Word8 -> Property
prop_ifb c r b x y = monadicIO $ do
    let local = if b then x + y else x - y
    remote <- run $ send c $ do
        writeRemoteRefE r $ ifB (lit b) (lit x + lit y) (lit x - lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_eq :: ArduinoConnection -> RemoteRef Bool -> Word8 -> Word8 -> Property
prop_eq c r x y = monadicIO $ do
    let local = x == y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) ==* (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEvalB remote)

prop_neq :: ArduinoConnection -> RemoteRef Bool -> Word8 -> Word8 -> Property
prop_neq c r x y = monadicIO $ do
    let local = x /= y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) /=* (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEvalB remote)

prop_lt :: ArduinoConnection -> RemoteRef Bool -> Word8 -> Word8 -> Property
prop_lt c r x y = monadicIO $ do
    let local = x < y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) <* (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEvalB remote)

prop_gt :: ArduinoConnection -> RemoteRef Bool -> Word8 -> Word8 -> Property
prop_gt c r x y = monadicIO $ do
    let local = x > y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) >* (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEvalB remote)

prop_lte :: ArduinoConnection -> RemoteRef Bool -> Word8 -> Word8 -> Property
prop_lte c r x y = monadicIO $ do
    let local = x <= y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) <=* (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEvalB remote)

prop_gte :: ArduinoConnection -> RemoteRef Bool -> Word8 -> Word8 -> Property
prop_gte c r x y = monadicIO $ do
    let local = x >= y
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit x) >=* (lit y)
        v <- readRemoteRefE r
        return v
    assert (local == litEvalB remote)

prop_arith :: ArduinoConnection -> RemoteRef Word8 -> 
              Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> NonZero Word8 -> Property
prop_arith c r a b d e f (NonZero g) = monadicIO $ do
    let local = a * b + d * e - f `P.div` g
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit a) * (lit b) + (lit d) * (lit e) - (lit f) `div` (lit g) 
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_bind :: ArduinoConnection -> RemoteRef Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Property
prop_bind c r a b d e = monadicIO $ do
    let local = a * b + d * e
    remote <- run $ send c $ do
        writeRemoteRefE r $ (lit a) 
        a' <- readRemoteRefE r
        writeRemoteRefE r $ a' * (lit b) 
        ab' <- readRemoteRefE r
        writeRemoteRefE r $ (lit d) 
        d' <- readRemoteRefE r
        v <- readRemoteRefE r
        writeRemoteRefE r $ d' * (lit e) 
        de' <- readRemoteRefE r
        writeRemoteRefE r $ ab' + de' 
        v <- readRemoteRefE r
        return v
    assert (local == litEval8 remote)

prop_ifthenelse :: ArduinoConnection -> Word8 -> Word8 -> Property
prop_ifthenelse c x y = monadicIO $ do
    let local = if (x < y) then x else y
    remote <- run $ send c $ do
        v <- ifThenElseE ((lit x) <* (lit y)) (return $ lit x) (return $ lit y)
        return v
    assert (local == litEval8 remote)

{-- The IfThenElseEithers are only used inside of Iterates
 -- so they will never be received by the host, but are here for
 -- initialtest purposes 
prop_ifthenelseeitherw8 :: ArduinoConnection -> Word8 -> Word8 -> Property
prop_ifthenelseeitherw8 c x y = monadicIO $ do
    let local = if (x < y) then x else y
    remote <- run $ send c $ do
        v <- ifThenElseEither ((lit x) <* (lit y)) (return $ ExprLeft $ lit x) (return $ ExprRight $ lit y)
        return v
    let remoteEval = case remote of
            ExprLeft  (LitW8 l) -> l
            ExprRight (LitW8 r) -> r
    assert (local == remoteEval)

prop_ifthenelseeitherb :: ArduinoConnection -> Word8 -> Word8 -> Property
prop_ifthenelseeitherb c x y = monadicIO $ do
    let local = if (x < y) then Left x else Right $ y > 0x7F
    remote <- run $ send c $ do
        v <- ifThenElseEither ((lit x) <* (lit y)) (return $ ExprLeft $ lit x) (return $ ExprRight $ lit (y > 0x7F))
        return v
    case local of 
        Left l -> do
            let (ExprLeft (LitW8 rl)) = remote
            assert (l == rl)
        Right r -> do
            let (ExprRight (LitB rr)) = remote
            assert (r == rr)
-}

prop_while :: ArduinoConnection -> NonZero Word8 -> Property
prop_while c (NonZero x) = monadicIO $ do
    let local = x
    remote <- run $ send c $ do
        v <- whileE (lit 0) (\z -> z <* lit x) (\z -> return $ z + 1)
        return v
    assert (local == litEval8 remote)

main :: IO ()
main = do
    conn <- openArduino False "/dev/cu.usbmodem1421"
    refW8 <- send conn $ newRemoteRefE 0
    refB <- send conn $ newRemoteRefE (lit False)
    print "Negation Tests:"
    quickCheck (prop_neg conn refW8)
    print "Signum Tests:"
    quickCheck (prop_sign conn refW8)
    print "Addition Tests:"
    quickCheck (prop_add conn refW8)
    print "Subtraction Tests:"
    quickCheck (prop_sub conn refW8)
    print "Multiplcation Tests:"
    quickCheck (prop_mult conn refW8)
    print "Division Tests:"
    quickCheck (prop_div conn refW8)
    print "Remainder Tests:"
    quickCheck (prop_rem conn refW8)
    print "Quotient Tests:"
    quickCheck (prop_quot conn refW8)
    print "Modulo Tests:"
    quickCheck (prop_mod conn refW8)
    print "Complement Tests:"
    quickCheck (prop_comp conn refW8)
    print "Bitwise And Tests:"
    quickCheck (prop_and conn refW8)
    print "Bitwise Or Tests:"
    quickCheck (prop_or conn refW8)
    print "Bitwise Xor Tests:"
    quickCheck (prop_xor conn refW8)
    print "Shift Left Tests:"
    quickCheck (prop_shiftL conn refW8)
    print "Shift Right Tests:"
    quickCheck (prop_shiftR conn refW8)
    print "Set Bit Tests:"
    quickCheck (prop_setBit conn refW8)
    print "Clear Bit Tests:"
    quickCheck (prop_clearBit conn refW8)
    print "Test Bit Tests:"
    quickCheck (prop_testBit conn refB)
    print "From Word16 Tests:"
    quickCheck (prop_from16 conn refW8)
    print "From Word32 Tests:"
    quickCheck (prop_from32 conn refW8)
    print "From Int8 Tests:"
    quickCheck (prop_fromI8 conn refW8)
    print "From Int16 Tests:"
    quickCheck (prop_fromI16 conn refW8)
    print "From Int32 Tests:"
    quickCheck (prop_fromI32 conn refW8)
    print "ifB Tests:"
    quickCheck (prop_ifb conn refW8)
    print "Equal Tests:"
    quickCheck (prop_eq conn refB)
    print "Not Equal Tests:"
    quickCheck (prop_neq conn refB)
    print "Less Than Tests:"
    quickCheck (prop_lt conn refB)
    print "Greater Than Tests:"
    quickCheck (prop_gt conn refB)
    print "Less Than Equal Tests:"
    quickCheck (prop_lte conn refB)
    print "Greater Than Equal Tests:"
    quickCheck (prop_gte conn refB)
    print "Arithemtic Tests:"
    quickCheck (prop_arith conn refW8)
    print "Bind Tests:"
    quickCheck (prop_bind conn refW8)
{-
    print "IfThenElse Tests:"
    quickCheck (prop_ifthenelse conn)
    print "IfThenElseEitherW8 Tests:"
    quickCheck (prop_ifthenelseeitherw8 conn)
    print "IfThenElseEitherBool Tests:"
    quickCheck (prop_ifthenelseeitherb conn)
    print "While Tests:"
    quickCheck (prop_while conn)
    closeArduino conn
-}
