import os
import json
from typing import Optional, Callable, Literal

def _red(text):return f"\033[31m{text}\033[0m"   # 红色
def _green(text):return f"\033[32m{text}\033[0m" # 绿色
def _yellow(text):return f"\033[33m{text}\033[0m"# 黄色
def _blue(text):return f"\033[34m{text}\033[0m"  # 蓝色
def _purple(text):return f"\033[35m{text}\033[0m"# 紫色
def _cyan(text):return f"\033[36m{text}\033[0m"  # 青色
def _white(text):return f"\033[37m{text}\033[0m"# 白色
def _gray(text):return f"\033[90m{text}\033[0m"  # 灰色

class NotFoundFileError(Exception): __module__ = "builtins"
class UnboundWindowError(Exception): __module__ = "builtins"
class ImageNotShowError(Exception): __module__ = "builtins"
class NotFoundAliasError(Exception): __module__ = "builtins"


_number = 0

_file_path_and_alias = {}
_image_number = {}
_first_image_number = {}
_image_styling = {}
_first_image_wait = {}
_image_wait = {}
_already_started = {}
_is_reverse = {}
_animation_speed = {}

_image_is_show = []
_image_is_start = []

__next_number = {}

import random
if random.randint(1, 2) == 1: print(_red("\n\tHello, Animation. BiliBili @西瓜汁Code\n\t你好，Animation。B站 @西瓜汁Code\n") )
else: print(_blue("\n\tHello, Animation. BiliBili @西瓜汁Code\n\t你好，Animation。B站 @西瓜汁Code\n"))

try:
    import pygame
except ImportError:
    print(_red("""Pygame is not installed. Please install it first. Download command:
               pip install pygame"""))
    exit()

