{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}

module Bits (runBits, cmodule) where

import Ivory.Compile.C.CmdlineFrontend
import Ivory.Language hiding (setBit, clearBit, runBits)
import MonadLib.Monads (runState, sets)

runBits :: IO ()
runBits = runCompiler [cmodule] [] initialOpts {outDir = Nothing}

cmodule :: Module
cmodule = package "Bits" $ do
  incl test1
  incl test2
  incl test3
  incl test4
  incl test5
  incl test6

test1 :: Def ('[Uint8, Uint16, Uint32, Uint64] ':-> Uint64)
test1 = proc "test1" $ \u8 u16 u32 u64 -> body $ do
  a <- assign $ u8  .& 0xFF
  b <- assign $ u16 .& 0xFF00
  c <- assign $ u32 .& 0xFF0000
  d <- assign $ u64 .& 0xFF000000
  ret $ (safeCast a) .| (safeCast b) .| (safeCast c) .| d

-- | Convert an array of four 8-bit integers into a 32-bit integer.
test2 :: Def ('[Ref s ('Array 4 ('Stored Uint8))] ':-> Uint32)
test2 = proc "test2" $ \arr -> body $ do
  a <- deref (arr ! 0)
  b <- deref (arr ! 1)
  c <- deref (arr ! 2)
  d <- deref (arr ! 3)
  ret $ ((safeCast a) `iShiftL` 24) .|
        ((safeCast b) `iShiftL` 16) .|
        ((safeCast c) `iShiftL` 8)  .|
        ((safeCast d) `iShiftL` 0)

-- | Example of using "extractByte" with a state monad.
extractUint32 :: Uint32 -> (Uint8, Uint8, Uint8, Uint8)
extractUint32 x = fst $ runState x $ do
  a <- sets extractByte
  b <- sets extractByte
  c <- sets extractByte
  d <- sets extractByte
  return (a, b, c, d)

-- | Convert a 32-bit integer to an array of 8-bit integers.
test3 :: Def ('[Uint32, Ref s ('Array 4 ('Stored Uint8))] ':-> ())
test3 = proc "test3" $ \n arr -> body $ do
  let (a, b, c, d) = extractUint32 n
  store (arr ! 0) d
  store (arr ! 1) c
  store (arr ! 2) b
  store (arr ! 3) a

setBit :: (IvoryBits a, IvoryStore a)
       => (Ref s ('Stored a)) -> Int -> Ivory eff ()
setBit ref bit = do
  val <- deref ref
  store ref (val .| (1 `iShiftL` (fromIntegral bit)))

clearBit :: (IvoryBits a, IvoryStore a)
         => (Ref s ('Stored a)) -> Int -> Ivory eff ()
clearBit ref bit = do
  val <- deref ref
  store ref (val .& (iComplement (1 `iShiftL` (fromIntegral bit))))

test4 :: Def ('[] ':-> Uint32)
test4 = proc "test4" $ body $ do
  n <- local (ival 0)
  setBit n 1
  setBit n 3
  setBit n 5
  setBit n 8
  clearBit n 3
  ret =<< deref n

test5 :: Def ('[Sint8] ':-> Uint8)
test5 = proc "test5" $ \s -> body $
  ret (twosComplementRep s)

test6 :: Def ('[Uint8] ':-> Sint8)
test6 = proc "test6" $ \s -> body $
  ret (twosComplementCast s)
