module CV.ImageOp where

import System.IO.Unsafe (unsafePerformIO)
import CV.Image
import Control.Monad ((>=>))
import Data.Monoid
import Control.Category
import Prelude hiding ((.),id)

-- |ImageOperation is a name for unary operators that mutate images inplace.
newtype ImageOperation c d = ImgOp (Image c d-> IO ())

-- |Compose two image operations
(#>) :: ImageOperation c d-> ImageOperation c d -> ImageOperation c d
(#>) (ImgOp a) (ImgOp b) = ImgOp (\img -> (a img >> b img))

-- |An unit operation for compose
nonOp = ImgOp (\i -> return ())

-- |Apply image operation to a Copy of an image
img <# op = unsafeOperate op img

blitOp img pos = ImgOp $\i -> blit img i pos

setPixelOp pos v = ImgOp $\i -> setPixel pos v i

-- motivating example:
-- >>> hop i = stretchHistogram $ i #- gaussian (5,5)
-- allocates two extra images
-- >>> hop = (gaussian (5,5) &#& id) #> subtract #> stretchHistogram
-- could be implemented in a way that allocates just one extra
fromImageOp (ImgOp f) = IOP $\i -> (f i >> return i)

newtype IOP a b = IOP (a -> IO b)

instance Category IOP where
    id = IOP return
    (IOP f) . (IOP g)  = IOP $ g >=> (f >>= return)

(&#&) :: IOP (Image c d) e -> IOP (Image c d) f -> IOP (Image c d) (Image c d,Image c d)
(IOP f) &#& (IOP g) = IOP $ op
    where
        op i = withCloneValue i $ \cl -> (f i >> g cl >> return (i,cl))

unsafeOperate op img = unsafePerformIO $ operate op img

runIOP (IOP f) img = withCloneValue img $ \clone -> f clone

-- |Apply list of image operations to a Copy of an image. (Makes a single copy and is
-- faster than folding over (<#)
img <## [] = img
img <## op = unsafeOperate (foldl1 (#>) op) img


operate ::ImageOperation c d -> Image c d -> IO (Image c d)
operate (ImgOp op) img = withCloneValue img $ \clone ->
                                    op clone >> return clone

operateOn = flip operate

-- |Iterate an operation N times
times n op = foldl (#>) nonOp (replicate n op)

directOp i (ImgOp op)  = op i
operateInPlace (ImgOp op) img = op img

unsafeOperateOn img op = unsafePerformIO $ operate op img

operateWithROI pos size (ImgOp op) img = withClone img $ \clone ->
                                  withIOROI pos size clone (op clone)