#!/usr/bin/env python3
import time


COLUMNS = 7
BLOCK_SIZE = 60

MAX_COLUMN_HEIGHT = 546
DETECT_COLUMN_OFFSET_X = 9, 49
DETECT_COLUMN_OFFSET_Y = 11
MIN_COLUMN_SAT = 130
MIN_COLUMN_VAL = 120

COLUMN_VSHIFT = [1, 1, 1, 0, 0, 0, 0]

RED, PINK, GREEN, BLUE, YELLOW, NOBLOCK = range(6)
BOMB_OFFSET = NOBLOCK + 1
BASIC_HUES = [248, 224, 118, 158, 26]
BOMB_HUES = [250, 219, 132, 174, 38]
HUE_TOLERANCE = 5
DETECT_BASIC_X = 9
DETECT_BASIC_Y = 15
DETECT_BASIC_Y_CHECK = 17
DETECT_BOMB_X = 22
DETECT_BOMB_Y = 43
DETECT_BOMB_Y_TOLERANCE = 7
DETECT_BOMB_CHECK = 8, 20, 137, 185, 9  # x, y, hue, min_value, y_tolerance
MIN_BASIC_SAT = 180
MIN_BOMB_SAT = 130

DETECT_EXA_X = 30
DETECT_EXA_Y = 547, 555
EXA_HUE = 129
EXA_MIN_VAL = 194
DETECT_HELD_Y = 579
DETECT_EXA_LIGHT_X = 30
DETECT_EXA_LIGHT_Y = 609
EXA_LIGHT_HUE = 226
MIN_EXA_LIGHT_VAL = 172


def is_hue(h, hexpect):
    return abs(h - hexpect) <= HUE_TOLERANCE


def is_basic(block):
    return RED <= block <= YELLOW


def is_bomb(block):
    return block > NOBLOCK


def bomb_to_basic(block):
    assert is_bomb(block)
    return block - BOMB_OFFSET


def detect_columns(board):
    def saturated(x, y):
        h, s, v = board.getpixel((x, y))
        return s > MIN_COLUMN_SAT and v > MIN_COLUMN_VAL

    miny = -min(DETECT_BASIC_Y, DETECT_BOMB_Y)

    for y in range(MAX_COLUMN_HEIGHT, miny + BLOCK_SIZE, -1):
        for col in range(COLUMNS):
            x = col * BLOCK_SIZE
            if all(saturated(x + cx, y) for cx in DETECT_COLUMN_OFFSET_X):
                #print('found bottom in column', col)
                return y + 1 + DETECT_COLUMN_OFFSET_Y - COLUMN_VSHIFT[col]


def detect_block(board, x, y):
    # check for basic blocks first, use saturation filter to avoid confusing
    # green blocks with background
    h, s, v = board.getpixel((x + DETECT_BASIC_X,
                              y + DETECT_BASIC_Y))
    if s >= MIN_BASIC_SAT:
        for ty, hexpect in enumerate(BASIC_HUES):
            if is_hue(h, hexpect):
                h, s, v = board.getpixel((x + DETECT_BASIC_X,
                                          y + DETECT_BASIC_Y_CHECK))
                if is_hue(h, hexpect):
                    return ty

    # if no basic block is detected, check another range of pixels for bomb
    # contents
    def check_bomb():
        dx, dy, hexpect, minval, tolerance = DETECT_BOMB_CHECK
        for offset in range(tolerance):
            h, s, v = board.getpixel((x + dx, y + dy + offset))
            if is_hue(h, hexpect) and v >= minval:
                return True
        return False

    if check_bomb():
        for offset in range(DETECT_BOMB_Y_TOLERANCE):
            h, s, v = board.getpixel((x + DETECT_BOMB_X,
                                      y + DETECT_BOMB_Y + offset))
            if s >= MIN_BOMB_SAT:
                for basic_ty, hexpect in enumerate(BOMB_HUES):
                    if is_hue(h, hexpect):
                        return basic_ty + BOMB_OFFSET

    # no basic block or bomb -> empty slot
    return NOBLOCK


def detect_blocks(board):
    maxy = detect_columns(board) - BLOCK_SIZE
    miny = -min(DETECT_BASIC_Y, DETECT_BOMB_Y)
    for y in range(maxy, miny, -BLOCK_SIZE):
        for col in range(COLUMNS):
            x = col * BLOCK_SIZE
            yield detect_block(board, x, y + COLUMN_VSHIFT[col])


def detect_exa(board):
    for col in range(COLUMNS):
        x = col * BLOCK_SIZE + DETECT_EXA_X
        for y in DETECT_EXA_Y:
            h, s, v = board.getpixel((x, y + COLUMN_VSHIFT[col]))
            if is_hue(h, EXA_HUE) and v >= EXA_MIN_VAL:
                return col


def detect_held(board, exa):
    if exa is not None:
        x = exa * BLOCK_SIZE + DETECT_EXA_LIGHT_X
        y = DETECT_EXA_LIGHT_Y + COLUMN_VSHIFT[exa]
        h, s, v = board.getpixel((x, y))
        if not is_hue(h, EXA_LIGHT_HUE) or v < MIN_EXA_LIGHT_VAL:
            return detect_block(board, exa * BLOCK_SIZE, DETECT_HELD_Y)

    return NOBLOCK


def print_board(blocks, exa, held):
    rows = len(blocks) // COLUMNS
    for row in range(rows):
        row = rows - row
        row_blocks = blocks[(row - 1) * COLUMNS:row * COLUMNS]
        print(' ' + ''.join('rpgby.RPGBY'[ty] for ty in row_blocks))

    if exa is not None:
        print(' ' * exa + ' |')
        print(' ' * int(exa > 0), '-' * (exa - 1),
              '(', 'rpgby RPGBY'[held], ')',
              '-' * (COLUMNS - exa - 2), sep='')


if __name__ == '__main__':
    from interaction import get_exapunks_window, screenshot_board

    win = get_exapunks_window()
    win.raise_window()

    while True:
        board = screenshot_board(win)
        blocks = list(detect_blocks(board))
        exa = detect_exa(board)
        held = detect_held(board, exa)
        print('\033c', end='')
        print_board(blocks, exa, held)
        time.sleep(0.05)
