import math
import random
from math import floor
from typing import Union

import pyxel

DEBUG = False
WIDTH = 128
HEIGHT = 128
TILE_HEIGHT = 8
enemy_bullets = []
bullets = []
enemies = []
splashes = []

x = 0
y = 0
tm = 1
u = 0
v = 0
w = 128 + (8 * 2)
h = 256

muzzle_flashes = []

SHIP_SPRITES = [(0, 8), (16, 8), (32, 8), (48, 8), (64, 8)]


class Splash:
    def __init__(self, x, y):
        self.position_x = x
        self.position_y = y
        self.sprites = ((57, 35, 6, 7), (56, 32, 10, 9), (72, 32, 16, 12))
        self.current_sprite_index = 0
        self.age = 0

    def update(self):
        self.age += 1

    def draw(self):
        current_sprite = self.sprites[self.current_sprite_index]
        pyxel.blt(
            self.position_x,
            self.position_y,
            0,
            current_sprite[0],
            current_sprite[1],
            current_sprite[2],
            current_sprite[3],
            0,
        )
        self.current_sprite_index = (self.current_sprite_index + 1) % len(self.sprites)


class CollisionBox:
    def __init__(self, position_x, position_y, width, height, x_scroll=0):
        self.position_x = position_x
        self.position_y = position_y
        self.width = width
        self.height = height
        self.color = 7
        self.x_scroll = x_scroll

    def draw(self):
        if DEBUG is True:
            pyxel.rectb(
                self.position_x,
                self.position_y,
                self.width,
                self.height,
                self.color,
            )
            pyxel.pset(self.position_x, self.position_y, 11)


def collision_detect(object_a, object_b):
    for obj in (object_a, object_b):
        if hasattr(obj, "ghost"):
            if obj.ghost is True:
                return False

    a_left_edge = object_a.position_x + object_a.x_scroll
    a_top_edge = object_a.position_y
    a_right_edge = object_a.position_x + object_a.x_scroll + object_a.width - 1
    a_bottom_edge = object_a.position_y + object_a.height - 1

    b_left_edge = object_b.position_x + object_b.x_scroll
    b_top_edge = object_b.position_y
    b_right_edge = object_b.position_x + object_b.x_scroll + object_b.width - 1
    b_bottom_edge = object_b.position_y + object_b.height - 1

    if a_top_edge > b_bottom_edge:
        return False
    if b_top_edge > a_bottom_edge:
        return False
    if a_left_edge > b_right_edge:
        return False
    if b_left_edge > a_right_edge:
        return False

    return True


def go_down_and_right_ai(position_x, position_y, speed_x, speed_y, age, x_scroll=0):
    return 1, 1


def go_down_then_fly_up_ai(position_x, position_y, speed_x, speed_y, age, x_scroll=0):
    if age < 15 // 2:
        return 0, 2 * 2
    elif age < 120 // 2:
        if age == 60 // 2:
            enemy_bullets.append(
                EnemyBullet(position_x, position_y + 2, 0, 2 * 2, x_scroll)
            )
        return 0, max(0, speed_y - 0.12)
    elif position_y > -16:
        return 0, speed_y - 0.08
    else:
        return 0, 0


def spawn_enemy():
    enemies.append(
        Enemy(random.randint(10, 128), random.randint(-32, -16), go_down_then_fly_up_ai)
    )


