module Codec.Crockford (encode, decode, prop_crockfordRoundTrip) where

import Control.Monad
import Data.Char
import Data.Digits (digits, unDigits)
import Data.Maybe
import Test.QuickCheck

{-
0  	0 O o  	0
1 	1 I i L l 	1
2 	2 	2
3 	3 	3
4 	4 	4
5 	5 	5
6 	6 	6
7 	7 	7
8 	8 	8
9 	9 	9
10 	A a 	A
11 	B b 	B
12 	C c 	C
13 	D d 	D
14 	E e 	E
15 	F f 	F
16 	G g 	G
17 	H h 	H
18 	J j 	J
19 	K k 	K
20 	M m 	M
21 	N n 	N
22 	P p 	P
23 	Q q 	Q
24 	R r 	R
25 	S s 	S
26 	T t 	T
27 	V v 	V
28 	W w 	W
29 	X x 	X
30 	Y y 	Y
31 	Z z 	Z
-}

decode :: Integral i => String -> Maybe i
decode s = mapM decodeChar s >>= return . unDigits 32

encode :: Integral i => i -> String
encode = fromJust . mapM encodeChar . digits 32

decodeChar :: Integral i => Char -> Maybe i
decodeChar c = case toUpper c of
    '0' -> Just 0
    'O' -> Just 0
    '1' -> Just 1
    'I' -> Just 1
    'L' -> Just 1
    '2' -> Just 2
    '3' -> Just 3
    '4' -> Just 4
    '5' -> Just 5
    '6' -> Just 6
    '7' -> Just 7
    '8' -> Just 8
    '9' -> Just 9
    'A' -> Just 10
    'B' -> Just 11
    'C' -> Just 12
    'D' -> Just 13
    'E' -> Just 14
    'F' -> Just 15
    'G' -> Just 16
    'H' -> Just 17
    'J' -> Just 18
    'K' -> Just 19
    'M' -> Just 20
    'N' -> Just 21
    'P' -> Just 22
    'Q' -> Just 23
    'R' -> Just 24
    'S' -> Just 25
    'T' -> Just 26
    'V' -> Just 27
    'W' -> Just 28
    'X' -> Just 29
    'Y' -> Just 30
    'Z' -> Just 31
    _ -> Nothing

encodeChar :: Integral i => i -> Maybe Char
encodeChar i = case i of
    0  -> Just '0'
    1  -> Just '1'
    2  -> Just '2'
    3  -> Just '3'
    4  -> Just '4'
    5  -> Just '5'
    6  -> Just  '6'
    7  -> Just '7'
    8  -> Just '8'
    9  -> Just '9'
    10 -> Just 'A'
    11 -> Just 'B'
    12 -> Just 'C'
    13 -> Just 'D'
    14 -> Just 'E'
    15 -> Just 'F'
    16 -> Just 'G'
    17 -> Just 'H'
    18 -> Just 'J'
    19 -> Just 'K'
    20 -> Just 'M'
    21 -> Just 'N'
    22 -> Just 'P'
    23 -> Just 'Q'
    24 -> Just 'R'
    25 -> Just 'S'
    26 -> Just 'T'
    27 -> Just 'V'
    28 -> Just 'W'
    29 -> Just 'X'
    30 -> Just 'Y'
    31 -> Just 'Z'
    _ -> Nothing

prop_crockfordRoundTrip
    :: Integer -- ^ The integer to test.
    -> Property
prop_crockfordRoundTrip i = i > 0 ==> Just i == (decode . encode) i