content: {-#LANGUAGE TypeFamilies, TypeSynonymInstances, ParallelListComp,
              FlexibleContexts, FlexibleInstances #-}
  module CV.Iterators
  ( ImageContext(..)
  , F32I
  , filterPixels
  , filterPixelsSlow
  ) where
  
  import Data.Maybe
  import Foreign.Ptr
  import Foreign.ForeignPtr
  import Foreign.Storable
  import CV.Bindings.Types
  import CV.Bindings.Iterators
  import CV.Image
  import System.IO.Unsafe
  
  type F32I = Ptr C'F32_image_iterator
  
  class ImageContext c where
    type V c :: *
    getPos :: c -> (Int,Int)
    getVal :: c -> Maybe (V c)
    evolve :: c -> c
  
  instance ImageContext F32I where
    type V F32I = Float
    getPos c = unsafePerformIO $ do
      (C'CvPoint x y) <- peek p
      return $! (fromIntegral x, fromIntegral y)
      where
        p = c'F32_rowwise_pos c
    getVal c
      | p == nullPtr = Nothing
      | otherwise = unsafePerformIO $ do
          v <- peek p
          return $! Just (realToFrac v)
      where
        p = c'F32_val c
    evolve c = unsafePerformIO $ c'F32_next c
  
  addPixelIf :: (Float -> Bool) -> [((Int,Int),Float)] -> F32I -> [((Int,Int),Float)]
  addPixelIf cond ps c
    | isNothing v = ps
    | cond (fromJust v) = pair `seq` addPixelIf cond (pair : ps) $! evolve c
    | otherwise = addPixelIf cond ps $! evolve c
    where
          pos = getPos c
          pair = pos `seq` (pos, (fromJust v))
          v = getVal c
  
  filterPixels :: (Float -> Bool) -> Image GrayScale D32 -> [((Int,Int),Float)]
  filterPixels cond img =
    unsafePerformIO $
      withImage img $ \cimg -> do
        ptr <- c'alloc_F32_image_iterator
        if ptr == nullPtr
          then
          return []
          else do
            fptr <- newForeignPtr p'free_F32_image_iterator ptr
            withForeignPtr fptr $ \i -> do
              r <- c'F32_create_rowwise_iterator i cimg
              return $! addPixelIf cond [] i
  
  -- slow method using list comprehensions, to verify iterator result...
  filterPixelsSlow :: (Float -> Bool) -> Image GrayScale D32 -> [((Int,Int),Float)]
  filterPixelsSlow cond img = filter (cond . snd) $ concat $ [ [((i,j),(v i j)) | j<-[0..height-1] ] | i <- [0..width-1] ]
    where
      v x y = getPixel (x,y) img
      (width,height) = getSize img
  