module GLL.Types.DataSets where

import GLL.Types.Grammar
import GLL.Types.BSR

import qualified Data.IntMap as IM
import qualified Data.Map as M
import qualified Data.Set as S
import Data.List (nub)

type Descr t    = (Slot t, Int, Int)
type Comm t     = (Nt, Int)
data Cont t c   = Cont (Slot t, Int) c
data State t c  = State { uset       :: USet t
                        , grel       :: GRel t c
                        , prel       :: PRel t
                        , bsrs       :: BSRs t
                        , successes  :: Int
                        }

instance (Ord t) => Ord (Cont t c) where
  (Cont c _) `compare` (Cont c' _) = c `compare` c'

instance (Eq t) => Eq (Cont t c) where
  (Cont c _) == (Cont c' _) = c == c'

emptyUSet       :: USet t
addDescr        :: (Ord t) => Descr t  -> USet t   -> USet t
hasDescr        :: (Ord t) => Descr t  -> USet t   -> Bool

emptyG         :: GRel t c
addCont         :: (Ord t) => Comm t -> (Slot t, Int, c) -> GRel t c -> GRel t c
conts           :: Comm t -> GRel t c -> [(Slot t, Int, c)]

emptyP         :: PRel t
addExtent       :: Comm t -> Int -> PRel t -> PRel t
extents         :: Comm t -> PRel t -> [Int]

emptyState :: (Ord t) => State t c
emptyState = State emptyUSet emptyG emptyP emptyBSRs 0

type RList t    =   [Descr t]
type USet t     =   IM.IntMap (IM.IntMap (S.Set (Slot t)))
type GRel t c   =   IM.IntMap (M.Map Nt (S.Set (Cont t c)))
type PRel t     =   IM.IntMap (M.Map Nt [Int])

descrs2list :: USet t -> [(Slot t, Int, Int)]
descrs2list uset =  [ (g,l,k)
                    | (l, k2m)  <- IM.assocs uset
                    , (k, g2m)  <- IM.assocs k2m
                    , g         <- S.toList g2m ]

printDescrs :: (Show t) => USet t -> IO ()
printDescrs = putStr . unlines . map show . descrs2list

emptyRList = []
popRList (x:xs)  = (x,xs)
popRList _       = error "popRList"
unionRList       = flip (++)
singletonRList   = (:[])
fromListRList    :: Ord t => [Descr t]  -> USet t   -> RList t
fromListRList ds uset = foldr op emptyRList (nub ds)
  where op d rset   | hasDescr d uset   = rset
                    | otherwise         = unionRList (singletonRList d) rset


emptyUSet = IM.empty

addDescr alt@(slot,i,l) = IM.alter inner i
  where inner mm = case mm of
                      Nothing -> Just $ IM.singleton l single
                      Just m  -> Just $ IM.insertWith (S.union) l single m
        single = S.singleton slot

hasDescr alt@(slot,i,l) = not . maybe True inner . IM.lookup i
  where inner m = maybe True (not . (slot `S.member`)) $ IM.lookup l m

emptyG = IM.empty
singleCG k v = addCont k v emptyG
addCont (n,i) (gs,l,c) = IM.alter inner i
 where inner mm = case mm of
                    Nothing -> Just $ M.singleton n single
                    Just m  -> Just $ M.insertWith S.union n single m
       single = S.singleton (Cont (gs,l) c)
conts (n,l) = maybe [] inner . IM.lookup l
         where inner m = maybe [] (map unCont . S.toList) $ M.lookup n m
               unCont (Cont (gs,l') cf) = (gs,l',cf)

emptyP = IM.empty
addExtent (gs,l) i = IM.alter inner l
 where inner mm = case mm of
                    Nothing -> Just $ M.singleton gs [i]
                    Just m  -> Just $ M.insertWith (++) gs [i] m

extents (gs,l) = maybe [] inner . IM.lookup l
         where inner = maybe [] id .  M.lookup gs