import ez180n.console

const LEFT_WALL: u8 = 24
const RIGHT_WALL: u8 = 55
const TOP_WALL: u8 = 5
const BOTTOM_WALL: u8 = 51
const PLAYER_Y: u8 = 47
const SHIP_START_X: u8 = 39
const MAX_METEORS: u8 = 8
const HUD_Y: u8 = 5
const DELAY_TICKS: u16 = 2400

global ship_x: u8 = SHIP_START_X
global score: u16 = 0
global tick: u8 = 0
global crashed: u8 = 0
global meteor_x: [u8; MAX_METEORS] = [26, 31, 36, 42, 47, 51, 29, 49]
global meteor_y: [u8; MAX_METEORS] = [7, 12, 17, 23, 29, 35, 41, 46]
global meteor_speed: [u8; MAX_METEORS] = [1, 1, 1, 2, 1, 1, 1, 2]

fn put_digit(x: u8, y: u8, value: u16, divisor: u16) {
    let digit: u8 = cast<u8>((value / divisor) % 10)
    console.put_char(x, y, '0' + digit)
}

fn put_score() {
    console.put_char(25, HUD_Y, 'S')
    console.put_char(26, HUD_Y, 'C')
    console.put_char(27, HUD_Y, 'O')
    console.put_char(28, HUD_Y, 'R')
    console.put_char(29, HUD_Y, 'E')
    put_digit(31, HUD_Y, score, 1000)
    put_digit(32, HUD_Y, score, 100)
    put_digit(33, HUD_Y, score, 10)
    put_digit(34, HUD_Y, score, 1)
}

fn draw_box() {
    let x: u8 = LEFT_WALL
    while x <= RIGHT_WALL {
        console.put_char(x, TOP_WALL, console.CHAR_BOX_HORIZONTAL)
        console.put_char(x, BOTTOM_WALL, console.CHAR_BOX_HORIZONTAL)
        x += 1
    }

    let y: u8 = TOP_WALL
    while y <= BOTTOM_WALL {
        console.put_char(LEFT_WALL, y, console.CHAR_BOX_VERTICAL)
        console.put_char(RIGHT_WALL, y, console.CHAR_BOX_VERTICAL)
        y += 1
    }

    console.put_char(LEFT_WALL, TOP_WALL, console.CHAR_BOX_DOWN_RIGHT)
    console.put_char(RIGHT_WALL, TOP_WALL, console.CHAR_BOX_DOWN_LEFT)
    console.put_char(LEFT_WALL, BOTTOM_WALL, console.CHAR_BOX_UP_RIGHT)
    console.put_char(RIGHT_WALL, BOTTOM_WALL, console.CHAR_BOX_UP_LEFT)
}

fn draw_stars() {
    console.put_char(27, 9, '.')
    console.put_char(34, 14, '.')
    console.put_char(50, 15, '.')
    console.put_char(44, 21, '.')
    console.put_char(29, 27, '.')
    console.put_char(53, 33, '.')
    console.put_char(36, 39, '.')
    console.put_char(48, 44, '.')
}

fn draw_ship() {
    console.put_char(ship_x, PLAYER_Y - 1, console.CHAR_ARROW_UP)
    console.put_char(ship_x - 1, PLAYER_Y, console.CHAR_LEFT_HALF)
    console.put_char(ship_x, PLAYER_Y, console.CHAR_FULL_BLOCK)
    console.put_char(ship_x + 1, PLAYER_Y, console.CHAR_RIGHT_HALF)
    console.put_char(ship_x - 1, PLAYER_Y + 1, '/')
    console.put_char(ship_x + 1, PLAYER_Y + 1, '\\')
}

fn draw_meteors() {
    let index: u8 = 0
    while index < MAX_METEORS {
        let x: u8 = meteor_x[index]
        let y: u8 = meteor_y[index]
        console.put_char(x, y, console.CHAR_FULL_BLOCK)
        console.put_char(x - 1, y, console.CHAR_DARK_SHADE)
        console.put_char(x + 1, y, console.CHAR_MEDIUM_SHADE)
        console.put_char(x, y + 1, console.CHAR_LOWER_HALF)
        index += 1
    }
}

fn clear_row(y: u8) {
    let x: u8 = 0
    while x < console.WIDTH {
        console.put_char(x, y, console.CHAR_SPACE)
        x += 1
    }
}

fn clear_header() {
    clear_row(0)
    clear_row(1)
    clear_row(2)
    clear_row(3)
}

