# -*- coding:utf-8 -*-

from __future__ import annotations
from typing import Dict

import pygame
import copy
import pygame.locals
import math
import random

Run = True
name = "cyfgame"
Image = pygame.Surface
Sound = pygame.mixer.Sound
Rect = pygame.Rect
clock = pygame.time.Clock()
width = 500
height = 500
gameTick = 30
openError = False
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption(name)

start_tick = pygame.time.get_ticks()

ticks = {}

def init() -> None:
    global screen
    pygame.init()
    screen = pygame.display.set_mode((width, height), 0, 32)
    pygame.display.set_caption(name)

def tick() -> int:
    return pygame.time.get_ticks()

def createSleep(name: str, time: int) -> None:
    if not (name in ticks.keys()) or (ticks[name] == (-1, -1)): ticks[name] = (tick(), time)
    elif openError: raise KeyError(f"Can't create \'{name}\'.")

def deleteSleep(name: int) -> None:
    if name in ticks: ticks[name] = (-1, -1)

def stopTick(name) -> bool:
    if not name in ticks: return True
    if tick() - ticks[name][0] >= ticks[name][1] * 1000:
        return True
    return False

def copyRole(role: Role) -> None:
    return copy.copy(role)

def quit() -> None:
    global Run
    Run = False

def python_pos(x: int, y: int) -> tuple[int]:
    return width//2+x, height//2-y

def roleImage(role: Role) -> tuple[Image, Rect]:
    image = role.image[role.imageid-1]
    new_width = image.get_width() * role.size / 200
    new_height = image.get_height() * role.size / 200
    image = pygame.transform.scale(image, (new_width, new_height))
    rect = image.get_rect()
    image = pygame.transform.rotate(image, (450-role.d)%360)
    rect = image.get_rect(center=rect.center)
    rect.center = (python_pos(role.x, role.y))
    image.set_alpha((100-role.alpha)/20*51)
    brightness = max(0, min(100, role.br))
    adjusted_image = image.copy()
    if brightness > 0:
        brighten_value = int(255 * (brightness / 100.0))
        brighten_surface = pygame.Surface(adjusted_image.get_size(), pygame.SRCALPHA)
        brighten_surface.fill((brighten_value, brighten_value, brighten_value, 0))
        adjusted_image.blit(brighten_surface, (0, 0), special_flags=pygame.BLEND_RGB_ADD)
    return (adjusted_image, rect)

def collision(self, role) -> bool:
    image1 = self.image[self.imageid-1].convert_alpha()
    new_width = image1.get_width() * self.size / 200
    new_height = image1.get_height() * self.size / 200
    image1 = pygame.transform.scale(image1, (new_width, new_height))
    image1 = pygame.transform.rotate(image1, (450 - self.d) % 360)
    rect1 = image1.get_rect(center=python_pos(self.x, self.y))
    mask1 = pygame.mask.from_surface(image1)

    image2 = role.image[role.imageid-1].convert_alpha()
    new_width = image2.get_width() * role.size / 200
    new_height = image2.get_height() * role.size / 200
    image2 = pygame.transform.scale(image2, (new_width, new_height))
    image2 = pygame.transform.rotate(image2, (450 - role.d) % 360)
    rect2 = image2.get_rect(center=python_pos(role.x, role.y))
    mask2 = pygame.mask.from_surface(image2)

    offset = (rect2.x - rect1.x, rect2.y - rect1.y)
    
    return mask1.overlap(mask2, offset) is not None

