module Byg.Functions.Image
  ( Image(..)
  , ImageConversionSettings(..)
  , openImage
  , saveImage
  , convertImage
  ) where

import Byg.Types (IsFunctionIO(..), Token(..))
import Byg.DependencyGenerator (DepGenM, TokenableTo(..), onTupleToken, toTupleToken,
                            runFunctionIO, runFunctionIO_)
import Byg.DependencyRunner (extractSndTokenAsList)

import qualified Codec.Picture as CP
import qualified Codec.Picture.STBIR as CPS


newtype Image = ImageWrapper (CP.Image CP.PixelRGB8)
  deriving (Eq)

instance Show Image where
  show = const "<image>"

data ImageConversionSettings = ResizeToWidth Int
  deriving (Eq, Show)

data OpenImage = OpenImage deriving Show
instance IsFunctionIO OpenImage FilePath Image where
  evalFunctionIO OpenImage s = do
    imageOrig <- CP.readImage s
    case imageOrig of
      Left e -> error ("unexpected error: " ++ e)
      Right image -> pure $ ImageWrapper $ CP.convertRGB8 image
  functionIOReads OpenImage s = [s]
  functionIOWrites OpenImage = const (ListToken [])
  functionIOWritesAny OpenImage = False

openImage :: TokenableTo FilePath a => a -> DepGenM (Token Image)
openImage a = runFunctionIO OpenImage =<< toToken a


data SaveImage = SaveImage deriving Show
instance IsFunctionIO SaveImage (Image, FilePath) () where
  evalFunctionIO SaveImage (ImageWrapper image, s) =
    CP.saveJpgImage 90 s $ CP.ImageRGB8 image
  functionIOReads SaveImage = const []
  functionIOWrites SaveImage = extractSndTokenAsList
  functionIOWritesAny SaveImage = True

saveImage :: (TokenableTo Image a, TokenableTo FilePath b) => a -> b -> DepGenM ()
saveImage a b = runFunctionIO_ SaveImage =<< toTupleToken a b


convertImage :: (TokenableTo Image a, TokenableTo ImageConversionSettings b) => a -> b -> DepGenM (Token Image)
convertImage = onTupleToken $ \(ImageWrapper image) (ResizeToWidth widthResized) ->
  let sizeFactor :: Double
      sizeFactor = fromIntegral (CP.imageWidth image) / fromIntegral widthResized
      heightResized = round (fromIntegral (CP.imageHeight image) / sizeFactor)
  in ImageWrapper $ CPS.resize CPS.defaultOptions widthResized heightResized image