__again = False
__help_language = ""
def animation_help():
    global __again, __help_language, ic
    if __again == False: 
        ic = input(f"Select language {_green("(cn/en)")}")
        __help_language = ic
    else:
        if __help_language == "en":
            input(_gray("Press any key to continue..."))
        elif __help_language == "cn":
            input(_gray("按下任意键继续..."))
        ic = ""

    while ic != "exit":
        if __help_language == "cn":
            ic = input(_cyan(f"""
                你想了解什么？
                {_white("1.")}{_cyan("返回值函数")}{_white(":")}
                    -  {_white("1.")}{_blue("is_image_showing")}({_purple("alias : str")})
                    -  {_white("2.")}{_blue("is_animation_started")}({_purple("alias : str")})
                    -  {_white("3.")}{_blue("get_animation_frame")}({_purple("alias : str")})
                {_white("2.")}{_blue("add_file_path")}({_purple("file_path : str")}, {_purple("alias : str")}, {_purple("wait : int = 10")})
                {_white("3.")}{_blue("image_show")}({_purple("alias : str")}, {_purple("x : int")}, {_purple("y : int")}, {_purple("window : pygame.Surface")})
                {_white("4.")}{_blue("animation_start")}({_purple("alias : str")}, {_purple("total_frames : int")}, {_purple("_ : bool = True")}, {_purple('file_type : str = "png"')})
                {_white("5.")}{_blue("animation_stop")}()
                {_white("6.")}{_blue("set_animation_frame")}({_purple("alias : str")}, {_purple("frame : int = 1")})
                {_white("7.")}{_blue("animation_help")}()
                {_white("8.")}{_blue("set_animation_speed")}()
                {_white("9.")}{_blue("animation_reverse")}()
                {_white("10.")}{_blue("animation_reverse_back")}
                {_white("11.")}{_cyan("类")}{_white(":")}
                    -  {_white("1.")}{_blue("AnimationSprite")}
                    -  {_white("2.")}{_blue("AnimationStatus")}
                请输入您要了解的函数的编号（输入exit退出）：
                """))
            if ic == "exit":
                break
            elif ic == "1":
                ic = input(_blue(f"返回值函数：\n{_white("1.")}{_blue("is_image_showing")}({_purple("alias : str")})\n{_white("2.")}{_blue("is_animation_started")}({_purple("alias : str")})\n{_white("3.")}{_blue("get_animation_frame")}({_purple("alias : str")})\n请输入您要了解的函数的编号："))
                if ic == "1":
                    print(_green(f"is_image_showing({_purple("alias : str")}):\n这个函数返回的是一个布尔值，表示当前图片是否正在显示。"))
                elif ic == "2":
                    print(_green(f"is_animation_started({_purple("alias : str")}):\n这个函数返回的是一个布尔值，表示当前动画是否正在播放。"))
                elif ic == "3":
                    print(_green(f"get_animation_frame({_purple("alias : str")}):\n这个函数返回的是当前动画的帧数。"))
            elif ic == "2":
                print(_green(f"add_file_path({_purple("file_path : str")}, {_purple("alias : str")}, {_purple("wait : int = 10")})：\n这个函数用来加载图片路径并存储，并给它们分配别名。"))
            elif ic == "3":
                print(_green(f"image_show({_purple("alias : str")}, {_purple("x : int")}, {_purple("y : int")}, {_purple("window : pygame.Surface")})：\n这个函数用来设置图片的位置并显示。"))
            elif ic == "4":
                print(_green(f"animation_start({_purple("alias : str")}, {_purple("total_frames : int")}, {_purple("_ : bool = True")}, {_purple('file_type : str = "png"')})：\n这个函数用来启动动画。"))
            elif ic == "5":
                print(_green(f"animation_stop()：\n这个函数用来停止动画。"))
            elif ic == "6":
                print(_green(f"set_animation_frame({_purple("alias : str")}, {_purple("frame : int = 1")})：\n这个函数用来设置动画的当前帧。"))
            elif ic == "7":
                print(_green("animation_help()：\n这个函数用来显示帮助信息。"))
            elif ic == "8":
                print(_green("set_animation_speed()：\n这个函数用来设置动画的播放速度。"))
            elif ic == "9":
                print(_green("animation_reverse()：\n这个函数用来反转动画的播放。"))
            elif ic == "10":
                print(_green("animation_reverse_back()：\n这个函数用来恢复动画的播放。"))
            elif ic == "11":
                ic = input(_cyan(f"类：\n{_white("1.")}{_blue("AnimationSprite")}\n{_white("2.")}{_blue("AnimationStatus")}\n请输入您要了解的类的编号："))
                if ic == "1":
                    print(_green("AnimationSprite：\n可以被class继承，是2D-Animation-lib的唯一父类。"))
                elif ic == "2":
                    print(_green("AnimationStatus：\n获取动画状态的类。"))
            __again = True
        elif __help_language == "en":
            ic = input(_cyan(f"""
                What would you like to know?
                {_white("1.")}{_cyan("Return value functions")}{_white(":")}
                    -  {_white("1.")}{_blue("is_image_showing")}({_purple("alias : str")})
                    -  {_white("2.")}{_blue("is_animation_started")}({_purple("alias : str")})
                    -  {_white("3.")}{_blue("get_animation_frame")}({_purple("alias : str")})
                {_white("2.")}{_blue("add_file_path")}({_purple("file_path : str")}, {_purple("alias : str")}, {_purple("wait : int = 10")})
                {_white("3.")}{_blue("image_show")}({_purple("alias : str")}, {_purple("x : int")}, {_purple("y : int")}, {_purple("window : pygame.Surface")})
                {_white("4.")}{_blue("animation_start")}({_purple("alias : str")}, {_purple("total_frames : int")}, {_purple("_ : bool = True")}, {_purple('file_type : str = "png"')})
                {_white("5.")}{_blue("animation_stop")}()
                {_white("6.")}{_blue("set_animation_frame")}({_purple("alias : str")}, {_purple("frame : int = 1")})
                {_white("7.")}{_blue("animation_help")}()
                {_white("8.")}{_blue("set_animation_speed")}()
                {_white("9.")}{_blue("animation_reverse")}()
                {_white("10.")}{_blue("animation_reverse_back")}
                {_white("11.")}{_cyan("Class")}{_white(":")}
                    -  {_white("1.")}{_blue("AnimationSprite")}
                    -  {_white("2.")}{_blue("AnimationStatus")}
                Please enter the number of the function you want to know (input exit to exit): 
                """))
            if ic == "exit":
                break
            elif ic == "1":
                ic = input(_blue(f"Return value functions:\n{_white("1.")}{_blue("is_image_showing")}({_purple("alias : str")})\n{_white("2.")}{_blue("is_animation_started")}({_purple("alias : str")})\n{_white("3.")}{_blue("get_animation_frame")}({_purple("alias : str")})\nEnter the number of the function you want to learn:"))
                if ic == "1":
                    print(_green(f"is_image_showing({_purple("alias : str")}):\nThis function returns a boolean value indicating whether the current image is showing."))
                elif ic == "2":
                    print(_green(f"is_animation_started({_purple("alias : str")}):\nThis function returns a boolean value indicating whether the current animation is playing."))
                elif ic == "3":
                    print(_green(f"get_animation_frame({_purple("alias : str")}):\nThis function returns the current frame number of the animation."))
            elif ic == "2":
                print(_green(f"add_file_path({_purple("file_path : str")}, {_purple("alias : str")}, {_purple("wait : int = 10")})：\nThis function is used to load image file paths and store them, and assign aliases to them."))
            elif ic == "4":
                print(_green(f"animation_start({_purple("alias : str")}, {_purple("total_frames : int")}, {_purple("_ : bool = True")}, {_purple('file_type : str = "png"')})：\nThis function is used to start the animation."))
            elif ic == "5":
                print(_green(f"animation_stop()：\nThis function is used to stop the animation."))
            elif ic == "6":
                print(_green(f"set_animation_frame({_purple("alias : str")}, {_purple("frame : int = 1")})：\nThis function is used to set the current frame of the animation."))
            elif ic == "7":
                print(_green(f"animation_help()：\nThis function is used to display help information."))
            elif ic == "8":
                print(_green(f"set_animation_speed()：\nThis function is used to set the playback speed of the animation."))
            elif ic == "9":
                print(_green(f"animation_reverse()：\nThis function is used to reverse the playback of the animation."))
            elif ic == "10":
                print(_green(f"animation_reverse_back()：\nThis function is used to restore the playback of the animation."))
            elif ic == "11":
                ic = input(_cyan(f"Class:\n{_white("1.")}{_blue("AnimationSprite")}\n{_white("2.")}{_blue("AnimationStatus")}\nPlease enter the number of the class you want to learn:"))
                if ic == "1":
                    print(_green("AnimationSprite：\nIt can be inherited by the class, and is the only parent class of 2D-Animation-lib."))
                elif ic == "2":
                    print(_green("AnimationStatus：\nA class used to get the status of the animation."))
            __again = True
        animation_help()
        ic = "exit"



