-- GENERATED by C->Haskell Compiler, version 0.18.2 The shapeless maps, 31 Oct 2014 (Haskell)
-- Edit the ORIGNAL .chs file instead!


{-# LINE 1 "./DVD/DVDRead.chs" #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

-- | Example:
--
-- >  do may <- dvdRead "/path/to/dvd/" (do Just s <- fileStat 1 TitleVobs
-- >                                        liftIO (print s)
-- >                                        file 1 TitleVobs (readBlocks 0 100)
-- >                                    )
-- >     case may of
-- >       Just b -> writeFile "tmp.vob" b
-- >       Nothing -> putStrLn "Could not read file"


module DVD.DVDRead
    ( -- * Datatypes
      DVDRead
    , File
    , Status

    , Offset
    , Size
    , Title
    , ReadDomain(..)
--    , DiscID
    , VolumeIdentifier
    , VolumeSetIdentifier

    , dvdRead
    , fileStat
    , getUDFCacheLevel
    , setUDFCacheLevel

    -- * Disc info
--    , discID
    , udfVolumeInfo
    , isoVolumeInfo




    , blockSize


    -- * File operations
    , file
    , size
    , readBlocks
    , readBytes

    -- * Utility read functions
    , readVMGInfoFile
    , readVMGInfoBackupFile
    , readVMGMenu
    , readVTSInfoFile
    , readVTSInfoBackupFile
    , readVTSMenu

    ) where

import Control.Monad.State
import Data.ByteString hiding (map)
import Data.ByteString.Unsafe
import qualified Data.ByteString.Char8 as C
import Foreign
import Foreign.C
import Foreign.C.Types




{-# LINE 70 "./DVD/DVDRead.chs" #-}


newtype DVDReader = DVDReader (ForeignPtr (DVDReader))
withDVDReader :: DVDReader -> (Ptr DVDReader -> IO b) -> IO b
withDVDReader (DVDReader fptr) = withForeignPtr fptr
{-# LINE 72 "./DVD/DVDRead.chs" #-}


cFromEnum :: (Enum e, Integral i) => e -> i
cFromEnum = fromIntegral . fromEnum

toDVDReader :: Ptr DVDReader -> IO DVDReader
toDVDReader ptr = newForeignPtr p_DVDClose ptr >>= return . DVDReader

foreign import ccall "&DVDClose"
   p_DVDClose :: FunPtr (Ptr DVDReader -> IO ())


toMaybeDVDReader :: Ptr DVDReader -> IO (Maybe DVDReader)
toMaybeDVDReader ptr | ptr == nullPtr = return Nothing
                     | otherwise = toDVDReader ptr >>= return . Just 

dvdOpen :: (FilePath) -> IO ((Maybe DVDReader))
dvdOpen a1 =
  withCString a1 $ \a1' -> 
  dvdOpen'_ a1' >>= \res ->
  toMaybeDVDReader res >>= \res' ->
  return (res')

{-# LINE 89 "./DVD/DVDRead.chs" #-}



-- |This is the main data type in this module and represent an action
-- in the underlying C library
newtype DVDRead a = DVDRead (StateT DVDReader IO a)
    deriving (Functor, Monad, MonadIO)

runDVDRead (DVDRead (StateT action)) = action


-- |Runs an action of type @'DVDRead'@ on the specified device/image
-- file.
dvdRead :: FilePath    -- ^A block device, mount point, image file or
                       -- directory containing a copy of a DVD
        -> DVDRead a   -- ^Action to perform on the DVD
        -> IO a        -- ^Resulting action
dvdRead file (DVDRead (StateT action)) =
    do md <- dvdOpen file
       case md of
         Nothing -> ioError $ userError ("Cannot open " ++ file)
         Just dvd -> do (a,DVDReader d) <- action dvd 
                        finalizeForeignPtr d
                        return a

-- |Returns whether or not some UDF operations are cached.
getUDFCacheLevel :: DVDRead Bool
getUDFCacheLevel = DVDRead $ StateT (\s -> do i <- dvdUDFCacheLevel s (-1)
                                              return (toEnum i,s)
                                    )

-- |Sets the cache level for UDF operations.
setUDFCacheLevel :: Bool -> DVDRead ()
setUDFCacheLevel b = DVDRead $ StateT (\s -> do dvdUDFCacheLevel s (fromEnum b)
                                                return ((),s)
                                      )

dvdUDFCacheLevel :: (DVDReader) -> (Int) -> IO ((Int))
dvdUDFCacheLevel a1 a2 =
  withDVDReader a1 $ \a1' -> 
  let {a2' = fromIntegral a2} in 
  dvdUDFCacheLevel'_ a1' a2' >>= \res ->
  let {res' = fromIntegral res} in
  return (res')

{-# LINE 128 "./DVD/DVDRead.chs" #-}



-- |The @'DiscID'@ is the MD5 sum of VIDEO_TS.IFO and the VTS_??_0.IFO
-- files in title order (those that exist).
newtype DiscID = DiscID ByteString

ptr2DiscID :: Ptr a -> IO DiscID
ptr2DiscID p = packCStringLen (castPtr p,16) >>= return . DiscID

-- |Generates the @'DiscID'@ or @'Nothing'@ if an error occurs.
discID :: DVDRead (Maybe DiscID)
discID = DVDRead $ StateT
         (\s ->
              do p <- mallocBytes 16
                 i <- withDVDReader s (\q -> c_DVDDiscID q p)
                 if i == -1
                      then do free p
                              return (Nothing,s)
                      else do id <- ptr2DiscID p
                              free p
                              return (Just id,s)
         )


-- |Specifies a file type inside a 'Title'
data ReadDomain = InfoFile
                | InfoBackupFile
                | MenuVobs
                | TitleVobs
  deriving (Enum,Eq,Read,Show)

{-# LINE 155 "./DVD/DVDRead.chs" #-}


newtype DVDFile = DVDFile (ForeignPtr (DVDFile))
withDVDFile :: DVDFile -> (Ptr DVDFile -> IO b) -> IO b
withDVDFile (DVDFile fptr) = withForeignPtr fptr
{-# LINE 157 "./DVD/DVDRead.chs" #-}


toDVDFile :: Ptr DVDFile -> IO DVDFile
toDVDFile ptr = newForeignPtr p_DVDCloseFile ptr >>= return . DVDFile

toMaybeDVDFile :: Ptr DVDFile -> IO (Maybe DVDFile)
toMaybeDVDFile ptr | ptr == nullPtr = return Nothing
                   | otherwise = toDVDFile ptr >>= return . Just 

foreign import ccall "&DVDCloseFile"
   p_DVDCloseFile :: FunPtr (Ptr DVDFile -> IO ())


-- |The @'File' a@ monad represents a file operation returning the
-- type @a@, to be executed on the content of a DVD.  It can be
-- converted into a @'DVDRead'@ action using @'file'@.
newtype File a = File (StateT DVDFile DVDRead a)
    deriving (Functor, Monad, MonadIO)

-- |The number of the title inside a DVD, between 0 and 99, with 0
-- referring to the Video Manager (VIDEO_TS.*).
type Title = Word8

-- |Applies @'File' a@ to the given 'Title' and 'ReadDomain', and
-- generates the corresponding @'DVDRead'@ action.
file :: Title        -- ^DVD title
     -> ReadDomain   -- ^Domain
     -> File a       -- ^Read operation
     -> DVDRead a    -- ^Reasulting action
file tit dom (File (StateT act)) =
    DVDRead $ StateT
                (\s ->
                     do 
                       mf <- dvdOpenFile s tit dom
                       case mf of
                         Nothing -> do
                                 a <- ioError $
                                      userError ("Cannot open title " ++ (show tit))
                                 return (a,s)
                         Just f -> do
                                 ((a, DVDFile g),r) <- (runDVDRead (act f)) s
                                 finalizeForeignPtr g
                                 return (a,r)
                )

dvdOpenFile :: (DVDReader) -> (Title) -> (ReadDomain) -> IO ((Maybe DVDFile))
dvdOpenFile a1 a2 a3 =
  withDVDReader a1 $ \a1' -> 
  let {a2' = fromIntegral a2} in 
  let {a3' = cFromEnum a3} in 
  dvdOpenFile'_ a1' a2' a3' >>= \res ->
  toMaybeDVDFile res >>= \res' ->
  return (res')

{-# LINE 205 "./DVD/DVDRead.chs" #-}


-- |Size in bytes or blocks of a file corresponding to a given
-- @'Title'@ \/ @'ReadDomain'@. A block equals @'blockSize'@ bytes.
type Size = Word64


-- |Returns the size of a file in blocks.
size :: File Size
size = File $ StateT
           (\f -> liftIO (do s <- withDVDFile f c_DVDFileSize
{-# LINE 215 "./DVD/DVDRead.chs" #-}

                             return (fromIntegral s,f))
           )


-- |The number of bytes or blocks since the beginning of the file
-- (offset 0). A block equals @'blockSize'@ bytes.
type Offset = Word32


-- |@'Status'@ is a list containing the sizes in bytes of each file
-- corresponding to a given @'Title'@ \/ @'ReadDomain'@.
newtype Status = Status [Size]
    deriving (Eq, Read, Show)
      

toStatus p = do n <- (\ptr -> do {peekByteOff ptr 8 ::IO CInt}) p
                q <- (\ptr -> do {peekByteOff ptr 16 ::IO (Ptr CLong)}) p
                l <- peekArray (fromIntegral n*2) ((plusPtr p 12)::(Ptr Word32))
                return $ Status (map fromIntegral (getEven l))
                    where getEven [] = []
                          getEven (x:[]) = []
                          getEven (x:(y:ys)) = y:(getEven ys)

-- |Returns the @'Status'@ of the given @'Title'@ \/ @'ReadDomain'@, or @'Nothing'@ in case of error.
fileStat :: Title -> ReadDomain -> DVDRead (Maybe Status)
fileStat t d = DVDRead $ StateT
               (\s ->
                    do p <- mallocBytes 88
{-# LINE 243 "./DVD/DVDRead.chs" #-}

                       i <- withDVDReader s (\x ->
                                                 c_DVDFileStat x (fromIntegral t) (cFromEnum d) p)
                       if i == (-1)
                         then do free p
                                 return (Nothing,s)
                         else do st <- toStatus p
                                 free p
                                 return (Just st,s)
               )


-- |Number of bytes in a logical block (2048).
blockSize :: Word64
blockSize = 2048

-- |Reads @'Size'@ number of blocks from the file starting at the
-- given block @'Offset'@. Returns @'Nothing'@ in case of error.  This
-- function should be used only for reading VOB data (VIDEO_TS.VOB or
-- VTS_??_?.VOB). When reading from an encrypted drive, blocks are
-- decrypted using libdvdcss where required.
readBlocks :: Offset  -- ^Starting offset in blocks
           -> Size    -- ^Number of blocks to read
           -> File (Maybe ByteString)
readBlocks off n = File $ StateT
                   (\f -> DVDRead $ StateT
                          (\d -> do p <- mallocBytes $ fromEnum (n * blockSize)
                                    s <- withDVDFile f (\x -> c_DVDReadBlocks x (fromIntegral off) (fromIntegral n) p)
                                    if s == (-1)
                                      then do free p
                                              return ((Nothing,f),d)
                                      else do q <- reallocBytes p (fromEnum ((fromIntegral s) * blockSize))
                                              b <- unsafePackCStringFinalizer (castPtr q) (fromEnum ((fromIntegral s) * blockSize)) (free q)
                                              return ((Just b,f),d)
                          )
                   )
                                                  
-- |Volume Identifier as described in ECMA-167.
newtype VolumeIdentifier = VolumeID C.ByteString
    deriving (Eq, Read, Show)

-- |Volume Set Identifier as described in ECMA-167.
newtype VolumeSetIdentifier = VolumeSetID C.ByteString
    deriving (Eq, Read, Show)

-- |Returns Volume Identifier and Volume Set Identifier of the UDF
-- filesystem, or @'Nothing'@ in case of error.
udfVolumeInfo :: DVDRead (Maybe (VolumeIdentifier,VolumeSetIdentifier))
udfVolumeInfo = DVDRead $ StateT
                (\s -> do p <- mallocBytes 33
                          q <- mallocBytes 128
                          i <- withDVDReader s (\x -> c_DVDUDFVolumeInfo x p 33 (castPtr q) 128)
                          if i == (-1)
                            then do free p
                                    free q
                                    return (Nothing,s)
                            else do v <- packCString p
                                    vs <- packCStringLen (q,128)
                                    free p
                                    free q
                                    return (Just (VolumeID v,VolumeSetID vs),s)
                )


-- |Only use this function as fallback if @'udfVolumeInfo'@ returns
-- @'Nothing'@. This will happen on a disc mastered only with a
-- iso9660 filesystem.
isoVolumeInfo :: DVDRead (Maybe (VolumeIdentifier,VolumeSetIdentifier))
isoVolumeInfo = DVDRead $ StateT
                (\s -> do p <- mallocBytes 33
                          q <- mallocBytes 128
                          i <- withDVDReader s (\x -> c_DVDISOVolumeInfo x p 33 (castPtr q) 128)
                          if i == (-1)
                            then do free p
                                    free q
                                    return (Nothing,s)
                            else do v <- packCString p
                                    vs <- packCStringLen (q,128)
                                    free p
                                    free q
                                    return (Just (VolumeID v,VolumeSetID vs),s)
                )


-- |Reads @'Size'@ number of bytes from the file starting at the given
-- bytes @'Offset'@. Returns @'Nothing'@ in case of error.  This
-- function should be used only for reading Info data (VIDEO_TS.IFO,
-- VIDEO_TS.BUP, VTS_??_?.IFO or VTS_??_?.BUP).
readBytes :: Offset  -- ^Starting offset in bytes
          -> Size    -- ^Number of bytes to read
          -> File (Maybe ByteString)
readBytes off n = File $ StateT
                   (\f -> DVDRead $ StateT
                          (\d -> do i <- withDVDFile f (\x -> c_DVDFileSeek x (fromIntegral off))
                                    if i == -1
                                      then return ((Nothing,f),d)
                                      else do
                                        p <- mallocBytes $fromEnum n
                                        s <- withDVDFile f (\x -> c_DVDReadBytes x p (fromIntegral n))
                                        if s == (-1)
                                          then do free p
                                                  return ((Nothing,f),d)
                                          else do b <- packCStringLen (castPtr p, fromIntegral s)
                                                  free p
                                                  return ((Just b,f),d)
                          )
                   )

-- |Returns the content of VIDEO_TS.IFO file.
readVMGInfoFile :: DVDRead ByteString
readVMGInfoFile = readWholeFile 0 InfoFile

-- |Returns the content of VIDEO_TS.BUP file.
readVMGInfoBackupFile :: DVDRead ByteString
readVMGInfoBackupFile = readWholeFile 0 InfoBackupFile

-- |Returns the content of VIDEO_TS.VOB file.
readVMGMenu ::DVDRead ByteString
readVMGMenu = readWholeFile 0 MenuVobs

-- |Returns the content of VTS_??_0.IFO file. Use with @'Title'@ from
-- 1 to 99.
readVTSInfoFile :: Title -> DVDRead ByteString
readVTSInfoFile t = readWholeFile t InfoFile

-- |Returns the content of VTS_??_0.BUP file. Use with @'Title'@ from
-- 1 to 99.
readVTSInfoBackupFile :: Title -> DVDRead ByteString
readVTSInfoBackupFile t = readWholeFile t InfoBackupFile

-- |Returns the content of VTS_??_0.VOB file. Use with @'Title'@ from
-- 1 to 99.
readVTSMenu :: Title -> DVDRead ByteString
readVTSMenu t = readWholeFile t MenuVobs


readWholeFile :: Title -> ReadDomain -> DVDRead ByteString
readWholeFile t d = file t d (do l <- size
                                 if (d == InfoFile) || (d == InfoBackupFile)
                                   then do Just b <- readBytes 0 (l * blockSize)
                                           return b
                                   else do Just b <- readBlocks 0 l
                                           return b
                             )
foreign import ccall safe "DVD/DVDRead.chs.h DVDOpen"
  dvdOpen'_ :: ((Ptr CChar) -> (IO (Ptr (DVDReader))))

foreign import ccall safe "DVD/DVDRead.chs.h DVDUDFCacheLevel"
  dvdUDFCacheLevel'_ :: ((Ptr (DVDReader)) -> (CInt -> (IO CInt)))

foreign import ccall unsafe "DVD/DVDRead.chs.h DVDDiscID"
  c_DVDDiscID :: ((Ptr (DVDReader)) -> ((Ptr CUChar) -> (IO CInt)))

foreign import ccall safe "DVD/DVDRead.chs.h DVDOpenFile"
  dvdOpenFile'_ :: ((Ptr (DVDReader)) -> (CInt -> (CInt -> (IO (Ptr (DVDFile))))))

foreign import ccall unsafe "DVD/DVDRead.chs.h DVDFileSize"
  c_DVDFileSize :: ((Ptr (DVDFile)) -> (IO CLong))

foreign import ccall unsafe "DVD/DVDRead.chs.h DVDFileStat"
  c_DVDFileStat :: ((Ptr (DVDReader)) -> (CInt -> (CInt -> ((Ptr ()) -> (IO CInt)))))

foreign import ccall unsafe "DVD/DVDRead.chs.h DVDReadBlocks"
  c_DVDReadBlocks :: ((Ptr (DVDFile)) -> (CInt -> (CULong -> ((Ptr CUChar) -> (IO CLong)))))

foreign import ccall unsafe "DVD/DVDRead.chs.h DVDUDFVolumeInfo"
  c_DVDUDFVolumeInfo :: ((Ptr (DVDReader)) -> ((Ptr CChar) -> (CUInt -> ((Ptr CUChar) -> (CUInt -> (IO CInt))))))

foreign import ccall unsafe "DVD/DVDRead.chs.h DVDISOVolumeInfo"
  c_DVDISOVolumeInfo :: ((Ptr (DVDReader)) -> ((Ptr CChar) -> (CUInt -> ((Ptr CUChar) -> (CUInt -> (IO CInt))))))

foreign import ccall unsafe "DVD/DVDRead.chs.h DVDFileSeek"
  c_DVDFileSeek :: ((Ptr (DVDFile)) -> (CInt -> (IO CInt)))

foreign import ccall unsafe "DVD/DVDRead.chs.h DVDReadBytes"
  c_DVDReadBytes :: ((Ptr (DVDFile)) -> ((Ptr ()) -> (CULong -> (IO CLong))))