{-# LINE 1 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings, RecordWildCards #-}
{-# LINE 2 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
-----------------------------------------------------------------------------
-- |
-- Module      : Hopfli.Raw
-- License     : Apache 2.0
-- Copyright   : (c) 2014 Anantha Kumaran
-- Stability   : experimental
-- Portability : unknown
--
-----------------------------------------------------------------------------

module Codec.Compression.Hopfli.Raw (
    Options(Options)
  , Format(..)
  , compress
  ) where

import qualified Codec.Compression.GZip     as G
import qualified Codec.Compression.Zlib     as Z
import qualified Codec.Compression.Zlib.Raw as ZR
import qualified Data.ByteString            as B
import           Data.ByteString.Char8      ()
import           Data.ByteString.Internal   (fromForeignPtr, toForeignPtr)
import qualified Data.ByteString.Lazy       as BL
import           Data.ByteString.Lazy.Char8 ()
import           Foreign
import           Foreign.C.Types
import           Prelude                    hiding (length)
import           System.IO.Unsafe           (unsafePerformIO)


{-# LINE 32 "src/Codec/Compression/Hopfli/Raw.hsc" #-}

data Options = Options { verbose            :: CInt
                       , verbose_more       :: CInt
                       , numiterations      :: CInt
                       , blocksplitting     :: CInt
                       , blocksplittinglast :: CInt
                       , blocksplittingmax  :: CInt
                       } deriving (Show)
instance Storable Options where
    sizeOf    _ = ((24))
{-# LINE 42 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
    alignment _ = alignment (undefined :: CInt)
    peek ptr = do
        verbose            <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) ptr
{-# LINE 45 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
        verbose_more       <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) ptr
{-# LINE 46 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
        numiterations      <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) ptr
{-# LINE 47 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
        blocksplitting     <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) ptr
{-# LINE 48 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
        blocksplittinglast <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) ptr
{-# LINE 49 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
        blocksplittingmax  <- ((\hsc_ptr -> peekByteOff hsc_ptr 20)) ptr
{-# LINE 50 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
        pure Options{..}
    poke ptr (Options verbose' verbose_more' numiterations' blocksplitting' blocksplittinglast' blocksplittingmax') = do
        ((\hsc_ptr -> pokeByteOff hsc_ptr 0)) ptr verbose'
{-# LINE 53 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 4)) ptr verbose_more'
{-# LINE 54 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 8)) ptr numiterations'
{-# LINE 55 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 12)) ptr blocksplitting'
{-# LINE 56 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 16)) ptr blocksplittinglast'
{-# LINE 57 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 20)) ptr blocksplittingmax'
{-# LINE 58 "src/Codec/Compression/Hopfli/Raw.hsc" #-}

data Format = GZIP
            | ZLIB
            | DEFLATE
  deriving (Eq, Show)

newtype ZopfliFormat = ZopfliFormat CInt

fromFormat :: Format -> ZopfliFormat
fromFormat GZIP    = ZopfliFormat 0
{-# LINE 68 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
fromFormat ZLIB    = ZopfliFormat 1
{-# LINE 69 "src/Codec/Compression/Hopfli/Raw.hsc" #-}
fromFormat DEFLATE = ZopfliFormat 2
{-# LINE 70 "src/Codec/Compression/Hopfli/Raw.hsc" #-}

foreign import ccall unsafe "zopfli.h ZopfliCompress"
  c_zopfli_compress :: Ptr Options -> ZopfliFormat -> Ptr Word8 -> CSize
                    -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()

compress :: Options -> Format -> B.ByteString -> B.ByteString
-- zopfli doesn't handle zero length data
compress _ GZIP ""            = mconcat . BL.toChunks $ G.compress ""
compress _ ZLIB ""            = mconcat . BL.toChunks $ Z.compress ""
compress _ DEFLATE ""         = mconcat . BL.toChunks $ ZR.compress ""
compress options format input = unsafePerformIO $ do
  let (inputFptr, start, length) = toForeignPtr input
  withForeignPtr inputFptr $ \inputPtr -> do
    alloca $ \outSizePtr -> do
      poke outSizePtr 0
      alloca $ \optionsPtr -> do
        poke optionsPtr options
        alloca $ \outPtrPtr -> do
          poke outPtrPtr nullPtr
          _ <- c_zopfli_compress optionsPtr (fromFormat format) (plusPtr inputPtr start) (fromIntegral length) outPtrPtr outSizePtr
          outSize <- peek outSizePtr
          resultPtr <- throwIfNull "Zopfli compression failed" (peek outPtrPtr)
          resultFptr <- newForeignPtr finalizerFree resultPtr
          pure $ fromForeignPtr resultFptr 0 (fromIntegral outSize)