from gint import *
import sys

BORDER = 5
TILE_SIZE = 16
N_TILE_WIDTH = DWIDTH // TILE_SIZE
N_TILE_HEIGHT = DHEIGHT // TILE_SIZE

GRASS_TIMER = 3

DIR_N = 0
DIR_W = 1
DIR_S = 2
DIR_E = 3
DIR_EOG = 4

GATE_OR = 0
GATE_AND = 1
GATE_XOR = 2

CONTROL_VALVE = 0
CONTROL_SEL2 = 1
CONTROL_SEL3 = 2

TILE_ROCK = 0
TILE_SOURCE = 1
TILE_HOLE = 2
TILE_EOL_N = 3
TILE_EOL_W = 4
TILE_EOL_S = 5
TILE_EOL_E = 6
TILE_GATE_OR = 7
TILE_GATE_AND = 8
TILE_GATE_XOR = 9
TILE_CONTROL_VALVE = 10
TILE_CONTROL_SEL2 = 11
TILE_CONTROL_SEL3 = 12
TILE_EOG = 13

STATE_ROCK = 0
STATE_SOURCE = 1
STATE_HOLE = 2
STATE_GATE = 3
STATE_CONTROL = 4

INPUT_KEYS = [KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9]

LEVELS = [
b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\n\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00',
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\x02\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\x02\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\x0c\x02\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\x02\x08\x02\x02\x02\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\x02\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x02\x0c\x02\x02\x02\x02\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\n\x02\x02\x00\x02\x02\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x02\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x02\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\t\x02\x02\x02\x02\x02\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\n\x02\x08\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\x02\x02\x02\x0c\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x07\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
]

LEVEL_NUMBER = 0
LEVEL_PASSWORD = ""
SIM_STATE = None

def draw_rock_tile(x, y):
    drect_border(BORDER + x*TILE_SIZE, y*TILE_SIZE,
                 BORDER + (x+1)*TILE_SIZE - 1, (y+1)*TILE_SIZE - 1,
                 #C_RGB(0xB0, 0xB0, 0xB0),
                 C_RGB(0xC0, 0xC0, 0xC0),
                 2, C_RGB(0xA8, 0xA8, 0xA8))

    # TODO : ahhhh, it slows down everything, i need to stop abusing dpixel,
    #        and rocks are ugly

    #tex = 0x3806f0c0f8000800c804de4707001000
    #base_x = BORDER + x*TILE_SIZE + 2
    #base_y = y*TILE_SIZE + 2
    #color = C_RGB(0x8B, 0x8B, 0x8B)
    #for ny in range(11, -1, -1):
    #    for nx in range(12):
    #        if tex & 1:
    #            dpixel(base_x + nx, base_y + ny, color)
    #        tex = tex >> 1

def draw_grass_tile(x, y):
    drect_border(BORDER + x*TILE_SIZE, y*TILE_SIZE,
                 BORDER + (x+1)*TILE_SIZE - 1, (y+1)*TILE_SIZE - 1,
                 C_RGB(0x40, 0xF0, 0x40),
                 2, C_RGB(0x20, 0xA8, 0x20))

def draw_water_tile(x, y):
    drect_border(BORDER + x*TILE_SIZE, y*TILE_SIZE,
                 BORDER + (x+1)*TILE_SIZE - 1, (y+1)*TILE_SIZE - 1,
                 C_RGB(0x40, 0x40, 0xF0),
                 2, C_RGB(0x20, 0x20, 0xA8))

def draw_hole_tile(x, y):
    drect_border(BORDER + x*TILE_SIZE, y*TILE_SIZE,
                 BORDER + (x+1)*TILE_SIZE - 1, (y+1)*TILE_SIZE - 1,
                 C_RGB(0x10, 0x10, 0x10),
                 2, C_RGB(0x08, 0x08, 0x08))

