# MIT License, Copyright (c) 2024 JGN1722. See GAMES-LICENSE.txt.
# AgentCalc adaptation: gentle, clock-based movement, buffered steering,
# arrow-to-start, edge-triggered pause, and replay without recursive calls.
from kandinsky import *
from time import *
from random import *
from ion import *

CELL = 11
COLUMNS = 28
ROWS = 18
LEFT = 6
TOP = 24
MOVE_SECONDS = 0.18
POLL_SECONDS = 0.01
DIRECTIONS = ((KEY_LEFT, (-1, 0)), (KEY_UP, (0, -1)),
              (KEY_DOWN, (0, 1)), (KEY_RIGHT, (1, 0)))


def cell(point, color):
    fill_rect(LEFT + point[0] * CELL, TOP + point[1] * CELL,
              CELL - 1, CELL - 1, color)


def food_for(body):
    free = [(x, y) for y in range(ROWS) for x in range(COLUMNS)
            if (x, y) not in body]
    return free[randrange(len(free))] if free else None


def direction_key():
    for key, direction in DIRECTIONS:
        if keydown(key):
            return direction
    return None


def queue_turn(turns, direction, candidate):
    previous = turns[-1] if turns else direction
    if (candidate is not None and candidate != previous
            and candidate != (-previous[0], -previous[1]) and len(turns) < 2):
        turns.append(candidate)


def score_line(score, paused=False):
    fill_rect(0, 0, 320, TOP, "white")
    draw_string("Paused" if paused else "Score: " + str(score), 6, 2)


def play():
    fill_rect(0, 0, 320, 222, "white")
    draw_string("Snake", 135, 55)
    draw_string("Arrows to start", 85, 85)
    draw_string("Pause: XNT", 110, 115)
    direction = None
    while direction is None:
        direction = direction_key()
        sleep(POLL_SECONDS)

    head = (COLUMNS // 2, ROWS // 2)
    body = [(head[0] - i * direction[0], head[1] - i * direction[1])
            for i in range(5)]
    food = food_for(body)
    score = 0
    turns = []
    paused = False
    pause_held = keydown(KEY_XNT)
    fill_rect(0, 0, 320, 222, "white")
    score_line(score)
    for point in body:
        cell(point, "red")
    cell(food, "blue")
    next_move = monotonic() + MOVE_SECONDS
    won = False
    while True:
        now = monotonic()
        pause_down = keydown(KEY_XNT)
        if pause_down and not pause_held:
            paused = not paused
            score_line(score, paused)
            # Resuming always gives a full movement interval to react.
            next_move = now + MOVE_SECONDS
        pause_held = pause_down
        if not paused:
            queue_turn(turns, direction, direction_key())
            if now >= next_move:
                if turns:
                    direction = turns.pop(0)
                head = (body[0][0] + direction[0], body[0][1] + direction[1])
                growing = head == food
                if (not (0 <= head[0] < COLUMNS and 0 <= head[1] < ROWS)
                        or head in (body if growing else body[:-1])):
                    break
                if not growing:
                    cell(body.pop(), "white")
                body.insert(0, head)
                cell(head, "red")
                if growing:
                    score += 1
                    score_line(score)
                    food = food_for(body)
                    if food is None:
                        won = True
                        break
                    cell(food, "blue")
                # Never catch up with a burst of moves after a slow frame.
                next_move = now + MOVE_SECONDS
        sleep(POLL_SECONDS)

    fill_rect(0, 0, 320, 222, "white")
    draw_string("You win!" if won else "Game over", 110, 65)
    draw_string("Score: " + str(score), 115, 95)
    draw_string("XNT: again   OK: exit", 60, 125)
    while keydown(KEY_XNT) or keydown(KEY_OK):
        sleep(POLL_SECONDS)
    while True:
        if keydown(KEY_OK):
            return False
        if keydown(KEY_XNT):
            while keydown(KEY_XNT):
                sleep(POLL_SECONDS)
            return True
        sleep(POLL_SECONDS)


while play():
    pass
