    {-# LANGUAGE DataKinds                        #-}
    {-# LANGUAGE GeneralizedNewtypeDeriving       #-}
    {-# LANGUAGE FlexibleInstances                #-}
    {-# LANGUAGE MultiParamTypeClasses            #-}
    {-# LANGUAGE TypeFamilies                     #-}

    -- | The internals of ChaCha20 ciphers.
    module Raaz.Cipher.ChaCha20.Internal
           ( ChaCha20(..), WORD, Counter(..), IV(..), KEY(..), ChaCha20Mem(..)
           ) where

    import Control.Applicative
    import Data.Word
    import Data.String
    import Foreign.Storable
    import Prelude

    import Raaz.Core
    import Raaz.Cipher.Internal

    -- | The chacha20 stream cipher.

    -- | The word type
    type WORD     = LE Word32

    -- | The IV for the chacha20
    newtype IV       = IV (Tuple 3 (LE Word32))     deriving (Storable, EndianStore)

    instance Encodable IV

    instance Show IV where
      show = showBase16
    instance IsString IV where
      fromString = fromBase16

    -- | The counter type for chacha20
    newtype Counter  = Counter (LE Word32) deriving (Num, Storable, EndianStore, Show, Eq, Ord)

    -- | The key type.
    newtype KEY      = ChaCha20Key (Tuple 8 WORD) deriving (Storable, EndianStore)

    instance Encodable KEY

    instance Show KEY where
      show = showBase16

    instance IsString KEY where
      fromString = fromBase16

    -- | The type associated with the ChaCha20 cipher.
    data ChaCha20 = ChaCha20

    instance Primitive ChaCha20 where
      blockSize _ = BYTES 64
      type Implementation ChaCha20 = SomeCipherI ChaCha20

    -- | The key for ChaCha20.
    type instance Key ChaCha20 = (KEY, IV, Counter)

    instance Describable ChaCha20 where
      name        _ = "chacha20"
      description _ = "The ChaCha20 cipher"

    instance Cipher ChaCha20

    instance StreamCipher ChaCha20

    ---------- Memory for ChaCha20 implementations  ------------------
    -- | chacha20 memory
    data ChaCha20Mem = ChaCha20Mem { keyCell      :: MemoryCell KEY
                                   , ivCell       :: MemoryCell IV
                                   , counterCell  :: MemoryCell Counter
                                   }

    instance Memory ChaCha20Mem where
      memoryAlloc     = ChaCha20Mem <$> memoryAlloc <*> memoryAlloc <*> memoryAlloc
      unsafeToPointer = unsafeToPointer . keyCell

    instance Initialisable ChaCha20Mem (KEY, IV, Counter) where
      initialise (k,iv,ctr) = do onSubMemory keyCell     $ initialise k
                                 onSubMemory ivCell      $ initialise iv
                                 onSubMemory counterCell $ initialise ctr

    instance Initialisable ChaCha20Mem (KEY, IV) where
      initialise (k, iv) = initialise (k, iv, 0 :: Counter)