# TODO : use better drawing routine instead of putting tons of const on the
# heap and dpixel-ing in a loop
def draw_gate_tile(x, y, gate, a, b, output):
    drect_border(BORDER + x*TILE_SIZE, y*TILE_SIZE,
                 BORDER + (x+1)*TILE_SIZE - 1, (y+1)*TILE_SIZE - 1,
                 C_RGB(0xF0, 0xF0, 0x40),
                 2, C_RGB(0xA8, 0xA8, 0x20))

    tex_gate = {
        GATE_OR: 0x108198168108108090060000000,
        GATE_AND: 0x1f8108108108108090060000000,
        GATE_XOR: 0x108090168198168108108090060000000,
    }[gate]
    tex_fluids = 0xaa8155008100008100008100000000000000000000000000000000000000003c00003c00
    base_x = BORDER + x*TILE_SIZE + 2
    base_y = y*TILE_SIZE + 2
    for ny in range(11, -1, -1):
        for nx in range(12):
            gate_p = tex_gate & 1
            fluids_p = tex_fluids & 3

            if gate_p:
                dpixel(base_x + nx, base_y + ny, C_WHITE)
            elif fluids_p:
                dpixel(base_x + nx, base_y + ny, {
                    0b01: C_BLUE if a else C_BLACK,
                    0b10: C_BLUE if b else C_BLACK,
                    0b11: C_BLUE if output else C_BLACK,
                }[fluids_p])

            tex_gate = tex_gate >> 1
            tex_fluids = tex_fluids >> 2

def draw_control_tile(x, y, control, hint, state, output):
    drect_border(BORDER + x*TILE_SIZE, y*TILE_SIZE,
                 BORDER + (x+1)*TILE_SIZE - 1, (y+1)*TILE_SIZE - 1,
                 C_RGB(0xFF, 0x40, 0xFF),
                 2, C_RGB(0xF0, 0x40, 0xF0))

    tex = {
        CONTROL_VALVE: 0xf03fc7fe7feffffffffffff7fe7fe3fc0f0,
        CONTROL_SEL2: 0x801402204108090060060060060060060060,
        CONTROL_SEL3: 0x60060060060060ffffff060060060060060,
    }[control]
    base_x = BORDER + x*TILE_SIZE + 2
    base_y = y*TILE_SIZE + 2
    for ny in range(12):
        for nx in range(12):
            color = C_RED if (control == CONTROL_VALVE and state > 0) or \
                             (control == CONTROL_SEL2 and ny <= 6) or \
                             (control == CONTROL_SEL2 and nx <= 5 and state == 0) or \
                             (control == CONTROL_SEL2 and nx > 5 and state == 1) or \
                             (control == CONTROL_SEL3 and ny <= 4) or \
                             (control == CONTROL_SEL3 and (ny == 5 or ny == 6) and nx <= 6 and state == 0) or \
                             (control == CONTROL_SEL3 and (nx == 5 or nx == 6) and state == 1) or \
                             (control == CONTROL_SEL3 and (ny == 5 or ny == 6) and nx >= 5 and state == 2) \
                         else C_BLACK
            if tex & 1:
                dpixel(base_x + nx, base_y + ny, color)
            # TODO : fluid tex
            tex = tex >> 1

    dtext(base_x + 5, base_y + 3, C_WHITE, str(hint))

def draw_eol_tile(x, y, direction):
    tex = 0x60d18b060301608c41a03 if direction & 1 else 0x30120482108440902807ff
    base_x = BORDER + x*TILE_SIZE + 3
    base_y = y*TILE_SIZE + 3
    iter_x = [
        range(10),
        range(8, -1, -1),
        range(10),
        range(9)
    ][direction]
    iter_y = [
        range(8, -1, -1),
        range(10),
        range(9),
        range(10)
    ][direction]
    for ny in iter_y:
        for nx in iter_x:
            if tex & 1:
                dpixel(base_x + nx, base_y + ny, C_WHITE)
            tex = tex >> 1

def draw_eog_tile(x, y, state):
    tex = 0x600600600600607fe0603fc0601f80600f0
    base_x = BORDER + x*TILE_SIZE + 2
    base_y = y*TILE_SIZE + 2
    color = C_GREEN if state else C_WHITE
    for ny in range(12):
        for nx in range(12):
            if tex & 1:
                dpixel(base_x + nx, base_y + ny, color)
            tex = tex >> 1

