module ExtArray where
import IOExts
import Array
import List

updIOArray         :: Ix ix => IOArray ix elt -> ix -> (elt -> elt) -> IO ()
updIOArray arr i f = readIOArray arr i >>= (writeIOArray arr i . f)


type ExtArray elt = IORef (ExtArrayI elt) 
   --in newExtArray,boundsExtArray,readExtArray,writeExtArray,clearExtArray,nextExtEntry,
   --   updExtArray

data ExtArrayI elt = 
 ExtArray {sizeExt :: Int,nextExt :: Int,freeExts :: [Int],
           extArrays :: [(Int,IOArray Int (Maybe elt))]}

newExtArray          :: Int -> IO (ExtArray elt)
newExtArray max = do
 newIORef $ ExtArray max 0 [] []

boundsExtArray       :: ExtArray elt -> IO (Int,Int)
boundsExtArray ext = do
 (ExtArray {nextExt = ix}) <- readIORef ext
 return (0,ix)

readExtArray         :: ExtArray elt -> Int -> IO (Maybe elt)
readExtArray extV ix = do
 (ExtArray {freeExts = frees,sizeExt = s,extArrays = arrs}) <- readIORef extV
 let (div,mod) = ix `divMod` s
 case lookup div arrs of
  Just arr -> readIOArray arr mod 
  _ -> return Nothing

findSafe :: [a] -> Int -> Maybe a
findSafe xs n = if length xs < n + 1 then Nothing else Just $ xs !! n

writeExtArray        :: ExtArray elt -> Int -> elt -> IO ()
writeExtArray extV ix elt = do
 ext@(ExtArray {sizeExt = s,extArrays = arrs}) <- readIORef extV
 let (div,mod) = ix `divMod` s
 case lookup div arrs of
  Nothing -> do ioarr <- newIOArray (0,s) Nothing
                writeIOArray ioarr mod (Just elt)
                writeIORef extV (ext {extArrays = insertBy comp (div,ioarr) arrs})
  Just arr -> writeIOArray arr mod (Just elt)
 where comp (a,_) (b,_) = a `compare` b

updExtArray :: ExtArray elt -> Int -> (elt -> elt) -> IO ()
updExtArray extV ix f = do
 ext@(ExtArray {sizeExt = s,extArrays = arrs}) <- readIORef extV
 let (div,mod) = ix `divMod` s
 case lookup div arrs of
  Nothing -> return ()
  Just arr -> do
   eltMb <- readIOArray arr mod
   case eltMb of
    Just elt -> writeIOArray arr mod (Just $ f elt)
    Nothing -> return ()
 where comp (a,_) (b,_) = a `compare` b

clearExtArray :: ExtArray elt -> Int -> IO ()
clearExtArray extV ix = do
 ext@(ExtArray {sizeExt = s,extArrays = arrs,freeExts = frees}) <- readIORef extV
 let (div,mod) = ix `divMod` s
 case lookup div arrs of
  Nothing -> return ()
  Just arr -> do writeIOArray arr mod Nothing
                 writeIORef extV $ ext {freeExts = ix:frees}

nextExtEntry :: ExtArray elt -> IO Int
nextExtEntry extV = do
 ext@(ExtArray {nextExt = n,freeExts = frees}) <- readIORef extV
 let (n',ext') = case frees of [] -> (n,ext {nextExt = n + 1})
                               (x:xs) -> (x,ext {freeExts = xs})
 writeIORef extV $ ext'
 return n' 
