-- | Reads and writes legacy /portable graymap/ (/PGM/) image files.
-- A PGM file contains one or more grayscale images.
--
-- For more information on the PGM format, see the PGM specification:
-- <http://netpbm.sourceforge.net/doc/pgm.html>.
--
-- This module is intended to be imported @qualified@, to avoid name
-- clashes with "Prelude" and "System.IO" functions; e.g.
--
-- > import qualified Codec.Image.PGM as PGM
module Codec.Image.PGM
    ( readFile
    , writeFile
    , fromString
    , toString
    ) where

import Control.Monad
import Data.Array.IArray
import Data.Bits
import Data.Maybe
import Prelude hiding (readFile, writeFile)
import System.IO hiding (readFile, writeFile)

import Codec.Image.PAM hiding (writeFile, toString)

-- | Writes one or more 'PAM' images to a PGM image file at the given
-- path, converting to grayscale if required.
writeFile :: FilePath -> [PAM] -> IO ()
writeFile filePath l
    = do h <- openFile filePath WriteMode
         hSetBinaryMode h True
         hPutStr h $ unlines $ map toString l
         hClose h
         return ()

-- | Transform a 'PAM' structure to its corresponding 'String'
-- representation in PGM format, converting to grayscale if required.
toString :: PAM -> String
toString (PAM (width, height) depth maxVal tuplType raster)
    = "P5\n"
      ++ show width  ++ " "
      ++ show height ++ "\n"
      ++ show maxVal ++ "\n"
      ++ rasterToString (0, 0)
  where
    rasterToString (x, y)
        | y >= height
            = []
        | x >= width
            = rasterToString (0, y + 1)
        | otherwise
            = let i = maximum $ raster ! (x, y)
              in (if maxVal < 256
                  then [toEnum i]
                  else [toEnum $ i `shift` (-8),
                        toEnum $ i .&. 0xFF])
                     ++ rasterToString (x + 1, y)
