{-# LANGUAGE LambdaCase #-}
module Opentype.Fileformat.Post where
import Opentype.Fileformat.Types
import Data.Word
import Data.Char
import Data.Binary.Put
import Data.Binary.Get
import Data.Foldable
import Control.Monad (replicateM, replicateM_, when)

data PostVersion =
  -- | The first 258 Glyphs have standard names
  PostTable1 |
  -- | Order of glyph names can be changed, and glyphs can have
  -- non-standard names.
  PostTable2 |
  -- | No glyph names
  PostTable3
  deriving (Eq, Show)

-- |This table contains additional information needed to use TrueType
-- or OpenType™ fonts on PostScript printers. This includes data for
-- the FontInfo dictionary entry and the PostScript names of all the
-- glyphs. For more information about PostScript names, see the Adobe
-- document Unicode and Glyph Names.
--
-- Versions 1.0, 2.0, and 2.5 refer to TrueType fonts and OpenType
-- fonts with TrueType data. OpenType fonts with TrueType data may
-- also use Version 3.0. OpenType fonts with CFF data use Version 3.0
-- only.
data PostTable = PostTable {
  postVersion :: PostVersion,
  -- | Italic angle in counter-clockwise degrees from the
  -- vertical. Zero for upright text, negative for text that leans to
  -- the right (forward).
  italicAngle :: Fixed,
  -- | This is the suggested distance of the top of the underline from
  -- the baseline (negative values indicate below baseline).
  --
  -- The PostScript definition of this FontInfo dictionary key (the y
  -- coordinate of the center of the stroke) is not used for
  -- historical reasons. The value of the PostScript key may be
  -- calculated by subtracting half the underlineThickness from the
  -- value of this field.
  underlinePosition :: FWord,
  -- | suggested values for the underline thickness.
  underlineThickness :: FWord,
  -- | Set to 0 if the font is proportionally spaced, non-zero if the
  -- font is not proportionally spaced (i.e. monospaced).
  isFixedPitch :: Word32,
  -- | Minimum memory usage when an OpenType font is downloaded.  Set
  -- to 0 if unsure.
  minMemType42 :: Word32,
  -- | Maximum memory usage when an OpenType font is downloaded.  Set
  -- to 0 if unsure.
  maxMemType42 :: Word32,
  -- | Minimum memory usage when an OpenType font is downloaded as a
  -- Type 1 font.  Set to 0 if unsure.
  minMemType1 :: Word32,
  -- | Maximum memory usage when an OpenType font is downloaded as a
  -- Type 1 font.  Set to 0 if unsure.
  maxMemType1 :: Word32,
  -- | Ordinal number of the glyph in 'post' string tables.  For
  -- format 2.0 only.
  -- 
  -- If the name index is between 0 and 257, treat the name index as a
  -- glyph index in the Macintosh standard order. If the name index is
  -- between 258 and 65535, then subtract 258 and use that to index
  -- into the list of Pascal strings at the end of the table. Thus a
  -- given font may map some of its glyphs to the standard glyph
  -- names, and some to its own names.
  --
  -- If you do not want to associate a PostScript name with a
  -- particular glyph, use index number 0 which points to the name
  -- .notdef.
  glyphNameIndex :: [Int],
  -- | strings for indices 258 and upwards.
  postStrings :: [String]
  }
  deriving (Show)

getPostTable :: Get PostTable
getPostTable = do
  iVersion <- getWord32be
  version <- case iVersion of
    0x00010000 -> return PostTable1
    0x00020000 -> return PostTable2
    0x00030000 -> return PostTable3
    _ -> fail "Unsupported post table version."
  itA <- getWord32be
  ulPos <- getInt16be
  ulThick <- getInt16be
  isFixed <- getWord32be
  min42 <- getWord32be
  max42 <- getWord32be
  min1 <- getWord32be
  max1 <- getWord32be
  if version /= PostTable2
    then return $ PostTable version itA ulPos
         ulThick isFixed min42 max42 min1 max1 [] []
    else do
      n <- fromIntegral <$> getWord16be
      gIndex <- replicateM n (fromIntegral <$> getWord16be)
      pStrings <- replicateM ((maximum gIndex+1)-258) $ do
        l <- fromIntegral <$> getWord8
        replicateM l ((chr.fromIntegral) <$> getWord8)
      return $ PostTable version itA ulPos
         ulThick isFixed min42 max42 min1 max1 gIndex pStrings

putPostTable :: PostTable -> Put
putPostTable table = do
  putWord32be $ case postVersion table of
    PostTable1 -> 0x00010000
    PostTable2 -> 0x00020000
    PostTable3 -> 0x00030000
  putWord32be $ italicAngle table
  putInt16be $ underlinePosition table
  putInt16be $ underlineThickness table
  putWord32be $ isFixedPitch table
  putWord32be $ minMemType42 table
  putWord32be $ maxMemType42 table
  putWord32be $ minMemType1 table
  putWord32be $ maxMemType1 table
  when (postVersion table == PostTable2) $ do
    let nameIndexLen = length (glyphNameIndex table)
        nameMax = maximum (glyphNameIndex table) + 1
        postLen = length (postStrings table)
    putWord16be $ fromIntegral nameIndexLen
    traverse_ (putWord16be.fromIntegral) $ glyphNameIndex table
    for_ (take (nameMax-258) $ postStrings table) $ \str -> do
      putWord8 $ fromIntegral $ length str
      traverse_ (putWord8.fromIntegral.ord) str
    replicateM_ (nameMax-258-postLen) (putWord8 0)