module Simulation.Aivika.Trans.Vector.Unboxed
       (Vector,
        newVector, 
        copyVector,
        vectorCount, 
        appendVector, 
        readVector, 
        writeVector,
        vectorBinarySearch,
        vectorInsert,
        vectorDeleteAt,
        vectorIndex,
        freezeVector) where 
import Data.Array
import Control.Monad
import Simulation.Aivika.Trans.Session
import Simulation.Aivika.Trans.ProtoRef
import Simulation.Aivika.Trans.ProtoArray.Unboxed
data Vector m a =
    Vector { vectorSession  :: Session m,
             vectorArrayRef :: ProtoRef m (ProtoArray m a),
             vectorCountRef :: ProtoRef m Int, 
             vectorCapacityRef :: ProtoRef m Int }
newVector :: ProtoArrayMonad m a => Session m -> m (Vector m a)
newVector session = 
  do array <- newProtoArray_ session 4
     arrayRef <- newProtoRef session array
     countRef <- newProtoRef session 0
     capacityRef <- newProtoRef session 4
     return Vector { vectorSession  = session,
                     vectorArrayRef = arrayRef,
                     vectorCountRef = countRef,
                     vectorCapacityRef = capacityRef }
copyVector :: ProtoArrayMonad m a => Vector m a -> m (Vector m a)
copyVector vector =
  do let session = vectorSession vector
     array <- readProtoRef (vectorArrayRef vector)
     count <- readProtoRef (vectorCountRef vector)
     array' <- newProtoArray_ session count
     arrayRef' <- newProtoRef session array'
     countRef' <- newProtoRef session count
     capacityRef' <- newProtoRef session count
     forM_ [0 .. count  1] $ \i ->
       do x <- readProtoArray array i
          writeProtoArray array' i x
     return Vector { vectorSession  = session,
                     vectorArrayRef = arrayRef',
                     vectorCountRef = countRef',
                     vectorCapacityRef = capacityRef' }
       
vectorEnsureCapacity :: ProtoArrayMonad m a => Vector m a -> Int -> m ()
vectorEnsureCapacity vector capacity =
  do capacity' <- readProtoRef (vectorCapacityRef vector)
     when (capacity' < capacity) $
       do array' <- readProtoRef (vectorArrayRef vector)
          count' <- readProtoRef (vectorCountRef vector)
          let capacity'' = max (2 * capacity') capacity
              session    = vectorSession vector
          array'' <- newProtoArray_ session capacity''
          forM_ [0 .. count'  1] $ \i ->
            do x <- readProtoArray array' i
               writeProtoArray array'' i x
          writeProtoRef (vectorArrayRef vector) array''
          writeProtoRef (vectorCapacityRef vector) capacity''
vectorCount :: ProtoArrayMonad m a => Vector m a -> m Int
vectorCount vector = readProtoRef (vectorCountRef vector)
appendVector :: ProtoArrayMonad m a => Vector m a -> a -> m ()          
appendVector vector item =
  do count <- readProtoRef (vectorCountRef vector)
     vectorEnsureCapacity vector (count + 1)
     array <- readProtoRef (vectorArrayRef vector)
     writeProtoArray array count item
     writeProtoRef (vectorCountRef vector) (count + 1)
readVector :: ProtoArrayMonad m a => Vector m a -> Int -> m a
readVector vector index =
  do array <- readProtoRef (vectorArrayRef vector)
     readProtoArray array index
writeVector :: ProtoArrayMonad m a => Vector m a -> Int -> a -> m ()
writeVector vector index item =
  do array <- readProtoRef (vectorArrayRef vector)
     writeProtoArray array index item
vectorBinarySearch :: (ProtoArrayMonad m a, Ord a) => Vector m a -> a -> m Int
vectorBinarySearch vector item =
  do array <- readProtoRef (vectorArrayRef vector)
     count <- readProtoRef (vectorCountRef vector)
     vectorBinarySearch' array item 0 (count  1)
vectorBinarySearchWithin :: (ProtoArrayMonad m a, Ord a) => Vector m a -> a -> Int -> Int -> m Int
vectorBinarySearchWithin vector item left right =
  do array <- readProtoRef (vectorArrayRef vector)
     vectorBinarySearch' array item left right
freezeVector :: ProtoArrayMonad m a => Vector m a -> m (Array Int a)
freezeVector vector =
  do array <- readProtoRef (vectorArrayRef vector)
     freezeProtoArray array
vectorInsert :: ProtoArrayMonad m a => Vector m a -> Int -> a -> m ()
vectorInsert vector index item =
  do count <- readProtoRef (vectorCountRef vector)
     when (index < 0) $
       error $
       "Index cannot be " ++
       "negative: vectorInsert."
     when (index > count) $
       error $
       "Index cannot be greater " ++
       "than the count: vectorInsert."
     vectorEnsureCapacity vector (count + 1)
     array <- readProtoRef (vectorArrayRef vector)
     forM_ [count, count  1 .. index + 1] $ \i ->
       do x <- readProtoArray array (i  1)
          writeProtoArray array i x
     writeProtoArray array index item
     writeProtoRef (vectorCountRef vector) (count + 1)
vectorDeleteAt :: ProtoArrayMonad m a => Vector m a -> Int -> m ()
vectorDeleteAt vector index =
  do count <- readProtoRef (vectorCountRef vector)
     when (index < 0) $
       error $
       "Index cannot be " ++
       "negative: vectorDeleteAt."
     when (index >= count) $
       error $
       "Index must be less " ++
       "than the count: vectorDeleteAt."
     array <- readProtoRef (vectorArrayRef vector)
     forM_ [index, index + 1 .. count  2] $ \i ->
       do x <- readProtoArray array (i + 1)
          writeProtoArray array i x
     writeProtoArray array (count  1) undefined
     writeProtoRef (vectorCountRef vector) (count  1)
vectorIndex :: (ProtoArrayMonad m a, Eq a) => Vector m a -> a -> m Int
vectorIndex vector item =
  do count <- readProtoRef (vectorCountRef vector)
     array <- readProtoRef (vectorArrayRef vector)
     let loop index =
           if index >= count
           then return $ 1
           else do x <- readProtoArray array index
                   if item == x
                     then return index
                     else loop $ index + 1
     loop 0
vectorBinarySearch' :: (ProtoArrayMonad m a, Ord a) => ProtoArray m a -> a -> Int -> Int -> m Int
vectorBinarySearch' array item left right =
  if left > right 
  then return $  (right + 1)  1
  else
    do let index = (left + right) `div` 2
       curr <- readProtoArray array index
       if item < curr 
         then vectorBinarySearch' array item left (index  1)
         else if item == curr
              then return index
              else vectorBinarySearch' array item (index + 1) right