{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE TypeOperators        #-}
{-# LANGUAGE TypeApplications     #-}
{-# LANGUAGE DataKinds            #-}
{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE AllowAmbiguousTypes  #-}
{-# LANGUAGE UndecidableInstances #-}

module Foreign.Storable.Generic.Tools.TypeFuns where

import Data.Proxy
import GHC.Generics
import GHC.TypeLits

type family SumArity (arg :: * -> *) where
    SumArity (M1 C _ t) = 1
    SumArity (M1 _ _ t) =  SumArity t
    SumArity  (f :+: g) = (SumArity f) + (SumArity g)
    -- There should be no more constructors within products, but who knows..   
    SumArity  (f :*: g) = (SumArity f) + (SumArity g)
    SumArity _          = 0

type family NoFields (arg :: * -> *) where
    NoFields (M1 _ _ t) =  NoFields t
    NoFields (f  :+: g) = (NoFields f) + (NoFields g)
    NoFields (f  :*: g) = (NoFields f) + (NoFields g)
    NoFields (K1  _  _) = 1
    NoFields _          = 0

type IsSumType (arg :: * -> *) = IsSumType' (CmpNat (SumArity arg) (1))

type family IsSumType' (ret :: Ordering) :: Bool where
    IsSumType' GT = True
    IsSumType' _  = False

noFields :: (KnownNat (NoFields f)) => f p -> Int
noFields (a :: f p) = fromIntegral.natVal $ (Proxy :: Proxy (NoFields f))

sumArity :: (KnownNat (SumArity f)) => f p -> Int
sumArity (a :: f p) = fromIntegral.natVal $ (Proxy :: Proxy (SumArity f))