def is_image_showing(alias : str):
    if alias in _image_number:
        return _image_is_show[_image_number[alias]]
    else:
        raise NotFoundAliasError(f"Not found alias: {alias}")
    
def is_animation_started(alias : str):
    if alias in _image_number:
        return _image_is_start[_image_number[alias]]
    else:
        raise NotFoundAliasError(f"Not found alias: {alias}")

def get_animation_frame(alias : str):
    if alias in _image_number:
        return _image_number[alias] + 1
    else:
        raise NotFoundAliasError(f"Not found alias: {alias}")


def _delete_number(string : str):
    number = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    for num in number:
        string = string.replace(str(num), "")
    return string

def _get_file_name(list : list, _ : bool = True):
    if len(list) <= 1:
        list.append("")
    string = ""
    if _:
        for i in range(len(list)-1):
            if i != len(list)-2:
                string = string + list[i] + "_"
            else:
                string = string + list[i]
    else:
        return _delete_number(list[0])
    return string

def add_file_path(file_path : str, alias : str, wait : int = 10):
    """

    This function loads image paths into a dictionary and assigns an alias to them. 
    For example:
    add_file_path("D:\image\player_walk_1.png", "player_walk"). 

    """
    global _number
    if os.path.exists(file_path):
        if not wait < 0:
            _file_path_and_alias[alias] = file_path
            _image_styling[alias] = file_path
            _image_number[alias] = _number
            _first_image_number[alias] = _number
            _first_image_wait[alias] = wait
            _image_wait[alias] = wait
            _already_started[alias] = False
            _is_reverse[alias] = False
            _animation_speed[alias] = 1
            __next_number[alias] = 0
            _number += 1
            _image_is_show.append(False)
            _image_is_start.append(False)
        else:
            raise ValueError("Wait time cannot be negative.")
    else:
        raise NotFoundFileError(f"Not found file: {file_path}")

