module Basement.NormalForm
    ( NormalForm(..)
    , deepseq
    , force
    ) where

import Basement.Compat.Base
import Basement.Compat.C.Types
import Basement.Compat.Natural
import Basement.Types.OffsetSize
import Basement.Types.Char7
import Basement.Types.Word128 (Word128)
import Basement.Types.Word256 (Word256)
import Basement.Bounded
import Basement.Endianness

-- | Data that can be fully evaluated in Normal Form
--
class NormalForm a where
    toNormalForm :: a -> ()

deepseq :: NormalForm a => a -> b -> b
deepseq a b = toNormalForm a `seq` b

force :: NormalForm a => a -> a
force a = toNormalForm a `seq` a

-----
-- GHC / base types

instance NormalForm Int8    where toNormalForm !_ = ()
instance NormalForm Int16   where toNormalForm !_ = ()
instance NormalForm Int32   where toNormalForm !_ = ()
instance NormalForm Int64   where toNormalForm !_ = ()
instance NormalForm Int     where toNormalForm !_ = ()
instance NormalForm Integer where toNormalForm !_ = ()

instance NormalForm Word8   where toNormalForm !_ = ()
instance NormalForm Word16  where toNormalForm !_ = ()
instance NormalForm Word32  where toNormalForm !_ = ()
instance NormalForm Word64  where toNormalForm !_ = ()
instance NormalForm Word    where toNormalForm !_ = ()
instance NormalForm Natural where toNormalForm !_ = ()

instance NormalForm Float  where toNormalForm !_ = ()
instance NormalForm Double where toNormalForm !_ = ()

instance NormalForm Char where toNormalForm !_ = ()
instance NormalForm Bool where toNormalForm !_ = ()
instance NormalForm ()   where toNormalForm !_ = ()

-----
-- C Types
instance NormalForm CChar  where toNormalForm !_ = ()
instance NormalForm CUChar where toNormalForm !_ = ()
instance NormalForm CSChar where toNormalForm !_ = ()

instance NormalForm CShort  where toNormalForm !_ = ()
instance NormalForm CUShort where toNormalForm !_ = ()
instance NormalForm CInt    where toNormalForm !_ = ()
instance NormalForm CUInt   where toNormalForm !_ = ()
instance NormalForm CLong   where toNormalForm !_ = ()
instance NormalForm CULong  where toNormalForm !_ = ()
instance NormalForm CLLong  where toNormalForm !_ = ()
instance NormalForm CULLong where toNormalForm !_ = ()

instance NormalForm CFloat  where toNormalForm !_ = ()
instance NormalForm CDouble where toNormalForm !_ = ()

instance NormalForm (Ptr a) where toNormalForm !_ = ()

-----
-- Basic Foundation primitive types
instance NormalForm (Offset a) where toNormalForm !_ = ()
instance NormalForm (CountOf a) where toNormalForm !_ = ()

instance NormalForm Char7 where toNormalForm !_ = ()
instance NormalForm Word128 where toNormalForm !_ = ()
instance NormalForm Word256 where toNormalForm !_ = ()
instance NormalForm (Zn n) where toNormalForm = toNormalForm . unZn
instance NormalForm (Zn64 n) where toNormalForm = toNormalForm . unZn64

-----
-- composed type

instance NormalForm a => NormalForm (Maybe a) where
    toNormalForm Nothing  = ()
    toNormalForm (Just a) = toNormalForm a `seq` ()
instance (NormalForm l, NormalForm r) => NormalForm (Either l r) where
    toNormalForm (Left l)  = toNormalForm l `seq` ()
    toNormalForm (Right r) = toNormalForm r `seq` ()
instance NormalForm a => NormalForm (LE a) where
    toNormalForm (LE a) = toNormalForm a `seq` ()
instance NormalForm a => NormalForm (BE a) where
    toNormalForm (BE a) = toNormalForm a `seq` ()

instance NormalForm a => NormalForm [a] where
    toNormalForm []     = ()
    toNormalForm (x:xs) = toNormalForm x `seq` toNormalForm xs

instance (NormalForm a, NormalForm b) => NormalForm (a,b) where
    toNormalForm (a,b) = toNormalForm a `seq` toNormalForm b

instance (NormalForm a, NormalForm b, NormalForm c) => NormalForm (a,b,c) where
    toNormalForm (a,b,c) = toNormalForm a `seq` toNormalForm b `seq` toNormalForm c

instance (NormalForm a, NormalForm b, NormalForm c, NormalForm d) => NormalForm (a,b,c,d) where
    toNormalForm (a,b,c,d) = toNormalForm a `seq` toNormalForm b `seq` toNormalForm c `seq` toNormalForm d

instance (NormalForm a, NormalForm b, NormalForm c, NormalForm d, NormalForm e)
      => NormalForm (a,b,c,d,e) where
    toNormalForm (a,b,c,d,e) =
        toNormalForm a `seq` toNormalForm b `seq` toNormalForm c `seq` toNormalForm d `seq`
        toNormalForm e

instance (NormalForm a, NormalForm b, NormalForm c, NormalForm d, NormalForm e, NormalForm f)
      => NormalForm (a,b,c,d,e,f) where
    toNormalForm (a,b,c,d,e,f) =
        toNormalForm a `seq` toNormalForm b `seq` toNormalForm c `seq` toNormalForm d `seq`
        toNormalForm e `seq` toNormalForm f

instance (NormalForm a, NormalForm b, NormalForm c, NormalForm d, NormalForm e, NormalForm f, NormalForm g)
      => NormalForm (a,b,c,d,e,f,g) where
    toNormalForm (a,b,c,d,e,f,g) =
        toNormalForm a `seq` toNormalForm b `seq` toNormalForm c `seq` toNormalForm d `seq`
        toNormalForm e `seq` toNormalForm f `seq` toNormalForm g
instance (NormalForm a, NormalForm b, NormalForm c, NormalForm d, NormalForm e, NormalForm f, NormalForm g, NormalForm h)
      => NormalForm (a,b,c,d,e,f,g,h) where
    toNormalForm (a,b,c,d,e,f,g,h) =
        toNormalForm a `seq` toNormalForm b `seq` toNormalForm c `seq` toNormalForm d `seq`
        toNormalForm e `seq` toNormalForm f `seq` toNormalForm g `seq` toNormalForm h