{-# LANGUAGE BangPatterns #-}

module OidCode (genRawPNG, DPI(..)) where

import Data.Word
import Data.Bits
import Data.Functor
import Control.Monad
import Control.Monad.Writer.Strict
import Codec.Picture
import Codec.Picture.Types
import Control.Monad.ST
import Control.Applicative

-- Image generation

checksum :: Word16 -> Word16
checksum dec = c3
  where
    c1  =       (((dec >> 2) ^ (dec >> 8) ^ (dec >> 12) ^ (dec >> 14)) & 0x01) << 1
    c2 = c1 .|. (((dec) ^ (dec >> 4) ^ (dec >>  6) ^ (dec >> 10)) & 0x01)
    c3 = c2  ^  0x02

    (>>) = shiftR
    (<<) = shiftL
    (^) = xor
    (&) = (.&.)


{-
oidSVG :: Int -> S.Svg
oidSVG code | code >= 4^8 = error $ printf "Code %d too large to draw" code
oidSVG code = S.docTypeSvg ! A.version (S.toValue "1.1")
                           ! A.width (S.toValue "1mm")
                           ! A.height (S.toValue "1mm")
                           ! A.viewbox (S.toValue "0 0 48 48") $ do
    S.defs pattern
    S.rect ! A.width (S.toValue "48") ! A.height (S.toValue "48")
           ! A.fill (S.toValue $ "url(#"++patid++")")
  where
    quart 8 = checksum code
    quart n = (code `div` 4^n) `mod` 4
    patid = "pat-" ++ show code

    pattern = S.pattern ! A.width (S.toValue "48")
                        ! A.height (S.toValue "48")
                        ! A.id_ (S.toValue patid)
                        ! A.patternunits (S.toValue "userSpaceOnUse") $ S.g (f (0,0))
    f = mconcat $ map position $
        zip (flip (,) <$> [3,2,1] <*> [3,2,1])
            [ value (quart n) | n <- [0..8] ] ++
        [ (p, plain) | p <- [(0,0), (1,0), (2,0), (3,0), (0,1), (0,3) ] ] ++
        [ ((0,2), special) ]

    -- pixel = S.rect ! A.width (S.toValue "2") ! A.height (S.toValue "2") ! pos (7,7)
    pixel (x,y) = S.path ! A.d path
      where path = mkPath $ do
            S.m (x+5) (y+5)
            S.hr 2
            S.vr 2
            S.hr (-2)
            S.z

    plain = pixel
    value 0 = at (2,2)   plain
    value 1 = at (-2,2)  plain
    value 2 = at (-2,-2) plain
    value 3 = at (2,-2)  plain
    special = at (3,0)   plain

    position ((n,m), p) = at (n*12, m*12) p

    -- Drawing combinators
    at (x, y) f = f . ((+x) *** (+y))

genSVGs :: String -> IO ()
genSVGs code_str = do
    codes <- parseRange code_str
    forM_ codes $ \c -> do
        let filename = printf "oid%d.svg" c
        printf "Writing %s...\n" filename
        genSVG c filename

genSVG :: Int -> FilePath -> IO ()
genSVG code filename = B.writeFile filename (renderSvg (oidSVG code))
-}

data DPI = D1200 | D600

imageFromBlackPixels :: Int -> Int -> [(Int, Int)] -> Image PixelYA8
imageFromBlackPixels width height pixels = runST $ do 
    i <- createMutableImage width height background
    forM_ pixels $ \(x,y) -> do
        writePixel i x y black
    freezeImage i
  where
    black =      PixelYA8 minBound maxBound
    background = PixelYA8 maxBound minBound

oidImage :: DPI -> Word16 -> Image PixelYA8
oidImage dpi code =
    imageFromBlackPixels
        (width *4*dotsPerPoint)
        (height*4*dotsPerPoint)
        (tile f)
  where
    width = 100 -- in mm
    height = 100 -- in mm
    !dotsPerPoint | D1200 <- dpi = 12
                  |  D600 <- dpi =  6


    quart 8 = checksum code
    quart n = (code `div` 4^n) `mod` 4

    f = mconcat $ map position $
        zip (flip (,) <$> [3,2,1] <*> [3,2,1])
            [ value (quart n) | n <- [0..8] ] ++
        [ (p, plain) | p <- [(0,0), (1,0), (2,0), (3,0), (0,1), (0,3) ] ] ++
        [ ((0,2), special) ]

    plain | D1200 <- dpi = [ (5,5), (5,6), (6,5), (6,6) ]
          | D600 <- dpi  = [ (3,3) ]

    s  | D1200 <- dpi = 2
       | D600  <- dpi = 1
    ss | D1200 <- dpi = 3
       | D600  <- dpi = 2
    value 0 = at ( s, s) plain
    value 1 = at (-s, s) plain
    value 2 = at (-s,-s) plain
    value 3 = at ( s,-s) plain
    special = at (ss,0)  plain

    position ((n,m), p) = at (n*dotsPerPoint, m*dotsPerPoint) p

    -- Drawing combinators

    at (x, y) = map (\(x', y') -> (x + x', y + y'))
    tile f = concat [ at (x*4*dotsPerPoint, y*4*dotsPerPoint) f
                    | x <- [0..width-1], y <- [0..height-1]]



genRawPNG :: DPI -> Word16 -> FilePath -> IO ()
genRawPNG dpi code filename = writePng filename (oidImage dpi code)