fn draw_title() {
    clear_header()
    console.put_char(35, 1, 'M')
    console.put_char(36, 1, 'E')
    console.put_char(37, 1, 'T')
    console.put_char(38, 1, 'E')
    console.put_char(39, 1, 'O')
    console.put_char(40, 1, 'R')
    console.put_char(42, 1, 'R')
    console.put_char(43, 1, 'U')
    console.put_char(44, 1, 'N')

    console.put_char(29, 53, 'D')
    console.put_char(30, 53, 'P')
    console.put_char(31, 53, 'A')
    console.put_char(32, 53, 'D')
    console.put_char(34, 53, 'M')
    console.put_char(35, 53, 'O')
    console.put_char(36, 53, 'V')
    console.put_char(37, 53, 'E')
    console.put_char(40, 53, 'A')
    console.put_char(42, 53, 'R')
    console.put_char(43, 53, 'E')
    console.put_char(44, 53, 'S')
    console.put_char(45, 53, 'T')
    console.put_char(46, 53, 'A')
    console.put_char(47, 53, 'R')
    console.put_char(48, 53, 'T')
}

fn draw_crash() {
    console.put_char(35, 24, 'S')
    console.put_char(36, 24, 'H')
    console.put_char(37, 24, 'I')
    console.put_char(38, 24, 'P')
    console.put_char(40, 24, 'L')
    console.put_char(41, 24, 'O')
    console.put_char(42, 24, 'S')
    console.put_char(43, 24, 'T')

    console.put_char(30, 27, 'P')
    console.put_char(31, 27, 'R')
    console.put_char(32, 27, 'E')
    console.put_char(33, 27, 'S')
    console.put_char(34, 27, 'S')
    console.put_char(36, 27, 'A')
    console.put_char(38, 27, 'T')
    console.put_char(39, 27, 'O')
    console.put_char(41, 27, 'R')
    console.put_char(42, 27, 'E')
    console.put_char(43, 27, 'L')
    console.put_char(44, 27, 'A')
    console.put_char(45, 27, 'U')
    console.put_char(46, 27, 'N')
    console.put_char(47, 27, 'C')
    console.put_char(48, 27, 'H')
}

fn render() {
    console.fill(console.CHAR_SPACE)
    draw_box()
    draw_stars()
    put_score()
    draw_meteors()
    draw_ship()
    if crashed != 0 {
        draw_crash()
    }
    draw_title()
    console.present()
}

fn reset_game() {
    ship_x = SHIP_START_X
    score = 0
    tick = 0
    crashed = 0
    meteor_y[0] = 7
    meteor_y[1] = 12
    meteor_y[2] = 17
    meteor_y[3] = 23
    meteor_y[4] = 29
    meteor_y[5] = 35
    meteor_y[6] = 41
    meteor_y[7] = 46
    console.play_sound(36)
}

fn handle_input() {
    if console.button_down(0, console.BUTTON_LEFT) != 0 {
        if ship_x > LEFT_WALL + 3 {
            ship_x -= 1
        }
    }

    if console.button_down(0, console.BUTTON_RIGHT) != 0 {
        if ship_x < RIGHT_WALL - 3 {
            ship_x += 1
        }
    }

    if console.button_down(0, console.BUTTON_A) != 0 {
        if crashed != 0 {
            reset_game()
        }
    }
}

fn wrap_meteor(index: u8) {
    meteor_y[index] = TOP_WALL + 2
    meteor_x[index] = LEFT_WALL + 4 + ((meteor_x[index] + 17 + tick) % 24)
    score += 1
}

fn update_meteors() {
    let index: u8 = 0
    while index < MAX_METEORS {
        meteor_y[index] += meteor_speed[index]
        if meteor_y[index] > BOTTOM_WALL - 3 {
            wrap_meteor(index)
        }
        index += 1
    }
}

fn check_collision() {
    let index: u8 = 0
    while index < MAX_METEORS {
        let dx: u8 = 0
        if meteor_x[index] > ship_x {
            dx = meteor_x[index] - ship_x
        } else {
            dx = ship_x - meteor_x[index]
        }

        if dx <= 2 && meteor_y[index] >= PLAYER_Y - 1 && meteor_y[index] <= PLAYER_Y + 1 {
            crashed = 1
            console.play_sound(91)
        }
        index += 1
    }
}

fn delay() {
    let spin: u16 = 0
    while spin < DELAY_TICKS {
        spin += 1
    }
}

fn main() {
    reset_game()
    loop {
        handle_input()
        if crashed == 0 {
            tick += 1
            update_meteors()
            check_collision()
        }
        render()
        delay()
    }
}
