module Kewar.Layout.Types (Module (..), Position, Grid, fromChar, flipM, sumP, maxP, mkGrid, insertAt, insert, transpose, rows, cols, dimension, moveTo, overlapsWith) where

import Data.Array
import Data.Bifunctor (bimap, first)
import Data.List (groupBy)
import Data.Tuple (swap)

-- | A module represent a single unit composing a QR code. We're not using the word `pixel` as they usually span more than a pixel.
data Module
  = -- | Dark module
    Black
  | -- | Light module
    White
  deriving (Module -> Module -> Bool
(Module -> Module -> Bool)
-> (Module -> Module -> Bool) -> Eq Module
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Module -> Module -> Bool
$c/= :: Module -> Module -> Bool
== :: Module -> Module -> Bool
$c== :: Module -> Module -> Bool
Eq)

fromChar :: Char -> Maybe Module
fromChar :: Char -> Maybe Module
fromChar Char
'0' = Module -> Maybe Module
forall a. a -> Maybe a
Just Module
White
fromChar Char
'1' = Module -> Maybe Module
forall a. a -> Maybe a
Just Module
Black
fromChar Char
_ = Maybe Module
forall a. Maybe a
Nothing

flipM :: Module -> Module
flipM :: Module -> Module
flipM Module
Black = Module
White
flipM Module
White = Module
Black

-- | Position of a module in a given grid. It's in the form (row index, column index).
type Position = (Int, Int)

sumP :: Position -> Position -> Position
sumP :: Position -> Position -> Position
sumP Position
a = (Int -> Int) -> (Int -> Int) -> Position -> Position
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap (Position -> Int
forall a b. (a, b) -> a
fst Position
a Int -> Int -> Int
forall a. Num a => a -> a -> a
+) (Position -> Int
forall a b. (a, b) -> b
snd Position
a Int -> Int -> Int
forall a. Num a => a -> a -> a
+)

maxP :: Position -> Position -> Position
maxP :: Position -> Position -> Position
maxP Position
a = (Int -> Int) -> (Int -> Int) -> Position -> Position
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (Position -> Int
forall a b. (a, b) -> a
fst Position
a)) (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (Position -> Int
forall a b. (a, b) -> b
snd Position
a))

-- | Array holding the drawable representation of the QR code. It includes encoded data, functional patterns, format, and version bits.
type Grid = Array Position Module

type Size = (Position, Position)

mkGrid :: Size -> Grid
mkGrid :: Size -> Grid
mkGrid Size
b = Size -> [Module] -> Grid
forall i e. Ix i => (i, i) -> [e] -> Array i e
listArray Size
b (Module -> [Module]
forall a. a -> [a]
repeat Module
White)

insertAt :: Grid -> Position -> Module -> Grid
insertAt :: Grid -> Position -> Module -> Grid
insertAt Grid
g Position
p Module
m = Grid
g Grid -> [(Position, Module)] -> Grid
forall i e. Ix i => Array i e -> [(i, e)] -> Array i e
// [(Position
p, Module
m)]

insert :: Grid -> [(Position, Module)] -> Grid
insert :: Grid -> [(Position, Module)] -> Grid
insert Grid
g [(Position, Module)]
as = Grid
g Grid -> [(Position, Module)] -> Grid
forall i e. Ix i => Array i e -> [(i, e)] -> Array i e
// [(Position, Module)]
as

transpose :: Grid -> Grid
transpose :: Grid -> Grid
transpose Grid
g = Size -> (Position -> Position) -> Grid -> Grid
forall i j e.
(Ix i, Ix j) =>
(i, i) -> (i -> j) -> Array j e -> Array i e
ixmap (Grid -> Size
forall i e. Array i e -> (i, i)
bounds Grid
g) Position -> Position
forall a b. (a, b) -> (b, a)
swap Grid
g

-- | Returns a list of rows for a given grid. Useful for drawing.
rows :: Grid -> [[(Position, Module)]]
rows :: Grid -> [[(Position, Module)]]
rows Grid
g = ((Position, Module) -> (Position, Module) -> Bool)
-> [(Position, Module)] -> [[(Position, Module)]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupBy (\((Int
a, Int
_), Module
_) ((Int
c, Int
_), Module
_) -> Int
a Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
c) (Grid -> [(Position, Module)]
forall i e. Ix i => Array i e -> [(i, e)]
assocs Grid
g)

-- | Returns a list of columns for a given grid. Useful for drawing.
cols :: Grid -> [[(Position, Module)]]
cols :: Grid -> [[(Position, Module)]]
cols Grid
g = ((Position, Module) -> (Position, Module) -> Bool)
-> [(Position, Module)] -> [[(Position, Module)]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupBy (\((Int
a, Int
_), Module
_) ((Int
c, Int
_), Module
_) -> Int
a Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
c) (Grid -> [(Position, Module)]
forall i e. Ix i => Array i e -> [(i, e)]
assocs (Grid -> [(Position, Module)]) -> Grid -> [(Position, Module)]
forall a b. (a -> b) -> a -> b
$ Grid -> Grid
transpose Grid
g)

dimension :: Grid -> Int
dimension :: Grid -> Int
dimension Grid
g = (Int
r Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Int -> Int -> Int
forall a. Num a => a -> a -> a
* (Int
c Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) where (Int
r, Int
c) = Size -> Position
forall a b. (a, b) -> b
snd (Size -> Position) -> Size -> Position
forall a b. (a -> b) -> a -> b
$ Grid -> Size
forall i e. Array i e -> (i, i)
bounds Grid
g

moveTo :: Position -> [(Position, Module)] -> [(Position, Module)]
moveTo :: Position -> [(Position, Module)] -> [(Position, Module)]
moveTo Position
p = ((Position, Module) -> (Position, Module))
-> [(Position, Module)] -> [(Position, Module)]
forall a b. (a -> b) -> [a] -> [b]
map (((Position, Module) -> (Position, Module))
 -> [(Position, Module)] -> [(Position, Module)])
-> ((Position, Module) -> (Position, Module))
-> [(Position, Module)]
-> [(Position, Module)]
forall a b. (a -> b) -> a -> b
$ (Position -> Position) -> (Position, Module) -> (Position, Module)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (Position -> Position -> Position
sumP Position
p)

overlapsWith :: Position -> (Position, Position) -> Bool
overlapsWith :: Position -> Size -> Bool
overlapsWith Position
p Size
r = Size -> Position -> Bool
forall a. Ix a => (a, a) -> a -> Bool
inRange Size
r Position
p