def image_show(alias : str, x : int, y : int, window : pygame.Surface):
    """

    This function sets and displays the position of the image on the window. 
    For example:
    image_show("player_walk", 100, 100) # sets the position of the image to (100, 100)

    """
    if _image_number[alias] >= len(_image_is_show):
        _image_is_show.extend([False] * (_image_number[alias] - len(_image_is_show) + 1))
    if alias in _file_path_and_alias:
        _image_is_show[_first_image_number[alias]] = True
        image = pygame.image.load(_image_styling[alias])
        window.blit(image, (x, y))
    else:
        raise NotFoundAliasError(f"Not found alias: {alias}")


def animation_start(alias : str, total_frames : int, callback : Optional[Callable[[], None]] = None,  _ : bool = True, file_type : str = "png"):
    """
    This funcion you can use animation_start to input the alias and decide whether to append an underscore suffix. 
    If it is "True", the result will be player_walk_1.png; if "False", it will be player_walk.png. 

    Additionally, you can specify the image format. 
    For example:
    animation_start("player_walk", True, "png") # player_walk_1.png
    """

    if alias not in _image_number:
        raise ImageNotShowError(f"Image not show: {alias} (alias not initialized)")

    if _image_number[alias] >= len(_image_is_show):
        _image_is_show.extend([False] * (_image_number[alias] - len(_image_is_show) + 1))

    if _image_is_show[_first_image_number[alias]] == True:
        if alias in _file_path_and_alias:
            _image_is_start[_first_image_number[alias]] = True
            _already_started[alias] = True
            if _image_wait[alias] <= 0:
                _image_wait[alias] = _first_image_wait[alias] // _animation_speed[alias]
                file_base_name : str = os.path.basename(_file_path_and_alias[alias])
                file_name  = file_base_name.split(".")[0].split("_")
                file_name = _delete_number(_get_file_name(file_name))
                
                # 获取当前的image_number
                current_number = _image_number[alias]

                if _is_reverse[alias] == False:
                    # 计算下一个image_number，如果超过count则重置为0
                    __next_number[alias] = (current_number + 1) % total_frames
                    if __next_number[alias] == 0:
                        if callback:
                            callback()
                elif _is_reverse[alias] == True:
                    # 计算上一个image_number，如果小于1则重置为count-1
                    __next_number[alias] = (current_number - 1) % total_frames
                    if __next_number[alias] == total_frames-1:
                        if callback:
                            callback()

                for i in range(1):  # 只更新一次
                    string = ""
                    if _ == True:
                        string = file_name + "_" + str(__next_number[alias] + 1) + "." + file_type
                    else:
                        string = file_name + str(__next_number[alias] + 1) + "." + file_type

                    _image_styling[alias] = os.path.join(os.path.dirname(_file_path_and_alias[alias]), string)

                    if not os.path.exists(_image_styling[alias]):
                        raise NotFoundFileError(f"Not found file: {_image_styling[alias]}")

                # 更新_image_number[alias]为next_number
                _image_number[alias] = __next_number[alias]

            else:
                _image_wait[alias] -= 1
        else:
            raise NotFoundAliasError(f"Not found alias: {alias}")
    elif _already_started[alias] == True:
        pass
    else:
        raise ImageNotShowError(f"Image not show: {_file_path_and_alias[alias]}")

def animation_stop(alias : str):
    """

    This function stops the animation of the image. 
    For example:
    animation_stop("player_walk")
    
    However, it is important to note that the animation_start() function will restart the animation, 
    so animation_stop() should be called only after ensuring that animation_start() will not be invoked.

    """

    if alias in _file_path_and_alias:
        _image_is_start[_first_image_number[alias]] = False
        _image_wait[alias] = _first_image_wait[alias]
    else:
        raise NotFoundAliasError(f"Not found alias: {alias}")
    