def draw_game():
    dclear(C_BLACK)
    draw_EXE_text = False

    for y in range(N_TILE_HEIGHT):
        for x in range(N_TILE_WIDTH):
            state_tuple = SIM_STATE[y][x]
            if state_tuple[0] == STATE_ROCK and state_tuple[1] < GRASS_TIMER:
                draw_rock_tile(x, y)
            elif state_tuple[0] == STATE_ROCK and state_tuple[1] >= GRASS_TIMER:
                draw_grass_tile(x, y)
            elif state_tuple[0] == STATE_SOURCE or (state_tuple[0] == STATE_HOLE and state_tuple[2]):
                draw_water_tile(x, y)
                if state_tuple[0] == STATE_HOLE and state_tuple[1] >= 0:
                    draw_eol_tile(x, y, state_tuple[1]) if state_tuple[1] != DIR_EOG else draw_eog_tile(x, y, state_tuple[2])
                    draw_EXE_text = True
            elif state_tuple[0] == STATE_HOLE and not state_tuple[2]:
                draw_hole_tile(x, y)
                if state_tuple[0] == STATE_HOLE and state_tuple[1] >= 0:
                    draw_eol_tile(x, y, state_tuple[1]) if state_tuple[1] != DIR_EOG else draw_eog_tile(x, y, state_tuple[2])
            elif state_tuple[0] == STATE_GATE:
                draw_gate_tile(x, y, state_tuple[1], state_tuple[2], state_tuple[3], state_tuple[4])
            elif state_tuple[0] == STATE_CONTROL:
                draw_control_tile(x, y, state_tuple[1], state_tuple[2], state_tuple[3], state_tuple[4])

    dtext(2, 2, C_WHITE, f"Level: {LEVEL_NUMBER}")
    dtext(DWIDTH-125, 2, C_WHITE, f"Password: {LEVEL_PASSWORD}")

    if draw_EXE_text:
        dtext(DWIDTH-50, DHEIGHT-12, C_WHITE, "[EXE]")

    dupdate()

def get_password(number):
    x = 1337
    for _ in range(number):
        x = 75*x % 0x10001
    return x % 9999

def load_level(number):
    global LEVEL_NUMBER, LEVEL_PASSWORD, SIM_STATE

    if number > len(LEVELS):
        # TODO : end cinematic
        print("\n\nThanks for playing!")
        sys.exit(0)

    # We empty the event list because for some reason some inputs from the
    # title screen bubles up
    while True:
        ev = pollevent()
        if ev.type == KEYEV_NONE:
            break

    LEVEL_NUMBER = number
    LEVEL_PASSWORD = f"{get_password(number):04}"

    assert len(LEVELS[number-1]) == N_TILE_HEIGHT * N_TILE_WIDTH

    SIM_STATE = []
    control_hint = 1
    for y in range(N_TILE_HEIGHT):
        SIM_STATE.append([])
        for x in range(N_TILE_WIDTH):
            level_byte = LEVELS[number-1][y*N_TILE_WIDTH + x]

            state_tuple = None
            if level_byte == TILE_ROCK:
                state_tuple = (STATE_ROCK, 0) # grass timer
            elif level_byte == TILE_SOURCE:
                state_tuple = (STATE_SOURCE,)
            elif level_byte == TILE_HOLE:
                state_tuple = (STATE_HOLE, -1, False, -1, -1, 0) # EOL, water state, source x, source y, dry timer
            elif level_byte >= TILE_EOL_N and level_byte <= TILE_EOL_E:
                state_tuple = (STATE_HOLE, level_byte - TILE_EOL_N, False, -1, -1, 0) # EOL, water state, source x, source y, dry timer
            elif level_byte >= TILE_GATE_OR and level_byte <= TILE_GATE_XOR:
                state_tuple = (STATE_GATE, level_byte - TILE_GATE_OR, False, False, False) # A, B, output
            elif level_byte >= TILE_CONTROL_VALVE and level_byte <= TILE_CONTROL_SEL3:
                state_tuple = (STATE_CONTROL, level_byte - TILE_CONTROL_VALVE, control_hint, 0, False) # control hint, control state, output state
                control_hint += 1
            elif level_byte == TILE_EOG:
                state_tuple = (STATE_HOLE, DIR_EOG, False, -1, -1, 0) # EOG, water state, source x, source y, dry timer

            SIM_STATE[-1].append(state_tuple)