class Enemy:
    def __init__(self, position_x, position_y, brain=go_down_and_right_ai):
        self.position_x = position_x
        self.position_y = position_y
        self.speed_x = 0
        self.speed_y = 0
        self.sprites = (
            (0, 64, 16, 16, [(26, 67, 2, 3)]),
            (17, 64, 18, 16, []),
            (36, 64, 18, 16, [(26, 67, 2, 3)]),
        )
        self.sprite_number = 0
        self.brain = brain
        self.age = 0
        self.width = 16
        self.height = 16
        self.collision_box = CollisionBox(
            self.position_x - self.width // 2 + 1,
            self.position_y - self.height // 2,
            self.width,
            self.height,
        )
        self.x_scroll = 0
        self.number_of_frames_to_flash_for = 0
        self.dead = False
        self.health = 5

    def draw(self):
        pyxel.camera(-self.x_scroll, 0)
        sprite = self.sprites[int(self.sprite_number)]
        offset = 0
        if int(self.sprite_number) == 0:
            offset = 1
        pyxel.blt(
            self.position_x - 8 + offset,
            self.position_y - 8,
            0,
            sprite[0],
            sprite[1],
            sprite[2],
            sprite[3],
            0,
        )
        for sprite in sprite[4]:
            pyxel.blt(
                self.position_x + 1,
                self.position_y - 5,
                0,
                sprite[0],
                sprite[1],
                sprite[2],
                sprite[3],
                0,
            )
        self.sprite_number = (self.sprite_number + 0.15) % len(self.sprites)

        if DEBUG is True:
            pyxel.pset(self.position_x, self.position_y, 8)

        self.collision_box.draw()
        pyxel.camera()

    def update(self, x_scroll):
        self.x_scroll = x_scroll
        self.speed_x, self.speed_y = self.brain(
            self.position_x,
            self.position_y,
            self.speed_x,
            self.speed_y,
            self.age,
            x_scroll,
        )
        self.position_x += self.speed_x
        self.position_y += self.speed_y
        self.age += 1
        if int(self.sprite_number) == 0:
            self.width = 16
        else:
            self.width = 18
        self.collision_box.position_x = self.position_x - self.width // 2 + 1
        self.collision_box.position_y = self.position_y - self.height // 2
        self.collision_box.width = self.width
        self.collision_box.x_scroll = x_scroll