def set_animation_frame(alias : str, frame : int = 1):
    """

    This function sets the current frame of the animation. 
    For example:
    set_animation_frame("player_walk", 1) # sets the current frame to 1

    """

    if alias in _file_path_and_alias:
        _image_number[alias] = frame-2
    else:
        raise NotFoundAliasError(f"Not found alias: {alias}")

def set_animation_speed(alias : str, speed : int = 2):
    """

    This function sets the speed of the animation. 
    For example:
    set_animation_speed("player_walk", 2) # sets the speed to 2


    这个函数用来设置动画的速度。
    例如：
    set_animation_speed("player_walk", 2) # 设置速度为2

    """

    if alias in _file_path_and_alias:
        if speed > 0:
            if isinstance(speed, int):
                _animation_speed[alias] = speed
            else:
                raise ValueError(f"Speed cannot be {type(speed)}. It int of type")
        else:
            raise ValueError("Speed cannot be negative.")
    else:
        raise NotFoundAliasError(f"Not found alias: {alias}")

def animation_reverse(alias : str):
    """

    This function reverses the animation of the image. 
    For example:
    animation_reverse("player_walk")


    这个函数用来反转动画。
    例如：
    animation_reverse("player_walk")

    """

    if alias in _file_path_and_alias:
        _is_reverse[alias] = True
    else:
        raise NotFoundAliasError(f"Not found alias: {alias}")
    
def animation_reverse_back(alias : str):
    """

    This function reverses the animation of the image back to its original state. 
    For example:
    animation_reverse_back("player_walk")


    这个函数用来将动画恢复到原来的状态。
    例如：
    animation_reverse_back("player_walk")

    """

    if alias in _file_path_and_alias:
        _is_reverse[alias] = False
    else:
        raise NotFoundAliasError(f"Not found alias: {alias}")

class AnimationStatus:
    def __init__(self, alias : str):
        if alias in _file_path_and_alias:
            self.alias = alias
            self.is_showing = is_image_showing(alias)
            self.is_started = is_animation_started(alias)
            self.frame = get_animation_frame(alias)
            self.speed = _animation_speed[alias]
            self.is_reverse = _is_reverse[alias]
            self.file_path = _file_path_and_alias[alias]
            self.file_name = os.path.basename(self.file_path).split(".")[0].split("_")
            self.file_name = _delete_number(_get_file_name(self.file_name))
            self.total_frames = len(os.listdir(os.path.dirname(self.file_path))) - 1
            self.current_frame = self.frame - 1
        else:
            raise NotFoundAliasError(f"Not found alias: {alias}")

    def get_settings(self):
        """

        This function returns a string in JSON format that contains most of the animation information.


        这个函数返回的是一个json格式的字符串，包含了动画的大部分信息。
        """

        return json.dumps({
            "alias": self.alias,
            "is_showing": self.is_showing,
            "is_started": self.is_started,
            "frame": self.frame,
            "speed": self.speed,
            "is_reverse": self.is_reverse,
            "file_path": str(self.file_path),
            "file_name": self.file_name,
            "total_frames": self.total_frames,
            "current_frame": self.current_frame
        })

    def get_status(self, status : Literal["animation_started", 
                                          "image_showing", 
                                          "animation_start", 
                                          "animation_end", 
                                          "animation_reverse"]):
        """

        This function returns some status information of the current animation.
        For example:
            animation_started -> bool
            image_showing -> bool
            animation_start -> bool
            animation_end -> bool
            animation_reverse -> bool


        这个函数返回的是当前动画的一些状态信息。
        比如：
            animation_started -> bool
            image_showing -> bool
            animation_start -> bool
            animation_end -> bool
            animation_reverse -> bool

        """
        
        if status == "animation_started":
            return self.is_started
        elif status == "image_showing":
            return self.is_showing
        elif status == "animation_start":
            return self.current_frame == 0
        elif status == "animation_end":
            return self.current_frame == self.total_frames - 1
        elif status == "animation_reverse":
            return self.is_reverse
        else:
            raise ValueError(f"Not found status: {status}")

