module UHC.Light.Compiler.Base.UID
( mkNewLevUID, mkNewLevUID2, mkNewLevUID3, mkNewLevUID4, mkNewLevUID5, mkNewLevUID6, mkNewLevUID7, mkNewLevUID8, uidNext, mkNewUID, mkNewUIDL, uidNull, uidChild, mkInfNewUIDL
, FreshUidT, FreshUid, runFreshUidT, runFreshUid, evalFreshUid
, MonadFreshUID (..)
, UID (..)
, mkUID
, uidFromInt
, uidStart, uidUnused
, nextUnique
, UIDS
, mkNewLevUIDL, mkInfNewLevUIDL
, showUIDParseable, ppUIDParseable
, uidSimplifications )
where
import UHC.Util.Pretty
import qualified Data.Set as Set
import Data.List
import Control.Monad.State
import Control.Monad.Identity
import Control.Monad
import UHC.Util.Binary as B
import UHC.Util.Serialize
import Data.Hashable





{-# LINE 37 "src/ehc/Base/UID.chs" #-}
type FreshUidT m   = StateT UID m
type FreshUid      = FreshUidT Identity

{-
freshUID :: MonadState UID m => m UID
freshUID = state $ \x -> (x, uidNext x)
-}

runFreshUidT :: Monad m => FreshUidT m a -> UID -> m (a,UID)
runFreshUidT f u = runStateT f u
{-# INLINE runFreshUidT #-}

runFreshUid :: FreshUid a -> UID -> (a,UID)
runFreshUid f u = runIdentity $ runFreshUidT f u
{-# INLINE runFreshUid #-}

evalFreshUid :: FreshUid a -> UID -> a
evalFreshUid f u = fst $ runIdentity $ runFreshUidT f u
{-# INLINE evalFreshUid #-}

{-# LINE 59 "src/ehc/Base/UID.chs" #-}
class Monad m => MonadFreshUID m where
  -- | Fresh single UID
  freshUID :: m UID
  freshUID = freshInfUID

  -- | Fresh infinite range of UID
  freshInfUID :: m UID
  freshInfUID = freshUID

-- TBD: flip results of mkNewLevUID (etc) to be in agreement with behavior of state
instance Monad m => MonadFreshUID (FreshUidT m) where
  freshUID = state $ \x -> (x, uidNext x)
  {-# INLINE freshUID #-}

  freshInfUID = state $ \x -> (uidChild x, uidNext x)
  {-# INLINE freshInfUID #-}

{-# LINE 83 "src/ehc/Base/UID.chs" #-}
data UID = UID { uidHash :: !Int, uidInts :: ![Int] }
  deriving (Eq,Ord)

{-# LINE 92 "src/ehc/Base/UID.chs" #-}
mkUID :: [Int] -> UID
mkUID is = UID (hash is) is

{-# LINE 101 "src/ehc/Base/UID.chs" #-}
type UIDL = [UID]

{-# LINE 105 "src/ehc/Base/UID.chs" #-}
type UIDS = Set.Set UID

{-# LINE 109 "src/ehc/Base/UID.chs" #-}
instance Show UID where
  show uid = concat . intersperse "_" . map show . reverse $ uidInts uid

{-# LINE 114 "src/ehc/Base/UID.chs" #-}
instance Hashable UID where
  hashWithSalt salt (UID h _) = salt `hashWithSalt` h

{-# LINE 119 "src/ehc/Base/UID.chs" #-}
uidNext :: UID -> UID
uidNext (UID _ (n:ns)) = mkUID (n+1:ns)

uidChild :: UID -> UID
uidChild (UID _ ns) = mkUID (0:ns)

{-# LINE 135 "src/ehc/Base/UID.chs" #-}
mkNewLevUID :: UID -> (UID,UID)
mkNewLevUID u = (uidNext u, uidChild u)

{-# LINE 140 "src/ehc/Base/UID.chs" #-}
uidFromInt :: Int -> UID
uidFromInt i = mkUID [i]

{-# LINE 145 "src/ehc/Base/UID.chs" #-}
uidStart :: UID
uidStart = uidFromInt 0

uidUnused :: UID
uidUnused = uidFromInt (-1)

{-# LINE 153 "src/ehc/Base/UID.chs" #-}
mkNewLevUID2 u = let { (u',u1)          = mkNewLevUID   u; (u'',u2)          = mkNewLevUID   u'} in (u'',u1,u2)
mkNewLevUID3 u = let { (u',u1,u2)       = mkNewLevUID2  u; (u'',u3)          = mkNewLevUID   u'} in (u'',u1,u2,u3)
mkNewLevUID4 u = let { (u',u1,u2)       = mkNewLevUID2  u; (u'',u3,u4)       = mkNewLevUID2  u'} in (u'',u1,u2,u3,u4)
mkNewLevUID5 u = let { (u',u1,u2)       = mkNewLevUID2  u; (u'',u3,u4,u5)    = mkNewLevUID3  u'} in (u'',u1,u2,u3,u4,u5)
mkNewLevUID6 u = let { (u',u1,u2,u3)    = mkNewLevUID3  u; (u'',u4,u5,u6)    = mkNewLevUID3  u'} in (u'',u1,u2,u3,u4,u5,u6)
mkNewLevUID7 u = let { (u',u1,u2,u3,u4) = mkNewLevUID4  u; (u'',u5,u6,u7)    = mkNewLevUID3  u'} in (u'',u1,u2,u3,u4,u5,u6,u7)
mkNewLevUID8 u = let { (u',u1,u2,u3,u4) = mkNewLevUID4  u; (u'',u5,u6,u7,u8) = mkNewLevUID4  u'} in (u'',u1,u2,u3,u4,u5,u6,u7,u8)

uidNull :: UID
uidNull  = mkUID []

mkNewUID :: UID -> (UID,UID)
mkNewUID   uid = (uidNext uid,uid)

mkInfNewUIDL' :: (UID -> (UID,UID)) -> UID -> [UID]
mkInfNewUIDL' mk uid
  =  let  l = drop 1 $ iterate (\(nxt,uid) -> mk nxt) $ mkNewUID uid
     in   map snd l

mkNewUIDL' :: (UID -> (UID,UID)) -> Int -> UID -> [UID] -- assume sz > 0
mkNewUIDL' mk sz uid
  =  take sz (mkInfNewUIDL' mk uid)

mkNewUIDL :: Int -> UID -> [UID] -- assume sz > 0
mkNewUIDL = mkNewUIDL' mkNewUID

mkInfNewUIDL :: UID -> [UID]
mkInfNewUIDL = mkInfNewUIDL' mkNewUID

instance PP UID where
  pp = text . show

{-# LINE 187 "src/ehc/Base/UID.chs" #-}
-- | Inverse of pUID
showUIDParseable :: UID -> String
-- showUIDParseable uid = "%[" ++ (concat $ intersperse "/" $ map show $ uidInts uid) ++ "]"
showUIDParseable uid = "`{" ++ (concat $ intersperse "," $ map show $ uidInts uid) ++ "}"

-- | Inverse of pUID
ppUIDParseable :: UID -> PP_Doc
ppUIDParseable = pp . showUIDParseable

{-# LINE 198 "src/ehc/Base/UID.chs" #-}
mkInfNewLevUIDL :: UID -> [UID]
mkInfNewLevUIDL = mkInfNewUIDL' mkNewLevUID

mkNewLevUIDL :: Int -> UID -> [UID]
mkNewLevUIDL = mkNewUIDL' mkNewLevUID

{-# LINE 210 "src/ehc/Base/UID.chs" #-}
-- | Simplifications obtained by omitting all but 1 of the Int's, then re-adding one by one, omitting the original
uidSimplifications :: UID -> [UID]
uidSimplifications = map (mkUID . reverse) . drop 1 . init . inits . reverse . uidInts

{-# LINE 220 "src/ehc/Base/UID.chs" #-}
nextUnique = mkNewLevUID

{-# LINE 228 "src/ehc/Base/UID.chs" #-}
deriving instance Typeable UID
deriving instance Data UID

{-# LINE 246 "src/ehc/Base/UID.chs" #-}
instance Binary UID where
  put (UID a b) = B.put a >> B.put b
  get = liftM2 UID B.get B.get

instance Serialize UID where
  sput = sputShared
  sget = sgetShared
  sputNested = sputPlain
  sgetNested = sgetPlain