{-# LANGUAGE FlexibleContexts #-}
module Diagrams.QRCode (pathList, pathMatrix, pathArray, stroke) where

import Control.Arrow ((***))
import Data.Array (assocs, Array, Ix)
import Data.Colour.Names (black, white)
import Data.Monoid (mempty)
import qualified Diagrams.Attributes as D
import qualified Diagrams.Core as D
import qualified Diagrams.Path as D
import qualified Diagrams.TwoD as D


-- | Stroke using default QR code colors (black on white) and
-- with the \"quiet\" region.
stroke :: (D.Backend b D.R2, D.Renderable (D.Path D.R2) b) => D.Path D.R2 -> D.Diagram b D.R2
stroke = D.bg white . quiet . D.fc black . D.lw 0 . D.stroke
  where
    zoneX = D.strutX 4
    zoneY = D.strutY 4
    quiet d =
                  zoneY
                  D.===
       (zoneX D.||| d D.||| zoneX)
                  D.===
                  zoneY


-- | Convert a QR code represented as a list of bounded values
-- into a 'Path'.  'minBound' values are considered to be
-- \"off\", while every other value is considered to be \"on\".
pathList :: (Bounded a, Eq a, Integral ix) => [((ix, ix), a)] -> D.Path D.R2
pathList = D.Path . fmap (p2int *** toTrail)
  where p2int = D.p2 . (fromIntegral *** fromIntegral)


-- | Same as 'pathList', but from a matrix represented as a list
-- of lists.
pathMatrix :: (Bounded a, Eq a) => [[a]] -> D.Path D.R2
pathMatrix matrix =
  pathList $ do
    (r, row) <- count matrix
    (c, val) <- count row
    return ((r,c), val)
  where count = zip [(0::Int)..]


-- | Same as 'pathList', but from an array.
pathArray :: (Bounded a, Eq a, Integral ix, Ix ix) => Array (ix, ix) a -> D.Path D.R2
pathArray = pathList . assocs


-- | Convert a value into a 'Trail'.
toTrail :: (Bounded a, Eq a) => a -> D.Trail D.R2
toTrail x = if x == minBound then mempty else D.square 1