-- | Unclassified tools

module Pdf.Toolbox.Core.Util
(
  readObjectAtOffset,
  readCompressedObject
)
where

import Data.Int
import qualified Data.Attoparsec.ByteString.Char8 as Parser
import Control.Monad
import qualified System.IO.Streams as Streams

import Pdf.Toolbox.Core.Object.Types
import Pdf.Toolbox.Core.Parsers.Object
import Pdf.Toolbox.Core.Error
import Pdf.Toolbox.Core.IO

-- | Read indirect object at the specified offset
readObjectAtOffset :: MonadIO m
                   => RIS              -- ^ input stream to read from
                   -> Int64            -- ^ object offset
                   -> Int              -- ^ object generation
                   -> PdfE m (Object Int64)
readObjectAtOffset ris off gen = do
  seek ris off
  (Ref _ gen', o) <- inputStream ris >>= parse parseIndirectObject
  unless (gen == gen') $ left $ UnexpectedError $ "Generation mismatch, expected: " ++ show gen ++ ", found: " ++ show gen'
  case o of
    ONumber val -> return $ ONumber val
    OBoolean val -> return $ OBoolean val
    OName val -> return $ OName val
    ODict val -> return $ ODict val
    OArray val -> return $ OArray val
    OStr val -> return $ OStr val
    OStream (Stream dict _) -> (OStream . Stream dict) `liftM` tell ris
    ORef _ -> left $ UnexpectedError "Indirect object can't be ORef"
    ONull -> return ONull

-- | Read object from object stream
readCompressedObject :: MonadIO m
                     => IS         -- ^ input object stream decoded content
                     -> Int64      -- ^ an offset of the first object (\"First\" key in dictionary)
                     -> Int        -- ^ object number to read
                     -> PdfE m (Object ())
readCompressedObject is first num = do
  (is', countConsumed) <- liftIO $ Streams.countInput is
  res <- replicateM (num + 1) $ parse headerP is' :: MonadIO m => PdfE m [(Int, Int64)]
  (_, off) <- tryLast (UnexpectedError $ "readCompressedObject: tryLast: impossible") res
  pos <- liftIO $ countConsumed
  dropExactly (fromIntegral $ first + off - pos) is
  parse parseObject is
  where
  headerP = do
    n <- Parser.decimal
    Parser.skipSpace
    off <- Parser.decimal
    Parser.skipSpace
    return (n, off)