{-# LINE 1 "src/Data/QRCode.hsc" #-}
{-# LANGUAGE CPP, ForeignFunctionInterface #-}
{-# LINE 2 "src/Data/QRCode.hsc" #-}

-- | Haskell bindings for libqrencode. <http://fukuchi.org/works/qrencode/index.en.html>
--
--   Libqrencode is a C library for encoding data in a QR Code symbol, a kind of 2D symbology
--   that can be scanned by handy terminals such as a mobile phone with CCD. The capacity of
--   QR Code is up to 7000 digits or 4000 characters, and is highly robust.

module Data.QRCode (encodeByteString,
                    encodeString,
                    getQRCodeVersion,
                    getQRCodeWidth,
                    getQRCodeString,
                    toMatrix,
                    QREncodeLevel (..),
                    QREncodeMode (..)) where

import Control.Monad
import Data.ByteString (ByteString, unpack, useAsCString, packCStringLen)
import qualified Data.ByteString as BS
import Data.Maybe
import Foreign
import Foreign.C.Error
import Foreign.C.String
import Foreign.C.Types
import Foreign.Storable


{-# LINE 29 "src/Data/QRCode.hsc" #-}

data QREncodeLevel = QR_ECLEVEL_L
                   | QR_ECLEVEL_M
                   | QR_ECLEVEL_Q
                   | QR_ECLEVEL_H

data QREncodeMode  = QR_MODE_NUM        -- ^ Numeric mode
                   | QR_MODE_AN         -- ^ Alphabet-numeric mode
                   | QR_MODE_EIGHT      -- ^ 8-bit data mode
                   | QR_MODE_KANJI      -- ^ Kanji (shift-jis) mode

convertQREncodeLevel :: QREncodeLevel -> CInt
convertQREncodeLevel QR_ECLEVEL_L = 0
{-# LINE 42 "src/Data/QRCode.hsc" #-}
convertQREncodeLevel QR_ECLEVEL_M = 1
{-# LINE 43 "src/Data/QRCode.hsc" #-}
convertQREncodeLevel QR_ECLEVEL_Q = 2
{-# LINE 44 "src/Data/QRCode.hsc" #-}
convertQREncodeLevel QR_ECLEVEL_H = 3
{-# LINE 45 "src/Data/QRCode.hsc" #-}

convertQREncodeMode :: QREncodeMode -> CInt
convertQREncodeMode QR_MODE_NUM       = 0
{-# LINE 48 "src/Data/QRCode.hsc" #-}
convertQREncodeMode QR_MODE_AN        = 1
{-# LINE 49 "src/Data/QRCode.hsc" #-}
convertQREncodeMode QR_MODE_EIGHT     = 2
{-# LINE 50 "src/Data/QRCode.hsc" #-}
convertQREncodeMode QR_MODE_KANJI     = 3
{-# LINE 51 "src/Data/QRCode.hsc" #-}

data QRcode = QRcode {
      getQRCodeVersion :: Int,
      getQRCodeWidth   :: Int,
      getQRCodeString  :: ByteString
    } deriving (Show, Read)

data QRcodeStruct = QRcodeStruct {
      c_version :: CInt,
      c_width   :: CInt,
      c_data    :: CString
    } deriving (Show)


{-# LINE 65 "src/Data/QRCode.hsc" #-}

instance Storable QRcodeStruct where

    alignment _ = 4
{-# LINE 69 "src/Data/QRCode.hsc" #-}

    sizeOf _ = (12)
{-# LINE 71 "src/Data/QRCode.hsc" #-}

    peek ptr = do
      version <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
{-# LINE 74 "src/Data/QRCode.hsc" #-}
      width   <- (\hsc_ptr -> peekByteOff hsc_ptr 4) ptr
{-# LINE 75 "src/Data/QRCode.hsc" #-}
      data'   <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
{-# LINE 76 "src/Data/QRCode.hsc" #-}
      return $ QRcodeStruct version width data'

    poke ptr (QRcodeStruct version width data') = do
      (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr version
{-# LINE 80 "src/Data/QRCode.hsc" #-}
      (\hsc_ptr -> pokeByteOff hsc_ptr 4) ptr width
{-# LINE 81 "src/Data/QRCode.hsc" #-}
      (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr data'
{-# LINE 82 "src/Data/QRCode.hsc" #-}


foreign import ccall safe "QRcode_encodeString"
    c_encodeString :: CString -- string
                   -> CInt    -- version
                   -> CInt    -- level
                   -> CInt    -- hint
                   -> CInt    -- casesensitive
                   -> IO (Ptr QRcodeStruct)

foreign import ccall unsafe "QRcode_free"
    c_free :: Ptr QRcodeStruct
           -> IO ()

-- | create a QR code from a ByteString
encodeByteString :: ByteString    -- ^ String to encode
                 -> Maybe Int     -- ^ Version (auto if Nothing)
                 -> QREncodeLevel -- ^ Encode Level
                 -> QREncodeMode  -- ^ Encode Mode
                 -> Bool          -- ^ Case-sensative
                 -> IO QRcode
encodeByteString str version level mode casesensitive = do
    when (BS.null str) $ error "empty bytestring provided"
    useAsCString str $ \s-> encoder s version level mode casesensitive

-- | create a QR code from a String
encodeString :: String        -- ^ String to encode
             -> Maybe Int     -- ^ Version (auto if Nothing)
             -> QREncodeLevel -- ^ Encode Level
             -> QREncodeMode  -- ^ Encode Mode
             -> Bool          -- ^ Case-sensative
             -> IO QRcode
encodeString str version  level mode casesensitive = do
    when (null str) $ error "empty string provided"
    newCAString str >>= \s-> encoder s version level mode casesensitive

encoder :: CString -> Maybe Int -> QREncodeLevel -> QREncodeMode -> Bool -> IO QRcode
encoder cstr ver level mode casesensitive = do
  let l = convertQREncodeLevel level
  let m = convertQREncodeMode mode
  c_qrptr <- throwErrnoIfNull "haskell-qrencode/QRcode_encodeString" $
             c_encodeString cstr (fromIntegral $ fromMaybe 0 ver) l m (b2i casesensitive)
  c_qr <- peek c_qrptr
  let version = fromIntegral (c_version c_qr)
  let width   = fromIntegral (c_width   c_qr)
  str <- packCStringLen (c_data c_qr, width * width)
  c_free c_qrptr
  return (QRcode version width str)
  where
    b2i True  = 1
    b2i False = 0

-- | Convert a QRcode to a matrix of ones and zeros (1 = On, 0 = Off)
toMatrix :: QRcode -> [[Word8]]
toMatrix (QRcode _ width str) =
    regroup . map tobin . unpack $ str
    where
      tobin c = c .&. 1
      regroup [] = []
      regroup xs = let ~(this, rest) = splitAt width xs
                   in this : regroup rest