-- | Contour contact sheets.
module Music.Theory.Diagram.Render.Contour.WT where

import Control.Monad {- base -}
import Data.CG.Minus {- hcg-minus -}
import qualified Graphics.Rendering.Cairo as C {- cairo -}
import Render.CG.Minus {- hcg-minus-cairo -}
import System.FilePath {- filepath -}

data Setup = Setup {wt_nc :: Int
                   ,wt_dimensions :: (Int,Int)
                   ,wt_spacers :: (R,R)
                   ,wt_scalar :: R}

type CN = [(Bool,[Int])]
type PP = [CN]

fi :: Integral a => a -> R
fi = fromIntegral

ipt :: Integral a => a -> a -> Pt R
ipt x y = Pt (fi x) (fi y)

-- | Generate grid points.
-- l=left, u=upper, r=rows, c=columns, w=width, h=height
grid :: (R,R) -> (Int,Int) -> (R,R) -> (R,R) -> [Pt R]
grid (l,u) (r,c) (w,h) (dx,dy) =
    let xs = take r [l,l + w + dx ..]
        ys = take c [u,u + h + dy ..]
    in concatMap (\y -> zipWith Pt xs (repeat y)) ys

-- | Calculate number of rows (nr) given number of columns (nc) and
-- number of entries (ne).
calc_nr :: Integral t => t -> t -> t
calc_nr nc ne =
    let (a,b) = ne `divMod` nc
    in a + if b == 0 then 0 else 1

-- | Cairo co-ordinates are /y/ descending.
invert :: (Num a) => a -> [a] -> [a]
invert n = map (\x -> n - x)

draw_contour :: Pt R -> Bool -> [Int] -> C.Render ()
draw_contour (Pt x y) c ys = do
  let (r,g,b) = if c then (1,0,0) else (0,0,0)
      p = zipWith ipt [0..] ys
  C.save
  C.setSourceRGBA r g b 0.75
  C.setLineWidth 0.05
  C.translate x y
  line p
  C.stroke
  C.restore

draw_border :: Pt R -> (Int,Int) -> C.Render ()
draw_border (Pt x y) (w,h) = do
  let g = 0.75
  C.save
  C.setSourceRGBA g g g 0.75
  C.setLineWidth 0.05
  C.translate x y
  C.rectangle 0 0 (fi w) (fi h)
  C.stroke
  C.restore

draw_img :: Pt R -> (Int,Int) -> CN -> C.Render ()
draw_img p (w,h) xs = do
  draw_border p (w,h)
  mapM_ (\(c,ys) -> draw_contour p c (invert h ys)) xs

draw_wt :: Setup -> PP -> C.Render ()
draw_wt (Setup nc (w,h) sp sc) dd = do
  let nr = calc_nr nc (length dd)
      g = grid (1,1) (nc,nr) (fi w,fi h) sp
  C.save
  C.scale sc sc
  zipWithM_ (\d p -> draw_img p (w,h) d) dd g
  C.showPage
  C.restore

-- | Select format from extension (ie. @.pdf@ or @.svg@).
draw :: FilePath -> Setup -> [PP] -> IO ()
draw o_fn wt_f ddd = do
  let (Setup nc (w,h) (dx,dy) sc) = wt_f
      nr = calc_nr nc (maximum (map length ddd))
      w' = fi nc * (fi w + dx) + dx
      h' = fi nr * (fi h + dy) + dy
      f s = C.renderWith s (mapM_ (draw_wt wt_f) ddd)
      wr = case takeExtension o_fn of
             ".pdf" -> C.withPDFSurface
             ".svg" -> C.withSVGSurface
             _ -> undefined
  wr o_fn (w' * sc) (h' * sc) f