{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances,
  UndecidableInstances, FlexibleContexts #-}

{- |
   The HList library

   (C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke

   Type-indexed operations on typeful heterogeneous lists.
-}

module Data.HList.HTypeIndexed where

import Data.HList.FakePrelude
import Data.HList.HListPrelude
import Data.HList.HArray
import Data.HList.HOccurs


{-----------------------------------------------------------------------------}

class HDeleteMany e l l' | e l -> l'
 where
  hDeleteMany :: Proxy e -> l -> l'

instance HDeleteMany e HNil HNil
 where
  hDeleteMany _ HNil = HNil

instance ( HList l
         , TypeEq e e' b
         , HDeleteManyCase b e e' l l'
         )
      =>   HDeleteMany e (HCons e' l) l'
 where
  hDeleteMany p (HCons e' l) = l'
   where
    b  = proxyEq p (toProxy e')
    l' = hDeleteManyCase b p e' l

class HDeleteManyCase b e e' l l' | b e e' l -> l'
 where
  hDeleteManyCase :: b -> Proxy e -> e' -> l -> l'

instance HDeleteMany e l l'
      => HDeleteManyCase HTrue e e l l'
 where
  hDeleteManyCase _ p _ l = hDeleteMany p l


instance HDeleteMany e l l'
      => HDeleteManyCase HFalse e e' l (HCons e' l')
 where
  hDeleteManyCase _ p e' l = HCons e' (hDeleteMany p l)


{-----------------------------------------------------------------------------}

-- | Map a type to a natural

class HNat n => HType2HNat e l n | e l -> n
instance (TypeEq e' e b, HType2HNatCase b e l n)
      =>  HType2HNat e (HCons e' l) n

-- | Helper class

class (HBool b, HNat n) => HType2HNatCase b e l n | b e l -> n
instance HOccursNot e l => HType2HNatCase HTrue e l HZero
instance HType2HNat e l n => HType2HNatCase HFalse e l (HSucc n)

hType2HNat :: HType2HNat e l n => Proxy e -> l -> n
hType2HNat _ _ = undefined



-- | Map types to naturals

class HTypes2HNats ps l ns | ps l -> ns
 where
  hTypes2HNats :: ps -> l -> ns

instance HTypes2HNats HNil l HNil
 where
  hTypes2HNats _ _ = HNil

instance ( HType2HNat   e l n
         , HTypes2HNats ps l ns
         )
      =>   HTypes2HNats (HCons (Proxy e) ps) l (HCons n ns)
 where
  hTypes2HNats (HCons p ps) l = HCons (hType2HNat p l) (hTypes2HNats ps l)


{-----------------------------------------------------------------------------}

-- | Define type-indexed delete in terms of the natural-based primitive
hDeleteAtProxy :: (HDeleteAtHNat n l l', HType2HNat e l n) => Proxy e -> l -> l'
hDeleteAtProxy p l = hDeleteAtHNat (hType2HNat p l) l


{-----------------------------------------------------------------------------}

-- | Define type-indexed update in terms of the natural-based update
hUpdateAtType :: (HUpdateAtHNat n e l l', HType2HNat e l n) => e -> l -> l'
hUpdateAtType e l = hUpdateAtHNat (hType2HNat (toProxy e) l) e l


{-----------------------------------------------------------------------------}

-- | Projection based on proxies
hProjectByProxies :: (HProjectByHNats ns l l', HTypes2HNats ps l ns) => ps -> l -> l'
hProjectByProxies ps l = hProjectByHNats (hTypes2HNats ps l) l


{-----------------------------------------------------------------------------}

-- | Splitting based on proxies
hSplitByProxies :: (HMap (HAddTag HTrue) l l', HSplitByHNats' ns l' l'1 l'',
                   HTypes2HNats ps l ns) =>
                  ps -> l -> (l'1, l'')
hSplitByProxies ps l = hSplitByHNats (hTypes2HNats ps l) l


{-----------------------------------------------------------------------------}