def say(role: Role, content: str, time: int = 2, tickName: str = "say") -> None:
    createSleep(tickName, time)
    rect = pygame.Rect(21, 21+15, width-40, 40-30)
    pygame.draw.rect(screen, "#ffffff", rect)
    rect = pygame.Rect(21+15, 21, width-40-30, 40)
    pygame.draw.rect(screen, "#ffffff", rect)
    pygame.draw.circle(screen, "#ffffff", (36, 36), 15)
    pygame.draw.circle(screen, "#ffffff", (width-35, 36), 15)
    pygame.draw.circle(screen, "#ffffff", (36, 45), 15)
    pygame.draw.circle(screen, "#ffffff", (width-35, 45), 15)
    roleRect = roleImage(role)[0].get_rect()
    roleRect.center = (width-35, rect.centery)
    screen.blit(roleImage(role)[0], roleRect)
    font = pygame.font.SysFont("华文宋体", 16)
    text = font.render(content, True, "#000000")
    textRect = text.get_rect()
    textRect.center = rect.center
    screen.blit(text, textRect)

def ceil(x: float) -> int: return math.ceil(x)
def floor(x: float) -> int: return math.floor(x)
def sqrt(x: float) -> float: return math.sqrt(x)

class Mouse:
    def __init__(self):
        self.name = "Mouse"

    def pos(self) -> tuple[int, int]:
        return pygame.mouse.get_pos()
    
    def left(self) -> bool:
        res, _, _ = pygame.mouse.get_pressed()
        return res
    
    def middle(self) -> bool:
        _, res, _ = pygame.mouse.get_pressed()
        return res
    
    def mid(self) -> bool:
        _, res, _ = pygame.mouse.get_pressed()
        return res

    def right(self) -> bool:
        _, _, res = pygame.mouse.get_pressed()
        return res 

stdmouse = Mouse()

class Rand:
    def __init__(self):
        self.name = "Rand"

    def getInt(self, l: int, r: int) -> int:
        return random.randint(l, r)
    
    def getFloat(self, l: int, r: int, d: int) -> float:
        return random.randint(l, r) / d
    
    def gi(self, l: int, r: int) -> int: return self.getInt(l, r)

    def gf(self, l: int, r: int, d: int) -> float: return self.getFloat(l, r, d)

stdrand = Rand()

class Process():
    def __init__(self, name):
        self.name = name
        self.processid = 0

processes: Dict[str, Process] = {}

def createProcess(name: str):
    processes[name] = Process(name)

def getProcessId(name: str):
    return processes[name].processid

def nextProcessId(name: str):
    processes[name].processid += 1

