#!/usr/bin/python

from PIL import Image

def invert(value):
    return ''.join(["1" if x == "0" else "0" for x in value])


# Load Image
im = Image.open('QR3C_cropped.png')
pix = im.load()

tiles = 21
tileSize = im.size[0] // tiles
version = (tiles - 17) // 4
print("tiles=%d tileSize=%d version=%d" % (tiles, tileSize, version))

# Data Array
data = [[0 for x in range(tiles)] for y in range(tiles)]

# Fill data
# Black -> 111
# White -> 000
# Blue  -> 110 ...
for y in range(tiles):
    for x in range(tiles):
        x_mid = x * tileSize + 0.5 * tileSize
        y_mid = y * tileSize + 0.5 * tileSize
        color = pix[x_mid, y_mid]
        data[x][y] = invert("%d%d%d" %  ( color[0] // 255, color[1] // 255, color[2] // 255 ))

def blackWhiteValue(string):
    new_value = ""
    if len(string) % 3 != 0:
        print("Cannot return black white value of", string)
        return new_value

    for i in range(len(string) // 3):
        new_value += string[i * 3]

    return new_value

mask1 = [data[8][18], data[8][17], data[8][16]]
mask2 = [data[2][8], data[3][8], data[4][8]]
if mask1 != mask2:
    print("masks do not match")
    exit()

mask = [''.join(mask1[i][j] for i in range(3)) for j in range(3)]
print("Mask:", mask)

def apply_mask(x, y, bit, mask):
    doInvert = False
    if mask == "111":
        doInvert = x % 3 == 0
    elif mask == "110":
        doInvert = (x + y) % 3 == 0
    elif mask == "101":
        doInvert = (x + y) % 2 == 0
    elif mask == "100":
        doInvert = y % 2 == 0
    elif mask == "000":
        doInvert = ((x*y) % 2) + ((x + y) % 3) == 0

    return invert(bit) if doInvert else bit

def get_masked_value(x, y):
    return ''.join([apply_mask(x, y, data[x][y][i], mask[i]) for i in range(3)])

def read_block(x, y, orientation):
    indizes = []
    if orientation == "UP":
        indizes = [[0,0], [-1,0], [0,-1], [-1,-1], [0,-2], [-1,-2], [0, -3], [-1, -3]]
    elif orientation == "LEFT_TOP":
        indizes = [[0,0], [-1,0], [0,-1], [-1,-1], [-2,-1], [-3,-1], [-2, 0], [-3, 0]]
    elif orientation == "DOWN":
        indizes = [[0,0], [-1,0], [0,1], [-1,1], [0,2], [-1,2], [0, 3], [-1, 3]]
    elif orientation == "LEFT_BOTTOM":
        indizes = [[0,0], [-1,0], [0,1], [-1,1], [-2,1], [-3,1], [-2, 0], [-3, 0]]

    return (''.join([get_masked_value(x + offset[0], y + offset[1]) for offset in indizes]))

def map_alphanumeric(x):
    if x < 10:
        return chr(ord('0') + x)
    elif x < 36:
        return chr(ord('A') + x - 10)
    else:
        special = { 36: " ", 37: "$", 38: "%", 39: "*", 40: "+", 41: "-", 42: ".", 43: "/", 44: ":" }
        if x in special:
            return special[x]
        else:
            print("unknown special character:", x)
            return "?"

def decode_alphanumeric(x):
    a = x // 45
    b = x % 45
    return map_alphanumeric(a) + map_alphanumeric(b)

encoding = blackWhiteValue(data[20][20] + data[19][20] + data[20][19] + data[19][19])
# encoding = data[20][20] + data[19][20] + data[20][19] + data[19][19]
# encoding = [get_masked_value(20, 20), get_masked_value(19, 20), get_masked_value(20, 19), get_masked_value(19, 19)]
print("Encoding:", encoding)

len_block = read_block(20, 18, "UP")
print("Length:", int(len_block, 2))

# 19 Blocks

blocks = ''.join([
    read_block(20, 14, "UP"),
    read_block(20, 10, "LEFT_TOP"),
    read_block(18, 11, "DOWN"),
    read_block(18, 15, "DOWN"),
    read_block(18, 19, "LEFT_BOTTOM"),
    read_block(16, 18, "UP"),
    read_block(16, 14, "UP"),
    read_block(16, 10, "LEFT_TOP"),
    read_block(14, 11, "DOWN"),
    read_block(14, 15, "DOWN")
])

channels = ["", "", ""]

blockSize = 3
for i in range(len(blocks) // blockSize):
    channels[0] += blocks[i*3]
    channels[1] += blocks[i*3+1]
    channels[2] += blocks[i*3+2]

    # chunk = int(blocks[blockSize*i:blockSize*i+blockSize],2)
    # string += chr(chunk)
    # string += decode_alphanumeric(chunk)

line = ""
for c in channels:
    for i in range(len(c) // 8):
        line += chr(int(c[i*8:i*8+8], 2))
print(line)

# print(len(blocks) /8)
# print(''.join(blocks))


# for x in range(tiles):
#     print(''.join(data[x]))