def simulate():
    global SIM_STATE
    new_state = []
    neighbours = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]

    def is_wet(x, y, direction=None):
        if y >= N_TILE_HEIGHT or x >= N_TILE_WIDTH:
            return False
        state_tuple = SIM_STATE[y][x]
        return ((direction is None or 0 in direction) and state_tuple[0] == STATE_SOURCE) or \
               ((direction is None or 0 in direction) and state_tuple[0] == STATE_HOLE and state_tuple[2]) or \
               ((direction is None or 0 in direction) and state_tuple[0] == STATE_CONTROL and state_tuple[1] == CONTROL_VALVE and state_tuple[4]) or \
               ((direction is None or direction == (1, -1)) and state_tuple[0] == STATE_CONTROL and state_tuple[1] == CONTROL_SEL2 and state_tuple[3] == 0 and state_tuple[4]) or \
               ((direction is None or direction == (-1, -1)) and state_tuple[0] == STATE_CONTROL and state_tuple[1] == CONTROL_SEL2 and state_tuple[3] == 1 and state_tuple[4]) or \
               ((direction is None or direction == (1, 0)) and state_tuple[0] == STATE_CONTROL and state_tuple[1] == CONTROL_SEL3 and state_tuple[3] == 0 and state_tuple[4]) or \
               ((direction is None or direction == (0, -1)) and state_tuple[0] == STATE_CONTROL and state_tuple[1] == CONTROL_SEL3 and state_tuple[3] == 1 and state_tuple[4]) or \
               ((direction is None or direction == (-1, 0)) and state_tuple[0] == STATE_CONTROL and state_tuple[1] == CONTROL_SEL3 and state_tuple[3] == 2 and state_tuple[4]) or \
               ((direction is None or direction == (0, -1)) and state_tuple[0] == STATE_GATE and state_tuple[4])

    def get_grass(x, y):
        if y >= N_TILE_HEIGHT or x >= N_TILE_WIDTH or y < 0 or x < 0:
            return 0
        if is_wet(x, y):
            return GRASS_TIMER+2
        state_tuple = SIM_STATE[y][x]
        if state_tuple[0] == STATE_ROCK and state_tuple[1] > 0:
            return state_tuple[1] - 1
        return 0

    for y in range(N_TILE_HEIGHT):
        new_state.append([])
        for x in range(N_TILE_WIDTH):
            state_tuple = SIM_STATE[y][x]
            if state_tuple[0] == STATE_ROCK:
                max_grass = 0
                for nx, ny in neighbours:
                    max_grass = max(get_grass(x + nx, y + ny), max_grass)

                if max_grass:
                    state_tuple = (STATE_ROCK, min(state_tuple[1] + 1, max_grass))
                else:
                    state_tuple = (STATE_ROCK, max(state_tuple[1] - 1, 0))
            elif state_tuple[0] == STATE_SOURCE:
                # nop
                pass
            elif state_tuple[0] == STATE_HOLE and not state_tuple[2] and state_tuple[5] <= 0:
                nstate = None
                for nx, ny in neighbours:
                    if is_wet(x + nx, y + ny, (nx, ny)):
                        nstate = (x + nx, y + ny)
                        break
                if nstate is not None:
                    state_tuple = (STATE_HOLE, state_tuple[1], True, nstate[0], nstate[1], 0)
            elif state_tuple[0] == STATE_HOLE and state_tuple[2] and state_tuple[5] <= 0:
                if not is_wet(state_tuple[3], state_tuple[4], (state_tuple[3] - x, state_tuple[4] - y)):
                    state_tuple = (STATE_HOLE, state_tuple[1], False, -1, -1, 4)
            elif state_tuple[0] == STATE_HOLE and state_tuple[5] > 0:
                state_tuple = (STATE_HOLE, state_tuple[1], False, -1, -1, state_tuple[5] - 1)
            elif state_tuple[0] == STATE_GATE:
                a = is_wet(x - 1, y)
                b = is_wet(x + 1, y)
                output = a or b if state_tuple[1] == GATE_OR else \
                         a and b if state_tuple[1] == GATE_AND else \
                         a ^ b if state_tuple[1] == GATE_XOR else False
                state_tuple = (STATE_GATE, state_tuple[1], a, b, output)
            elif state_tuple[0] == STATE_CONTROL and state_tuple[1] == CONTROL_VALVE:
                output = False
                if state_tuple[3]:
                    for nx, ny in neighbours:
                        if is_wet(x + nx, y + ny):
                            output = True
                            break
                state_tuple = (STATE_CONTROL, CONTROL_VALVE, state_tuple[2], state_tuple[3], output)
            elif state_tuple[0] == STATE_CONTROL and (state_tuple[1] == CONTROL_SEL2 or state_tuple[1] == CONTROL_SEL3):
                state_tuple = (STATE_CONTROL, state_tuple[1], state_tuple[2], state_tuple[3], is_wet(x, y - 1))

            new_state[-1].append(state_tuple)
    SIM_STATE = new_state

