module Graphics.ImageMagick.MagickWand.PixelIterator
  ( pixelIterator
  , pixelRegionIterator
  , pixelGetNextIteratorRow
  , pixelSyncIterator
  , pixelResetIterator
  , pixelIterateList
  , pixelGetMagickColor     -- TODO move to another file
  , pixelSetMagickColor     -- TODO move to another file
--  , clonePixelIterator 
--  , isPixelIterator 
--  , pixelGetCurrentIteratorRow 
--  , pixelGetIteratorRow 
--  , pixelGetNextIteratorRow 
--  , pixelGetPreviousIteratorRow 
--  , pixelSetFirstIteratorRow 
--  , pixelSetIteratorRow 
--  , pixelSetLastIteratorRow 
  ) where

import           Control.Monad
import           Control.Monad.IO.Class
import           Control.Monad.Trans.Resource
import           Data.Vector.Storable                              (Vector)
import qualified Data.Vector.Storable                              as V
import           Foreign                                           hiding (void)
import           Foreign.C.Types                                   (CSize)
import           System.IO.Unsafe                                  (unsafeInterleaveIO)

import qualified Graphics.ImageMagick.MagickWand.FFI.PixelIterator as F
import qualified Graphics.ImageMagick.MagickWand.FFI.PixelWand     as F
import           Graphics.ImageMagick.MagickWand.FFI.Types
import           Graphics.ImageMagick.MagickWand.Types
import           Graphics.ImageMagick.MagickWand.Utils


pixelIterator :: (MonadResource m) => Ptr MagickWand -> m (ReleaseKey, PPixelIterator)
pixelIterator w = allocate (F.newPixelIterator w) destroy
  where destroy = void . F.destroyPixelIterator

pixelRegionIterator :: (MonadResource m)
  => Ptr MagickWand -> Int -> Int -> Int -> Int -> m (ReleaseKey, PPixelIterator)
pixelRegionIterator w x y width height =  allocate (F.newPixelRegionIterator w x' y' width' height') destroy
  where destroy = void . F.destroyPixelIterator
        x' = fromIntegral x
        y' = fromIntegral y
        width' = fromIntegral width
        height' = fromIntegral height

pixelGetNextIteratorRow :: (MonadResource m) => PPixelIterator  -> m (Maybe (Vector PPixelWand))
pixelGetNextIteratorRow p = do
    x <- allocate (createPixelWandVector (F.pixelGetNextIteratorRow p)) (const $ return ())
    case x of
      (_, Just v) -> return (Just v)
      (_, Nothing) -> return Nothing

pixelGetMagickColor :: (MonadIO m) => PPixelWand -> m PMagickPixelPacket
pixelGetMagickColor w = liftIO $ do
          c <- mallocForeignPtr
          withForeignPtr c (F.pixelGetMagickColor w)
          return c

pixelSetMagickColor :: (MonadResource m) => PPixelWand -> PMagickPixelPacket -> m ()
pixelSetMagickColor w c = liftIO $ withForeignPtr c (F.pixelSetMagickColor w)

pixelSyncIterator :: (MonadResource m) => PPixelIterator -> m ()
pixelSyncIterator p =  withException_ p $ F.pixelSyncIterator p

pixelResetIterator :: (MonadResource m) => PPixelIterator -> m ()
pixelResetIterator = liftIO . F.pixelResetIterator

-- | creates lazy list of pixel vectors
pixelIterateList :: (MonadResource m) => PPixelIterator -> m [Vector PPixelWand]
pixelIterateList it = pixelResetIterator it >> liftIO go
  where
    go :: IO [Vector PPixelWand]
    go = unsafeInterleaveIO $ do
          mv <- createPixelWandVector (F.pixelGetNextIteratorRow it)
          case mv of
            Just v -> go >>= return . (:) v
            Nothing -> return []


createPixelWandVector :: (Ptr CSize -> IO (Ptr PPixelWand)) -> IO (Maybe (Vector (PPixelWand)))
createPixelWandVector f = alloca $ \x -> do
          ptr <- f x
          if ptr == nullPtr
              then return Nothing
              else do
                  n   <- fmap fromIntegral (peek x)
                  fmap (Just . (\p -> V.unsafeFromForeignPtr0 p n)) (newForeignPtr_ ptr)