{-# LANGUAGE MagicHash, NoMonomorphismRestriction, CPP #-}
module Bio.PDB.Structure.List(List(..), TempList,
                              initialNew, new, add, finalize, tempLength, empty, last,
                              singleton,
                              map, mapM, foldl, foldl', foldr, foldM, filter, length,
                              defaultSize, residueVectorSize, chainVectorSize,
                              toList, vimap, (!)
                             ) where

--import Prelude(Int,Num(..),Monad(..))
import Prelude hiding (length, filter, drop, take, init, tail, mapM, splitAt, map, mapM, foldl, foldr, last)
import qualified Data.Vector.Mutable as M
import qualified Data.Vector as V
import Control.Monad(when)
import Control.DeepSeq(NFData(..))
import Control.Monad.State.Strict(lift)
import Data.STRef as ST

-- | Type alias for a immutable sequence of elements.
type List a       = V.Vector a

-- | Type alias for a mutable sequence of elements.
data TempList m a = TempList {-# Unpack #-} !(ST.STRef m Int) !(ST.STRef m (M.MVector m a))

--instance Show (TempList m a) where
--  showsPrec p (TempList i v) s = Prelude.concat ["TempList ", show i, " ..."]

-- | Empty vector.
empty = V.empty

-- | Vector with a single element
singleton= V.singleton

#ifdef DEFINE_NFDATA_VECTOR
-- It is defined in newer versions of vector package.
instance NFData (V.Vector a)   where
#endif

instance NFData (TempList m a) where
  rnf t@(TempList i v) = i `seq` v `seq` ()

-- Defined in Data.Vector
--instance Show (M.MVector a) where
--  showsPrec v = shows $ V.fromList v

-- | Create a new mutable vector.
new i = lift $ initialNew i

-- | Allocate initial space for a new mutable vector.
initialNew i = do v  <- M.new i
                  s  <- ST.newSTRef 0
                  vs <- ST.newSTRef v
                  return $! TempList s vs

-- | Length of mutable vector.
tempLength (TempList s v) = lift $ readSTRef s

-- | Default initial size of a mutable vector for residue contents.
residueVectorSize =  23 -- most PDB atoms per residue seem to be in RNA

-- | Default initial size of a mutable vector for chain contents.
chainVectorSize   = 150 -- we don't really expect it to be much shorter..., average is 300, but PDB contains more small proteins of course

-- | Default initial size of a mutable vector for structure contents.
defaultSize       =   4 -- Not much point in allocating less than this...

-- | Appends an element to a mutable vector.
add (TempList s vs) a = lift $ do i <- readSTRef s
                                  v <- readSTRef vs
                                  when (i < 0) $ fail "Negative STRef"
                                  let j = i + 1
                                      l = M.length v
                                  v' <- if j > l
                                          then do nV <- M.grow v j
                                                  writeSTRef vs nV
                                                  return nV
                                          else return v
                                  M.write v' i a
                                  ST.modifySTRef s (+1)
                                  return ()

-- | Finalizes a mutable vector, and returns immutable vector.
--   [Does it shrink allocated space?]
finalize (TempList s vs) = lift $ do i  <- readSTRef s
                                     when (i < 0) $ fail "Negative STRef"
                                     v  <- readSTRef vs
                                     v' <- V.unsafeFreeze v
                                     writeSTRef s (-1)
                                     -- Can I? || writeSTRef vs undefined ||
                                     return $! V.slice 0 i v'


-- | `foldl` on immutable vectors.
foldl  = V.foldl

-- | `foldl'` on immutable vectors.
foldl' = V.foldl'

-- | `foldr` on immutable vectors.
foldr  = V.foldr 

-- | `map` on immutable vectors.
map    = V.map

-- | `filter` on immutable vectors.
filter = V.filter

-- | `mapM` on immutable vectors.
mapM   = V.mapM

-- | `foldM` on immutable vectors.
foldM  = V.foldM

-- | `length` on immutable vectors.
length = V.length

-- | `last` on immutable vectors.
last   = V.last  

-- | Conversion of an immutable vector to list.
toList = V.toList

-- | `map` on immutable vectors.
vimap  = V.imap

-- | Indexing of an immutable vector.
(!)    = (V.!)