class Role():
    def __init__(self):
        self.x = 0
        self.y = 0
        self.d = 90
        self.alpha = 0
        self.actionid = 0
        self.size = 100
        self.br = 0
        self.name = "Role"
        self.image: list[Image] = []
        self.imageid = 1
        self.cloneMe: list[Role] = []
        self.father: Role = None
        self.isShow = True
        self.st = 0
        self.isSleep = False
        self.second = 0

    def left(self, angle: int) -> None:
        self.d = (self.d - angle + 360) % 360

    def right(self, angle: int) -> None:
        self.d = (self.d + angle + 360) % 360

    def setx(self, x: int) -> None:
        self.x = x
    
    def sety(self, y: int) -> None:
        self.y = y

    def goto(self, x, y) -> None:
        if type(x) == type((0, 0)):
            x, y = x
        self.x = x
        self.y = y

    def glide(self, sx: int, sy: int, x: int, y: int, t: int) -> None:
        self.addx((x-sx) / (gameTick * t))
        self.addy((y-sy) / (gameTick * t))

    def move(self, dis: int) -> None:
        radian = math.radians(90 - self.d)
        dx = dis * math.cos(radian)
        dy = dis * math.sin(radian)
        self.x += dx
        self.y += dy

    def setd(self, d: int) -> None:
        self.d = d

    def isIn(self, x: int, y: int) -> None:
        return abs(self.x-x) <= 10 and abs(self.y-y) <= 10

    def point(self, obj: Role) -> None:
        x1 = self.x
        y1 = self.y
        if obj.name == "Role":
            x2 = obj.x
            y2 = obj.y
        if obj.name == "Rand":
            x2 = obj.gi(-500, 500)
            y2 = obj.gi(-500, 500)
        if obj.name == "Mouse":
            x2, y2 = obj.pos()

        dx = x2 - x1
        dy = y2 - y1
        d = 90 - math.degrees(math.atan2(dy, dx))

        self.d = d

    def setSize(self, size: int) -> None:
        self.size = size

    def setBr(self, br: int) -> None:
        self.br = br

    def addx(self, x: int) -> None:
        self.x += x

    def addy(self, y: int) -> None:
        self.y += y

    def addSize(self, size: int) -> None:
        self.size += size

    def addBr(self, br: int) -> None:
        self.br = min(self.br+br, 100)

    def insertImage(self, *image: Image) -> None:
        for i in image: self.image.append(i)

    def setImage(self, id: int) -> None:
        self.imageid = id

    def nextImage(self) -> None:
        self.imageid = (self.imageid) % len(self.image) + 1

    def hide(self) -> None:
        self.isShow = False

    def show(self) -> None:
        self.isShow = True

    def say(self, content: str, time: int = 2, tickName: str = "say") -> None:
        say(self, content, time, tickName)

    def cloneBack(self) -> Role:
        return self.cloneMe[-1]
    
    def cb(self) -> Role:
        return self.cloneBack()

    def clone(self, role: Role = None) -> None:
        if self.father == None:
            father = self
        else:
            father = self.father
        if role == None:
            cloneMe = copy.copy(self)
        else:
            cloneMe = copy.copy(role)
        cloneMe.cloneMe = []
        cloneMe.father = father
        father.cloneMe.append(cloneMe)


    def removeClone(self, cloneMe: Role) -> None:
        self.cloneMe.remove(cloneMe)

    def collision(self, role=None) -> bool:
        if role == None:
            _, rect = roleImage(self)
            return rect[0] < 0 or rect[1] < 0 or rect[0] + rect[2] - 1 > width or rect[1] + rect[3] - 1 > height 
        if role.name == "Role":
            if role.isShow and collision(self, role): return role
            for i in role.cloneMe:
                if i.isShow and collision(self, i): return i
            return False

    def sleep(self, second: float) -> None:
        self.isSleep = True
        self.st = tick()
        self.second = second * 1000

    def stopSleep(self) -> bool:
        if self.isSleep and tick() - self.st >= self.second:
            self.isSleep = False
        return not self.isSleep

    def display(self) -> None:
        if self.isShow:
            image, rect = roleImage(self)
            screen.blit(image, rect)

class GameControl:
    def __init__(self):
        self.gameid = 0
        self.processid = 0
        self.lastKey = {}

    def key(self, k: int) -> bool:
        self.lastKey = pygame.key.get_pressed()
        return pygame.key.get_pressed()[k]
    
    def updatekey(self) -> None:
        self.lastKey = pygame.key.get_pressed()

    def keydown(self, k: int) -> bool:
        return not self.lastKey[k] and self.key(k)

class MixerControl:
    def __init__(self):
        self.sound: Dict[str, Sound] = {}

    def insertSound(self, name: str, sound: Sound) -> None:
        self.sound[name] = sound

    def play(self, name: str, loops: int = 0) -> None:
        self.sound[name].play(loops=loops)

    def stop(self) -> None:
        pygame.mixer.stop()

class BackGround:
    def __init__(self):
        self.image = []
        self.imageid = 0
        self.var = {}

    def insertImage(self, image: Image) -> None:
        self.image.append(image)

    def setImage(self, id: int) -> None:
        self.imageid = id - 1

    def nextImage(self) -> None:
        self.imageid = (self.imageid + 1) % len(self.image)

    def display(self) -> None:
        image = self.image[self.imageid]
        rect = image.get_rect()
        screen.blit(image, rect)

def app() -> None:
    pass

def main() -> None:
    global Run
    screen.fill("#ffffff")
    pygame.display.update()
    while Run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                Run = False

        app()
        clock.tick(gameTick)
        pygame.display.update()