class Ship:
    def __init__(self, position_x, position_y):
        self.position_x = position_x
        self.position_y = position_y
        self.dx, self.dy = 0, 0
        self.last_dx, self.last_dy = 0, 0
        self.speed = 1.4 * 2
        self.ship_spr = 2.5
        self.shot_wait = 0
        self.weapon = "ddp"
        self.width = 16
        self.height = 16
        self.collision_box = CollisionBox(
            self.position_x - self.width // 2 + 0,
            self.position_y - self.height // 2,
            self.width,
            self.height,
        )
        self.x_scroll = 0

    def draw(self):
        pyxel.camera()
        pyxel.blt(
            self.position_x + 6 - 8,
            self.position_y + 15 - 8,
            0,
            0 + ((pyxel.frame_count // 2) % 3) * 8,
            24,
            8,
            8,
            0,
        )
        pyxel.blt(
            self.position_x + 3 - 8,
            self.position_y + 15 - 8,
            0,
            0 + ((pyxel.frame_count // 2) % 3) * 8,
            24,
            8,
            8,
            0,
        )

        for muzzle_flash in muzzle_flashes:
            pyxel.blt(
                self.position_x + muzzle_flash.x - 8,
                self.position_y + muzzle_flash.y - 8,
                0,
                muzzle_flash.muzzle_flash_sprites[muzzle_flash.animation_frame][0],
                muzzle_flash.muzzle_flash_sprites[muzzle_flash.animation_frame][1],
                16,
                16,
                0,
            )

        pyxel.blt(
            self.position_x - 8,
            self.position_y - 8,
            0,
            SHIP_SPRITES[floor(self.ship_spr)][0],
            SHIP_SPRITES[floor(self.ship_spr)][1],
            self.width,
            self.height,
            0,
        )
        if DEBUG is True:
            pyxel.pset(self.position_x, self.position_y, 8)

        self.collision_box.draw()

    def update(self, x_scroll):
        self.x_scroll = x_scroll
        if floor(self.ship_spr) in (0, 4):
            self.width = 13
        elif floor(self.ship_spr) in (1, 3):
            self.width = 14
        else:
            self.width = 16

        modifier = 0
        if floor(self.ship_spr) == 4:
            modifier = -1
        if DEBUG is True:
            pyxel.pset(self.position_x, self.position_y, 8)

        self.collision_box.position_x = self.position_x - self.width // 2 + modifier
        self.collision_box.position_y = self.position_y - self.height // 2
        self.collision_box.width = self.width
        self.width = 16
        self.dx, self.dy = 0, 0

        if pyxel.btn(pyxel.KEY_LEFT):
            if self.position_x > 8:
                self.dx = -self.speed
        if pyxel.btn(pyxel.KEY_RIGHT):
            if self.position_x < WIDTH - 8:
                self.dx = self.speed
        if pyxel.btn(pyxel.KEY_DOWN):
            self.dy = self.speed
        if pyxel.btn(pyxel.KEY_UP):
            self.dy = -self.speed

        if abs(self.dx) == abs(self.dy) and self.dx != 0:
            self.dx *= 0.7
            self.dy *= 0.7

        if self.last_dx != self.dx or self.last_dy != self.dy:
            # going diag
            if abs(self.dx) == abs(self.dy) and self.dx != 0:
                # anti cobblestone
                self.position_x = floor(self.position_x) + 0.5
                self.position_y = floor(self.position_y) + 0.5

        self.last_dx, self.last_dy = self.dx, self.dy

        self.position_x, self.position_y = (
            self.position_x + self.dx,
            self.position_y + self.dy,
        )

        bank_speed = 0.5

        if self.dx < 0:
            self.ship_spr = max(self.ship_spr - bank_speed, 0.9)
        elif self.dx > 0:
            self.ship_spr = min(self.ship_spr + bank_speed, len(SHIP_SPRITES) - 1)


def draw_menu(self):
    pyxel.bltm(
        0,
        0,
        tm,
        19 * 8,
        8 * 8,
        w,
        8 * 8 * 2,
    )


def update_menu(self):
    if pyxel.btn(pyxel.KEY_X):
        self.start_game()


def update_game(self):
    global enemies
    global splashes
    self.ship.collision_box.color = 7
    for enemy in enemies:
        enemy.collision_box.color = 7
    for bullet in bullets:
        bullet.collision_box.color = 7

    if pyxel.btn(pyxel.KEY_B):
        self.start_game()
        return

    if pyxel.btn(pyxel.KEY_U):  # Stop the bullets
        for bullet in bullets:
            bullet.speed_y = 0

    if pyxel.btn(pyxel.KEY_I):  # Stop the bullets
        for enemy in enemies:
            enemy.brain = lambda position_x, position_y, speed_x, speed_y, age: (0, 0)

    self.current_segments = [
        segment
        for segment in self.current_segments
        if self.scroll - segment[2] < 16 * 16
    ]

    if len(self.current_segments) == 0:
        self.current_segments.append(
            [
                map_segments_converter(self.map_segments[self.current_segment])[0],
                map_segments_converter(self.map_segments[self.current_segment])[1],
                self.scroll,
            ]
        )
        self.current_segment += 1
        self.current_segments.append(
            [
                map_segments_converter(self.map_segments[self.current_segment])[0],
                map_segments_converter(self.map_segments[self.current_segment])[1],
                self.scroll + 8 * 8,
            ]
        )
        self.current_segment += 1
        self.current_segments.append(
            [
                map_segments_converter(self.map_segments[self.current_segment])[0],
                map_segments_converter(self.map_segments[self.current_segment])[1],
                self.scroll + 8 * 16,
            ]
        )
        if self.current_segment < len(self.map_segments) - 1 and self.boss is False:
            self.current_segment += 1
    elif self.scroll - self.current_segments[-1][2] >= -64:
        self.current_segments.append(
            [
                map_segments_converter(self.map_segments[self.current_segment])[0],
                map_segments_converter(self.map_segments[self.current_segment])[1],
                self.current_segments[-1][2] + 64,
            ]
        )
        if self.current_segment < len(self.map_segments) - 1 and self.boss is False:
            self.current_segment += 1
    if self.boss is True and self.scroll % (16 * 8) == 0:
        self.scroll = self.scroll - 16 * 8
        for segment in self.current_segments:
            segment[2] -= 16 * 8

    self.scroll += 0.2

    self.ship.update(self.x_scroll)

    if pyxel.btnp(pyxel.KEY_E):
        self.explode(64, 64)
        self.slow_mo = False

    if pyxel.frame_count % 60 == 0:
        spawn_enemy()

    if not self.slow_mo:
        self.t += 1
        for i, p in enumerate(self.parts):
            self.do_part(p, i)

    if pyxel.btn(pyxel.KEY_O):
        self.boss = not self.boss

    self.x_scroll = floor(
        max(0, min(1, (self.ship.position_x - 10) / (128 - 20))) * (-16)
    )  # Ship width is 16

    if self.ship.shot_wait > 0:
        self.ship.shot_wait -= 1

    do_shots(self.x_scroll, self.ship)

    for enemy in enemies:
        enemy.update(self.x_scroll)
        if collision_detect(enemy.collision_box, self.ship.collision_box):
            enemy.collision_box.color = 8
            self.ship.collision_box.color = 8
        if enemy.position_y <= -16 and enemy.age > 20:
            enemy.dead = True

        if enemy.health <= 0:
            enemy.dead = True
            self.explode(enemy.position_x, enemy.position_y)

    for splash in splashes:
        splash.update()

    enemies = [enemy for enemy in enemies if enemy.dead is False]
    splashes = [splash for splash in splashes if splash.age < 4]

    if pyxel.btn(pyxel.KEY_X):
        shot_speed = 6 * 2
        number_of_bullets = 100
        number_of_frames_to_wait_between_shots = 4 // 2
        if self.ship.shot_wait <= 0 and len(bullets) < number_of_bullets:
            shot_ddp(
                self.ship.position_x, self.ship.position_y, shot_speed, self.x_scroll
            )
            self.ship.shot_wait = number_of_frames_to_wait_between_shots
            pyxel.play(0, 0)


def draw_game(self):
    pyxel.camera(-self.x_scroll, 0)
    for index, map_segment in enumerate(self.current_segments):
        segment_uplift = self.scroll - map_segment[2] + 64
        pyxel.bltm(
            0,
            0 + segment_uplift,
            tm,
            0 + (map_segment[0] * 8),
            map_segment[1] * 8,
            w,
            8 * 8,
        )
    pyxel.camera()

    for part in self.parts:  # Explosions
        if part.age >= 0:
            part.draw(part, self.x_scroll)

    pal_array = [8, 8, 8, 8, 8, 14, 7, 14, 15, 7, 7, 8, 8, 14, 7]
    for enemy in enemies:
        if enemy.number_of_frames_to_flash_for > 0:
            enemy.number_of_frames_to_flash_for -= 1
            for key, value in enumerate(pal_array):
                pyxel.pal(key, value)
        enemy.draw()
        pyxel.pal()

    for bullet in bullets:
        bullet.draw(self.ship.weapon)
    self.ship.draw()

    for splash in splashes:
        splash.draw()

    for bullet in enemy_bullets:
        bullet.draw()
    pyxel.camera()


def draw_circle(
    x: int,
    y: int,
    radius: float,
    color: int,
    bitfield: Union[int, str, None] = None,
    off_color: int = None,
    binary=False,
):
    grid = [[2**i for i in range(4 * k, 4 * (k + 1))] for k in range(4)]

    if bitfield:
        if isinstance(bitfield, str):
            if binary:
                bitfield = int(bitfield, 2)
            else:
                bitfield = int(bitfield, 16)

    color_to_draw = color

    for index_y, sub_y in enumerate(
        range(math.floor(y - radius), math.ceil(y + radius + 1))
    ):
        for index_x, sub_x in enumerate(
            range(math.floor(x - radius), math.ceil(x + radius + 1))
        ):
            if bitfield:
                value = grid[index_y % 4][index_x % 4]
                if value & bitfield == value:
                    color_to_draw = off_color
                else:
                    color_to_draw = color
            if radius <= 1:
                if math.sqrt((x - sub_x) ** 2 + (y - sub_y) ** 2) <= radius:
                    pyxel.pset(sub_x, sub_y, color_to_draw)
            else:
                if math.sqrt((x - sub_x) ** 2 + (y - sub_y) ** 2) < radius:
                    pyxel.pset(sub_x, sub_y, color_to_draw)


def spark(part, x_scroll):
    pyxel.camera(-x_scroll, 0)
    pyxel.line(
        part.x,
        part.y,
        part.x + (part.speed_x * 2),
        part.y + (part.speed_y * 2),
        part.primary_color,
    )
    pyxel.line(
        part.x, part.y, part.x + (part.speed_x * 2) + 1, part.y + (part.speed_y * 2), 7
    )
    pyxel.camera()


def blob(part, x_scroll):
    pyxel.camera(-x_scroll, 0)
    thickness = [0, part.r * 0.05, part.r * 0.15, part.r * 0.3]

    patterns = ["0b1111111111111111", "0b1011010010101101", "0b1000000000000000", 0]

    if part.r <= 2:
        patterns = ["0b1111111111111111"]
        thickness = [0]
    elif part.r <= 5:
        del thickness[3]
        del thickness[1]
        del patterns[1]
        del patterns[1]
    elif part.r <= 6:
        del thickness[2]
        del patterns[2]
        patterns[1] = "0b1010101010101010"
    elif part.r <= 8:
        del thickness[3]
        del patterns[2]
        patterns[1] = "0b1010101010101010"

    for index, th in enumerate(thickness):
        draw_circle(
            math.floor(part.x),
            math.floor(part.y) - th,
            part.r - th,
            part.primary_color,
            patterns[index],
            part.secondary_color,
            True,
        )
    if part.r == 1:
        pyxel.line(part.x, part.y - 1, part.x, part.y, part.secondary_color)
    elif part.r == 2:
        pyxel.rect(part.x, part.y - 1, 1, 1, part.secondary_color)

    pyxel.camera()


class Blob:
    def __init__(
        self,
        x,
        y,
        r,
        age=0,
        max_age=10,
        to_x=None,
        to_y=None,
        on_end=None,
        to_r=5,
        speed=1,
        primary_color=7,
        secondary_color=7,
        ctab=None,
        speed_x=0,
        speed_y=0,
        draw=blob,
        drag=None,
        ctabv=0,
    ):
        self.x = x
        self.y = y
        self.r = r
        self.age = age
        self.max_age = max_age
        self.to_x = to_x
        self.to_y = to_y
        self.on_end = on_end
        self.origin_x = x
        self.origin_y = y
        self.to_r = to_r
        self.speed = speed
        self.primary_color = primary_color
        self.secondary_color = secondary_color
        self.ctab = ctab
        self.speed_radius = None
        self.speed_x = speed_x
        self.speed_y = speed_y
        self.draw = draw
        self.drag = drag
        self.ctabv = ctabv


class Bullet:
    def __init__(self, position_x, position_y, speed_x, speed_y, original_x_scroll):
        self.position_x = position_x
        self.position_y = position_y
        self.speed_x = speed_x
        self.speed_y = speed_y
        self.shot_array = [0, 0, 1, 1, 2, 2]
        self.shot_animation = pyxel.frame_count % len(self.shot_array)
        self.original_x_scroll = original_x_scroll
        self.width = 8
        self.height = 16
        self.collision_box = CollisionBox(
            self.position_x - self.width // 2,
            self.position_y - self.height // 2,
            self.width,
            self.height,
        )
        self.x_scroll = original_x_scroll
        self.dead = False

    def draw(self, weapon_type):
        pyxel.camera(self.original_x_scroll - self.x_scroll, 0)
        if weapon_type == "raiden":
            pyxel.blt(self.position_x - 8, self.position_y - 8, 0, 128, 0, 3, 5, 0)
        else:
            pyxel.blt(
                self.position_x - 4,
                self.position_y - 8,
                0,
                8 * self.shot_array[self.shot_animation],
                32,
                8,
                16,
                0,
            )
            if DEBUG is True:
                pyxel.pset(self.position_x, self.position_y, 8)

            self.collision_box.draw()
        pyxel.camera()

    def update(self, x_scroll):
        self.x_scroll = x_scroll
        self.collision_box.position_x = self.position_x - self.width // 2
        self.collision_box.position_y = self.position_y - self.height // 2
        self.collision_box.width = self.width
        self.collision_box.heigt = self.height
        self.collision_box.x_scroll = self.original_x_scroll - self.x_scroll
        self.shot_animation = (self.shot_animation + 1) % len(self.shot_array)


class EnemyBullet:
    def __init__(self, position_x, position_y, speed_x, speed_y, x_scroll):
        self.position_x = position_x
        self.position_y = position_y
        self.speed_x = speed_x
        self.speed_y = speed_y
        self.shot_array = []
        self.shot_animation = 0
        self.width = 7
        self.height = 7
        self.x_scroll = x_scroll
        self.collision_box = CollisionBox(
            self.position_x - self.width // 2,
            self.position_y - self.height // 2,
            self.width,
            self.height,
        )

    def update(self, x_scroll):
        self.x_scroll = x_scroll
        self.collision_box.position_x = self.position_x - self.width // 2
        self.collision_box.position_y = self.position_y - self.height // 2
        self.collision_box.width = self.width
        self.collision_box.height = self.height
        self.collision_box.x_scroll = x_scroll

    def draw(self):
        pyxel.camera(-self.x_scroll, 0)
        pyxel.blt(
            self.position_x - self.width // 2,
            self.position_y - self.height // 2,
            0,
            24,
            32,
            6,
            6,
            0,
        )

        self.collision_box.draw()
        if DEBUG is True:
            pyxel.pset(self.position_x, self.position_y, 0)
        pyxel.camera()


class MuzzleFlash:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.muzzle_flash_sprites = [(0, 48), (16, 48), (16 * 2, 48), (16 * 3, 48)]
        self.animation_frame = 0


def do_shots(x_scroll, player_ship):
    global bullets
    global enemy_bullets
    global muzzle_flashes

    for bullet in bullets:
        bullet.position_x += bullet.speed_x
        bullet.position_y += bullet.speed_y
        bullet.update(x_scroll)

        for enemy in enemies:
            if collision_detect(enemy.collision_box, bullet.collision_box):
                enemy.number_of_frames_to_flash_for = 2
                enemy.collision_box.color = 8
                bullet.collision_box.color = 8
                bullet.dead = True
                splashes.append(Splash(bullet.position_x - 4, bullet.position_y - 8))
                enemy.health -= 1

    for bullet in enemy_bullets:
        bullet.position_x += bullet.speed_x
        bullet.position_y += bullet.speed_y
        bullet.update(x_scroll)
        if collision_detect(player_ship.collision_box, bullet.collision_box):
            player_ship.collision_box.color = 8
            bullet.collision_box.color = 8

    for muzzle_flash in muzzle_flashes:
        muzzle_flash.animation_frame += 1

    bullets = [
        bullet for bullet in bullets if bullet.position_y > -16 and bullet.dead is False
    ]
    enemy_bullets = [
        bullet
        for bullet in enemy_bullets
        if bullet.position_y > -16 or bullet.position_y > HEIGHT + 20
    ]
    muzzle_flashes = [
        muzzle_flash
        for muzzle_flash in muzzle_flashes
        if muzzle_flash.animation_frame < len(muzzle_flash.muzzle_flash_sprites)
    ]


def shot_ddp(px, py, shot_speed, x_scroll):
    bullets.append(Bullet(px - 4, py - 12, 0, -shot_speed, x_scroll))
    bullets.append(Bullet(px + 4, py - 12, 0, -shot_speed, x_scroll))
    muzzle_flashes.append(MuzzleFlash(-5, -8))
    muzzle_flashes.append(MuzzleFlash(5, -8))


def map_segments_converter(map_segment: int):
    return (map_segment // 4) * 18, (map_segment % 4) * 8


class App:
    def __init__(self):
        self.ship = Ship(WIDTH // 2, HEIGHT // 2)
        self.scroll = 0
        self.current_segment = 0
        self.map_segments = [
            3,
            3,
            3,
            3,
            3,
            2,
            1,
            0,
            1,
            7,
            6,
            5,
            10,
            4,
            11,
            6,
            11,
            11,
            5,
            9,
            10,
            8,
            1,
            0,
            15,
            14,
            1,
            13,
            12,
            19,
            19,
            18,
            17,
            16,
            18,
            17,
            16,
            17,
            16,
            19,
            22,
            21,
            20,
            27,
            26,
            25,
            23,
            24,
            3,
            3,
        ]
        self.current_segments = []
        self.x_scroll = 0
        self.boss = False

        self.my_blob = Blob(64, 64, 10)
        self.parts = []
        self.slow_mo = False
        self.t = 0

        pyxel.init(WIDTH, HEIGHT, title="Cow Shmup", fps=30)
        pyxel.load("my_resource_4.pyxres")
        n = 2
        output = []
        with open("./map.txt", "r") as myfile:
            for line in myfile:
                elements_as_integers = [
                    str(element).zfill(2) for element in line if element != "\n"
                ]
                elements_2 = [
                    reversed(elements_as_integers[i : i + n])
                    for i in range(0, len(elements_as_integers), n)
                ]
                elements_3 = ["".join(element) for element in elements_2]
                element_4 = " ".join(elements_3)
                output.append(element_4)

        pyxel.tilemap(1).set(0, 0, output)

        self._update = update_menu
        self._draw = draw_menu
        self.debug = [self.scroll]

        pyxel.run(self.update, self.draw)

    def start_game(self):
        self.ship = Ship(WIDTH // 2, HEIGHT // 2)

        self.scroll = 0
        self.x_scroll = 0
        self.boss = False
        self.current_segment = 0
        self.current_segments = []
        self.parts = []
        global bullets
        bullets = []
        global enemies
        enemies = []
        self.shot_wait = 0
        global muzzle_flashes
        muzzle_flashes = []

        self._update = update_game
        self._draw = draw_game

    def update(self):
        self._update(self)
        self.debug = [self.scroll]

    def draw(self):
        self._draw(self)
        y_offset = 0
        for text in self.debug:
            if DEBUG is True:
                pyxel.text(0, y_offset, str(text), 7)
                y_offset += 8

    def explode(self, ex, ey):
        pyxel.play(1, 1)
        self.parts.append(
            Blob(
                ex,
                ey,
                17,
                0,
                2,
                speed=2 * 2,
                primary_color=7,
                secondary_color=7,
                ctab=[(10, 9)],
                to_r=17,
            )
        )

        self.spark_blast(ex, ey, 2)
        self.spark_blast(ex, ey, 8)
        self.grape(
            ex,
            ey,
            2,
            13,
            2,
            "return",
            primary_color=7,
            secondary_color=10,
            ctab=[(7, 7), (7, 10), (7, 10), (10, 9)],
            drift=0,
        )
        self.grape(
            random.randrange(ex - 5, ex + 5),
            ey - 5,
            5,
            20,
            2,
            "return",
            primary_color=7,
            secondary_color=10,
            ctab=[(7, 10), (10, 9), (9, 10)],
            drift=-0.2,
        )
        self.grape(
            random.randrange(ex - 5, ex + 5),
            ey - 10,
            13,
            25,
            2,
            "fade",
            primary_color=10,
            secondary_color=9,
            ctab=[(7, 10), (7, 10), (10, 9), (9, 10), (13, 8), (13, 0)],
            drift=-0.3,
        )

    def grape(
        self,
        ex,
        ey,
        wait=0,
        max_age=120,
        speed=1,
        on_end="return",
        primary_color=0,
        secondary_color=0,
        ctab=None,
        drift=0,
    ):
        spokes = 6
        angle = random.random() * 2 * math.pi
        step = 1 / spokes

        for i in range(spokes):
            my_angle = angle + (step * 2 * math.pi * i)
            distance = 7 + random.randint(0, 3)
            distance_to = distance / 2
            # spawn blobs
            self.parts.append(
                Blob(
                    ex + math.sin(my_angle) * distance_to,
                    ey + math.cos(my_angle) * distance_to,
                    2,
                    -wait,
                    max_age,
                    ex + math.sin(my_angle) * distance,
                    ey + math.cos(my_angle) * distance,
                    on_end,
                    random.randint(4, 7),
                    speed=speed,
                    primary_color=primary_color,
                    secondary_color=secondary_color,
                    ctab=ctab,
                    speed_x=0,
                    speed_y=drift,
                    ctabv=random.randint(0, 5),
                )
            )
        self.parts.append(
            Blob(
                ex,
                ey,
                5,
                -wait,
                max_age,
                on_end=on_end,
                to_r=7,
                speed=speed,
                primary_color=primary_color,
                secondary_color=secondary_color,
                ctab=ctab,
                speed_x=0,
                speed_y=drift,
            )
        )

    def do_part(self, part, index):
        if part.age < 0:
            part.age += 1
            return
        else:
            part.age += 1

            # animate color
            if part.ctab:
                life = (part.age + part.ctabv) / part.max_age
                i = min(
                    [math.floor(life * (len(part.ctab) - 1)), len(part.ctab) - 1]
                )  # Might go over array
                part.primary_color = part.ctab[i][0]
                part.secondary_color = part.ctab[i][1]

            # movement
            if part.to_x is not None:
                part.x += (part.to_x - part.x) / (4 / part.speed)
                part.y += (part.to_y - part.y) / (4 / part.speed)

            if part.speed_x is not None:
                part.x += part.speed_x
                part.y += part.speed_y

                if part.to_x is not None:
                    part.to_x += part.speed_x
                    part.to_y += part.speed_y

                if part.drag is not None:
                    part.speed_x = part.speed_x * part.drag
                    part.speed_y = part.speed_y * part.drag
            # size
            if part.to_r is not None:
                part.r += (part.to_r - part.r) / (5 / part.speed)

            if part.speed_radius:
                part.r += part.speed_radius

            if part.age >= part.max_age or part.r < 0.5:
                part.ctab = None
                if part.on_end == "return":
                    part.on_end = None
                    part.max_age = 32_000
                    part.to_x = part.origin_x
                    part.to_y = part.origin_y
                    part.to_r = 0
                    part.to_r = None
                    part.speed_radius = -0.3
                elif part.on_end == "fade":
                    part.on_end = None
                    part.max_age = 32_000
                    part.to_r = None
                    part.speed_radius = -0.1 - random.random() * 0.3
                else:
                    del self.parts[index]

    def spark_blast(self, ex, ey, ewait):
        angle = random.random() * 2 * math.pi
        spd = random.randrange(4, 8)
        for i in range(6):
            angle_two = angle + random.random() * math.pi
            self.parts.append(
                Blob(
                    ex,
                    ey,
                    17,
                    -ewait,
                    random.randrange(8, 14),
                    speed=2,
                    primary_color=10,
                    secondary_color=7,
                    ctab=[(7, 7), (10, 10)],
                    to_r=17,
                    drag=0.8,
                    draw=spark,
                    speed_x=math.sin(angle_two) * spd,
                    speed_y=math.cos(angle_two) * spd,
                )
            )


App()