{-# LANGUAGE GADTs                #-}
{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE StandaloneDeriving   #-}
{-# LANGUAGE FlexibleContexts     #-}

module Language.Embedded.Hardware.Expression.Represent where

import Language.Embedded.Hardware.Expression.Represent.Bit

import Language.Embedded.VHDL (VHDL, MonadV)
import Language.Embedded.VHDL.Monad (newSym, addLibrary, addImport)
import Language.Embedded.VHDL.Monad.Type
import Language.Embedded.VHDL.Monad.Util (printBits)

import Data.Char (isDigit)
import Data.Int
import Data.Word
import Data.Proxy (Proxy(..))
import Data.Typeable (Typeable)

import GHC.TypeLits

--------------------------------------------------------------------------------
-- * Inhabited types.
--------------------------------------------------------------------------------

-- | Inhabited types, that is, types with a base element.
class Inhabited a
  where
    -- | Ground value.
    reset :: a

instance Inhabited Bool    where reset = False
instance Inhabited Int8    where reset = 0
instance Inhabited Int16   where reset = 0
instance Inhabited Int32   where reset = 0
instance Inhabited Int64   where reset = 0
instance Inhabited Word8   where reset = 0
instance Inhabited Word16  where reset = 0
instance Inhabited Word32  where reset = 0
instance Inhabited Word64  where reset = 0
instance Inhabited Int     where reset = 0
instance Inhabited Integer where reset = 0
instance Inhabited Float   where reset = 0
instance Inhabited Double  where reset = 0

instance forall n . KnownNat n => Inhabited (Bits n)
  where
    reset = bitFromInteger 0

--------------------------------------------------------------------------------
-- * Sized types.
--------------------------------------------------------------------------------

-- | Types with a known size.
class Sized a
  where
    -- | Bits required to represent values of type 'a'.
    bits :: proxy a -> Integer

instance Sized Bool    where bits _ = 1
instance Sized Int8    where bits _ = 8
instance Sized Int16   where bits _ = 16
instance Sized Int32   where bits _ = 32
instance Sized Int64   where bits _ = 64
instance Sized Word8   where bits _ = 8
instance Sized Word16  where bits _ = 16
instance Sized Word32  where bits _ = 32
instance Sized Word64  where bits _ = 64
instance Sized Int     where bits _ = 32
instance Sized Integer where bits _ = 64
instance Sized Float   where bits _ = 32
instance Sized Double  where bits _ = 64

instance forall n . KnownNat n => Sized (Bits n)
  where
    bits _ = ni (Proxy :: Proxy n)

--------------------------------------------------------------------------------
-- * Representable types.
--------------------------------------------------------------------------------

-- | Representation of primitive hardware types.
data TypeRep a
  where
    -- booleans.
    BoolT    :: TypeRep Bool
    -- signed numbers.
    Int8T    :: TypeRep Int8
    Int16T   :: TypeRep Int16
    Int32T   :: TypeRep Int32
    Int64T   :: TypeRep Int64
    -- unsigned numbers.
    Word8T   :: TypeRep Word8
    Word16T  :: TypeRep Word16
    Word32T  :: TypeRep Word32
    Word64T  :: TypeRep Word64
    -- integers.
    IntT     :: TypeRep Int
    IntegerT :: TypeRep Integer
    -- floating point numbers.
    FloatT   :: TypeRep Float
    DoubleT  :: TypeRep Double
    -- variable sized bit values (todo).
    BitsT    :: KnownNat n => TypeRep (Bits n)

deriving instance Eq       (TypeRep a)
deriving instance Show     (TypeRep a)
deriving instance Typeable (TypeRep a)

isBool :: TypeRep a -> Bool
isBool BoolT = True
isBool _     = False

isSigned :: TypeRep a -> Bool
isSigned Int8T  = True
isSigned Int16T = True
isSigned Int32T = True
isSigned Int64T = True
isSigned _      = False

isUnsigned :: TypeRep a -> Bool
isUnsigned Word8T  = True
isUnsigned Word16T = True
isUnsigned Word32T = True
isUnsigned Word64T = True

isInteger :: TypeRep a -> Bool
isInteger IntT     = True
isInteger IntegerT = True
isInteger _        = False

isFloat :: TypeRep a -> Bool
isFloat FloatT  = True
isFloat DoubleT = True
isFloat _       = False

--------------------------------------------------------------------------------

-- | Primitive hardware types.
class (Eq a, Show a, Typeable a, Inhabited a, Sized a) => PrimType a
  where
    typeRep :: TypeRep a

instance PrimType Bool    where typeRep = BoolT
instance PrimType Int8    where typeRep = Int8T
instance PrimType Int16   where typeRep = Int16T
instance PrimType Int32   where typeRep = Int32T
instance PrimType Int64   where typeRep = Int64T
instance PrimType Word8   where typeRep = Word8T
instance PrimType Word16  where typeRep = Word16T
instance PrimType Word32  where typeRep = Word32T
instance PrimType Word64  where typeRep = Word64T
instance PrimType Int     where typeRep = IntT
instance PrimType Integer where typeRep = IntegerT
instance PrimType Float   where typeRep = FloatT
instance PrimType Double  where typeRep = DoubleT

instance forall n . KnownNat n => PrimType (Bits n)
  where
    typeRep = BitsT

--------------------------------------------------------------------------------

-- | Print a value.
primTypeVal :: forall a . PrimType a => a -> String
primTypeVal a = case typeRep :: TypeRep a of
  BoolT    -> if a then "\'1\'" else "\'0\'"
  t        -> show a

-- | Print a value as its bit representation.
primTypeBits :: forall a . PrimType a => a -> String
primTypeBits a = case typeRep :: TypeRep a of
  BoolT    -> primTypeVal a
  Int8T    -> printBits 8  a
  Int16T   -> printBits 16 a
  Int32T   -> printBits 32 a
  Int64T   -> printBits 64 a
  Word8T   -> printBits 8  a
  Word16T  -> printBits 16 a
  Word32T  -> printBits 32 a
  Word64T  -> printBits 64 a
  IntT     -> error "todo: print ints as bits."
  IntegerT -> error "todo: print integers as bits."
  FloatT   -> error "todo: print floats as bits."
  DoubleT  -> error "todo: print doubles as bits."

-- | Hardware type representation of a primitive type.
primTypeRep :: forall a . PrimType a => Proxy a -> Type
primTypeRep _ = case typeRep :: TypeRep a of
  BoolT    -> std_logic
  Int8T    -> signed8
  Int16T   -> signed16
  Int32T   -> signed32
  Int64T   -> signed64
  Word8T   -> usigned8
  Word16T  -> usigned16
  Word32T  -> usigned32
  Word64T  -> usigned64
  IntT     -> integer Nothing -- todo: might be wrong.
  IntegerT -> integer Nothing -- todo: migth be wrong.
  FloatT   -> float
  DoubleT  -> double
  BitsT    -> primTypeRepBits (Proxy :: Proxy a)

primTypeRepBits :: forall n. KnownNat n => Proxy (Bits n) -> Type
primTypeRepBits _ = std_logic_vector size
  where size = fromInteger (ni (undefined :: Bits n))

-- | Declare the necessary imports/packages to support a primitive type.
primTypeDeclare :: forall m a. (MonadV m, PrimType a) => Proxy a -> m ()
primTypeDeclare p = case typeRep :: TypeRep a of
  BoolT    -> declareBoolean
  Int8T    -> declareNumeric
  Int16T   -> declareNumeric
  Int32T   -> declareNumeric
  Int64T   -> declareNumeric
  Word8T   -> declareNumeric
  Word16T  -> declareNumeric
  Word32T  -> declareNumeric
  Word64T  -> declareNumeric
  IntT     -> declareNumeric
  IntegerT -> declareNumeric
  FloatT   -> declareFloating
  DoubleT  -> declareFloating
  BitsT    -> declareBoolean

-- | Declare a primitive hardware type and get back its representation.
declareType :: (MonadV m, PrimType a) => Proxy a -> m Type
declareType proxy = primTypeDeclare proxy >> return (primTypeRep proxy)

-- | Declare the necessary libraries to support boolean operations.
declareBoolean :: MonadV m => m ()
declareBoolean =
  do addLibrary "IEEE"
     addImport  "IEEE.std_logic_1164"

-- | Declare the necessary libraries to support numerical operations.
declareNumeric :: MonadV m => m ()
declareNumeric =
  do addLibrary "IEEE"
     addImport  "IEEE.std_logic_1164"
     addImport  "IEEE.numeric_std"

-- | Declare the necessary libraries to support floating point operations.
declareFloating :: MonadV m => m ()
declareFloating =
  do addLibrary "IEEE"
     addImport  "IEEE.float_pkg"

--------------------------------------------------------------------------------
