{-# LANGUAGE UndecidableInstances #-} -- for nested type families
{-# LANGUAGE AllowAmbiguousTypes  #-} -- for reification util

module Binrep.CBLen where

import GHC.TypeNats
import Data.Word
import Data.Int
import Binrep.Util.ByteOrder

import GHC.Exts ( Int#, Int(I#), Proxy# )
import Util.TypeNats ( natValInt )

import DeFun.Core ( type (~>), type App )

import Refined

class IsCBLen a where type CBLen a :: Natural

instance IsCBLen () where type CBLen () = 0
instance (IsCBLen l, IsCBLen r) => IsCBLen (l, r) where
    type CBLen (l, r) = CBLen l + CBLen r

instance IsCBLen Word8  where type CBLen Word8  = 2^0
instance IsCBLen  Int8  where type CBLen  Int8  = 2^0
instance IsCBLen Word16 where type CBLen Word16 = 2^1
instance IsCBLen  Int16 where type CBLen  Int16 = 2^1
instance IsCBLen Word32 where type CBLen Word32 = 2^2
instance IsCBLen  Int32 where type CBLen  Int32 = 2^2
instance IsCBLen Word64 where type CBLen Word64 = 2^3
instance IsCBLen  Int64 where type CBLen  Int64 = 2^3

instance IsCBLen a => IsCBLen (ByteOrdered end a) where
    type CBLen (ByteOrdered end a) = CBLen a

instance IsCBLen (Refined (pl `And` pr) a) where
    type CBLen (Refined (pl `And` pr) a) = CBLen (Refined pr (Refined pl a))

-- | Reify a type's constant byte length to the term level.
cblen :: forall a. KnownNat (CBLen a) => Int
cblen :: forall {k} (a :: k). KnownNat (CBLen a) => Int
cblen = forall (n :: Nat). KnownNat n => Int
natValInt @(CBLen a)

cblen# :: forall a. KnownNat (CBLen a) => Int#
cblen# :: forall {k} (a :: k). KnownNat (CBLen a) => Int#
cblen# = Int#
i#
  where !(I# Int#
i#) = forall (n :: Nat). KnownNat n => Int
natValInt @(CBLen a)

cblenProxy# :: forall a. KnownNat (CBLen a) => Proxy# a -> Int#
cblenProxy# :: forall {k} (a :: k). KnownNat (CBLen a) => Proxy# a -> Int#
cblenProxy# Proxy# a
_ = Int#
i#
  where !(I# Int#
i#) = forall (n :: Nat). KnownNat n => Int
natValInt @(CBLen a)

-- | Defunctionalization symbol for 'CBLen'.
--
-- This is required for parameterized type-level generics e.g. bytezap's
-- 'Bytezap.Struct.Generic.GPokeBase'.
type CBLenSym :: a ~> Natural
data CBLenSym a
type instance App CBLenSym a = CBLen a