def level_solved():
    global SIM_STATE
    for y in range(N_TILE_HEIGHT):
        for x in range(N_TILE_WIDTH):
            state_tuple = SIM_STATE[y][x]
            if state_tuple[0] == STATE_HOLE and state_tuple[1] >= 0 and state_tuple[2]:
                return True
    return False

def handle_inputs():
    global SIM_STATE, LEVEL_NUMBER

    while True:
        ev = pollevent()
        if ev.type == KEYEV_NONE:
            return
        if ev.type == KEYEV_UP and ev.key == KEY_EXE and level_solved():
            # TODO level transitions, maybe, if more than 1FPS with my stupid dpixel abuse :')
            load_level(LEVEL_NUMBER + 1)
        if ev.type != KEYEV_UP or ev.key not in INPUT_KEYS:
            continue

        toggled_hint = INPUT_KEYS.index(ev.key) + 1
        for y in range(N_TILE_HEIGHT):
            for x in range(N_TILE_WIDTH):
                state_tuple = SIM_STATE[y][x]
                if state_tuple[0] == STATE_CONTROL and state_tuple[2] == toggled_hint:
                    mod = {
                        CONTROL_VALVE: 2,
                        CONTROL_SEL2: 2,
                        CONTROL_SEL3: 3,
                    }[state_tuple[1]]
                    state_tuple = (STATE_CONTROL, state_tuple[1], state_tuple[2], (state_tuple[3] + 1) % mod, state_tuple[4])
                    SIM_STATE[y][x] = state_tuple
                    return

def game_loop():
    while True:
        draw_game()
        simulate()
        handle_inputs()

def title_screen():
    selected = 0
    while True:
        dclear(C_WHITE)
        dtext((DWIDTH//2) - (94//2), 40, C_BLACK, "Water logic")
        dtext(10, 100, C_BLACK, "New Game")
        dtext(10, 110, C_BLACK, "Continue")
        drect(3, 100 + selected*10, 8, 108 + (selected*10), C_BLACK)
        dupdate()

        ev = getkey()
        if ev.key == KEY_UP:
            selected = 0
        elif ev.key == KEY_DOWN:
            selected = 1
        elif ev.key == KEY_EXE:
            break

    assert selected in (0,1)
    if selected == 0:
        load_level(1)
        # TODO : play init cinematic
    if selected == 1:
        buf = ""
        while len(buf) < 4:
            dclear(C_WHITE)
            dtext(10, 100, C_BLACK, f"Password: {buf}_")
            dupdate()

            ev = getkey()
            if ev.key in INPUT_KEYS or ev.key == KEY_0:
                buf += str(([KEY_0] + INPUT_KEYS).index(ev.key))

        for level_number in range(1, len(LEVELS) + 1):
            if get_password(level_number) == int(buf):
                load_level(level_number)
                return

        dclear(C_WHITE)
        dtext(10, 100, C_BLACK, f"Password: {buf}_")
        dtext(10, 110, C_BLACK, f"Invalid password, press [EXE]")
        dupdate()

        while True:
            ev = getkey()
            if ev.key == KEY_EXE:
                return title_screen()

def main():
    title_screen()
    game_loop()

main()
