-- | Paper and BBox.
module Graphics.PS.Paper where

import Graphics.PS.Unit

-- | Enumeration type for Units.
data Units = MM | PT deriving (Eq,Show)

-- | Paper size data type, the units are specified in the type.
-- The units in postscript are points, we often work in millimeters.
data Paper = Paper {units :: Units
                   ,width :: Int
                   ,height :: Int}
             deriving (Eq,Show)

-- | Translate 'Paper' from mm to points.
--
-- > let a4 = Paper MM 210 297
-- > paper_size_pt a4 == (595,841)
paper_size_pt :: Paper -> (Int,Int)
paper_size_pt (Paper unit w h) =
    case unit of
      MM -> (mm_pt_int w,mm_pt_int h)
      PT -> (w,h)

-- | BoundingBox for an EPSF file with an optional HiResBoundingBox
data BBox = BBox {llx :: Int -- ^ lower left x (x-min)
                 ,lly :: Int -- ^ lower left y (y-min)
                 ,urx :: Int -- ^ upper right x (x-max)
                 ,ury :: Int -- ^ upper right y (y-max)
                 }
          | HRBBox {llx :: Int
                   ,lly :: Int
                   ,urx :: Int
                   ,ury :: Int
                   ,hrllx :: Double -- ^ high resolution 'llx'
                   ,hrlly :: Double -- ^ high resolution 'lly'
                   ,hrurx :: Double -- ^ high resolution 'urx'
                   ,hrury :: Double -- ^ high resolution 'ury'
                   }
            deriving (Eq,Show)

-- | Swap width and height of 'Paper'.
landscape :: Paper -> Paper
landscape (Paper u w h) = Paper u h w

-- | Proportion of 'Paper'.
--
-- > import Graphics.PS.Paper.Names
-- > proportion broadsheet == 1.25
-- > map (round . (* 1e3) . proportion) [a0,b0,c0] == [1414,1414,1414]
-- > map proportion [usLetter,berliner,tabloid]
proportion :: Paper -> Double
proportion (Paper _ w h) = i_to_d h / i_to_d w

-- | The margin given by placing paper size /p/ within /q/.
inset_margin :: Paper -> Paper -> (Units,Int,Int)
inset_margin (Paper u1 w1 h1) (Paper u2 w2 h2) =
    if u1 /= u2
    then error "inset_margin: unequal units"
    else (u2,(w2 - w1) `div` 2,(h2 - h1) `div` 2)

-- * BBox

-- | Sum of 'BBox'.
--
-- > bbox_sum (BBox 5 5 10 10) (BBox 0 0 2 20) == BBox 0 0 10 20
bbox_sum :: BBox -> BBox -> BBox
bbox_sum p q =
    case (p,q) of
      (BBox x0 y0 x1 y1,BBox x2 y2 x3 y3) ->
          BBox (min x0 x2) (min y0 y2) (max x1 x3) (max y1 y3)
      _ -> error "bbox_sum: HR not handled"

-- | Translate 'BBox' from mm to points.
bbox_mm_to_pt :: BBox -> BBox
bbox_mm_to_pt bb =
    case bb of
      BBox x y x' y' ->
          BBox (mm_pt_int x) (mm_pt_int y) (mm_pt_int x') (mm_pt_int y')
      HRBBox x y x' y' hx hy hx' hy' ->
          HRBBox (mm_pt_int x) (mm_pt_int y) (mm_pt_int x') (mm_pt_int y')
                 (mm_pt hx) (mm_pt hy) (mm_pt hx') (mm_pt hy')

{- | A 'div' variant that rounds rather than truncates.

> let f (Paper _ h _) = h `div` 2 == h `divRound` 2
> in all id (map f [b0,b1,b2,b3,b4,b5,b6,b7,b8,b9]) == False

-}
divRound :: Int -> Int -> Int
divRound x y =
    let x' = (fromIntegral x)::Double
        y' = (fromIntegral y)::Double
    in round (x' / y')