import inspect
import pygame as pg
import pygame._sdl2
from types import SimpleNamespace
import os
import sys
import traceback
from datetime import datetime
import platform
import time
pg.init()
pg.display.init()

VERSION = "1.0.0"

class Build:
    MGL = SimpleNamespace()
    sell = SimpleNamespace()
    fell = SimpleNamespace()
    fell.Data = fell.data = SimpleNamespace()
    sell.data_Texture = {}
    sell.data_Music = {}
    sell.Cache = {}
    sell.Events = {}

    def __init__(self, name="My Game", left=800, up=600, ot_pos=True, SafeMode=True, logs=True, DeveLoperMode=True, vsync=True,
                 black_window=True, size_window_standart=(800, 600), size_window_how=(800, 600)):
        global VERSION
        if logs: print(f"\033[90mInitializing Engine {VERSION}\033[0m")
        if getattr(sys, 'frozen', False): self.sell.PATH = sys._MEIPASS
        else: self.sell.PATH = os.path.dirname(os.path.abspath(__file__))
        self.sell.PATH = os.path.dirname(self.sell.PATH.rstrip(os.sep))

        self.sell.screen = pg._sdl2.video.Window(name, size=(left, up), opengl=1)
        self.sell.left, self.sell.up = left, up
        self.sell.render = pg._sdl2.video.Renderer(self.sell.screen, index=-1, accelerated=1, vsync=vsync)
        self.sell.render_texture = pg._sdl2.video.Texture(self.sell.render, size=(left, up), target=1)
        self.sell.war = True
        self.sell.ot_pos = ot_pos
        def Event_List_def(events):
            res = {
                "quit": False,
                "mouse down left": False,
                "mouse down right": False,
                "mouse down center": False,
                "mouse up left": False,
                "mouse up right": False,
                "mouse up center": False,
                "touch down": False,
                "touch up": False,
                "scroll +": False,
                "scroll -": False,
                "mouse down additional 1": False,
                "mouse down additional 2": False,
                "mouse up additional 1": False,
                "mouse up additional 2": False
            }
            
            for ev in events:
                if ev.type == pg.QUIT:
                    res["quit"] = True
                elif ev.type == pg.MOUSEBUTTONDOWN:
                    if ev.button == pg.BUTTON_LEFT: res["mouse down left"] = True
                    elif ev.button == pg.BUTTON_RIGHT: res["mouse down right"] = True
                    elif ev.button == pg.BUTTON_MIDDLE: res["mouse down center"] = True
                    elif ev.button == pg.BUTTON_X1: res["mouse down additional 1"] = True
                    elif ev.button == pg.BUTTON_X2: res["mouse down additional 2"] = True
                elif ev.type == pg.MOUSEBUTTONUP:
                    if ev.button == pg.BUTTON_LEFT: res["mouse up left"] = True
                    elif ev.button == pg.BUTTON_RIGHT: res["mouse up right"] = True
                    elif ev.button == pg.BUTTON_MIDDLE: res["mouse up center"] = True
                    elif ev.button == pg.BUTTON_X1: res["mouse up additional 1"] = True
                    elif ev.button == pg.BUTTON_X2: res["mouse up additional 2"] = True
                elif ev.type == pg.FINGERDOWN:
                    res["touch down"] = True
                elif ev.type == pg.FINGERUP:
                    res["touch up"] = True
                elif ev.type == pg.MOUSEWHEEL:
                    if ev.y < 0: res["scroll +"] = True
                    elif ev.y > 0: res["scroll -"] = True
                    
            return res
        self.sell.Event_List_def = Event_List_def
        self.sell.Scene = {}
        self.sell.falt = None
        self.sell.vsync = vsync
        self.sell.SafeMode = SafeMode
        self.sell.logs = logs
        self.sell.Clock = pg.time.Clock()
        self.sell.FPS = 0
        self.sell.PressedButton = None
        self.sell.DeltaTimeHow = None
        self.sell.DeveLoperMode = DeveLoperMode
        self.sell.DeveLoperMode_font = pg.font.SysFont(None, 20)
        self.sell.DeveLoperMode_text = f"DeveLoperMode: fps: {0} | ... "
        self.sell.DeveLoperMode_render = self.sell.DeveLoperMode_font.render(self.sell.DeveLoperMode_text, True, (255, 255, 255))
        self.sell.DeveLoperMode_Texture = pg._sdl2.Texture.from_surface(self.sell.render, self.sell.DeveLoperMode_render)
        self.sell.touch = {}
        self.sell.black_window = black_window
        self.sell.size_window_standart = size_window_standart
        self.sell.size_window_how = size_window_how
        self.sell.TextureScreen = pg._sdl2.Texture.from_surface(self.sell.render, pg.Surface(size_window_how))
        self.sell.PyGame = pg
        pg.key.set_repeat(500, 50)
        if logs: print("\033[92mcompleted successfully\033[0m")

        if logs: print("\033[90mInitializing Functions\033[0m")
        def NewCanvas(color=None):
            if not color is None: self.sell.render.draw_color = color
            self.sell.render.clear()
        self.fell.NewCanvas = NewCanvas
        
        def End(): self.sell.war = False
        self.fell.End = End
        
        def Draw():
            if self.sell.DeveLoperMode:
                if not self.sell.DeveLoperMode_text == f"DeveLoperMode: fps: {Build.fell.GetFPS()} | ... ":
                    self.sell.DeveLoperMode_text = f"DeveLoperMode: fps: {Build.fell.GetFPS()} | ... "
                    self.sell.DeveLoperMode_render = self.sell.DeveLoperMode_font.render(self.sell.DeveLoperMode_text, True, (255, 255, 255))
                    self.sell.DeveLoperMode_Texture = pg._sdl2.Texture.from_surface(
                        self.sell.render, self.sell.DeveLoperMode_font.render(self.sell.DeveLoperMode_text, True, (255, 255, 255)))
                self.sell.DeveLoperMode_Texture.draw(None, pg.Rect(0, 0, self.sell.DeveLoperMode_render.get_width(), self.sell.DeveLoperMode_render.get_height()))

            pg._sdl2.video.Renderer.present(self.sell.render)
        self.fell.Draw = Draw

        def File(path): return os.path.join(Build.sell.PATH, path)
        self.fell.File = File
        
        def LoadedTexture(path):
            if path in Build.sell.Cache: return Build.sell.Cache[path]
            else:
                surf = pg.image.load(File(path))
                tex = pg._sdl2.video.Texture.from_surface(Build.sell.render, surf)
                Build.sell.Cache[path] = tex
                return tex
        self.fell.LoadedTexture = LoadedTexture

        class Texture:
            def __init__(self, name, file, x=100, y=100, w=100, h=100, angle=0, static=False):
                if ot_pos: x, y, w, h = left*x, up*y, left*w, up*h
                self.scripts = []
                self.type = "Texture"
                if logs:
                    if ot_pos:
                        print(f"\033[90mCreate {self.type} with parameters:\n- name:",\
                            f" {name};\n- file: {file};\n- position: {x, y};\n- size: {w, h};\n- angle: {angle};\n- static: {static};\033[0m")
                    else:
                        print(f"\033[90mCreate {self.type} with parameters:\n- name:",\
                            f" {name};\n- file: {file};\n- position: {x, y};\n- size: {w, h};\n- angle: {angle};\n- static: {static};\033[0m")
                self.static = static
                if file in Build.sell.Cache: tex = Build.sell.Cache[file]
                else:
                    surf = pg.image.load(File(file))
                    if surf.get_size() != (w, h): surf = pg.transform.scale(surf, (w, h))
                    tex = pg._sdl2.video.Texture.from_surface(Build.sell.render, surf)
                    Build.sell.Cache[file] = tex
                    del surf
                self.texture = tex
                self.angle = angle
                self.pos = [x, y]
                self.size = [w, h]
                Build.sell.data_Texture[name] = {"tex": self}
                print(f"\033[92mCreate {self.type} completed successfully\033[0m")
            def AddScript(self, script):
                self.scripts.append(script)
                if logs: print(f"\033[90mAdd script {script} to {self.type} completed successfully\033[0m")
            def TextureUpdate(self):
                for script in self.scripts: script.run()
            def RemoveScript(self, script): self.scripts.remove(script)
            def GetScripts(self): return self.scripts
            def get_size(self): return self.size
            def get_type(self): return self.type
            def get_static(self): return self.static
            def get_angle(self): return self.angle
            def set_angle(self, angle): self.angle = angle
            def set_static(self, static): self.static = static
            def get_pos(self): return self.pos
            def get_texture(self): return self.texture
            def set_pos(self, x, y): 
                if ot_pos: x, y = left*x, up*y
                self.pos = [x, y]
            def Move(self, x, y): self.pos[0] += x; self.pos[1] += y
            def MoveY(self, y): self.pos[1] += y
            def MoveX(self, x): self.pos[0] += x
            def AngleAdd(self, angle): self.angle += angle
            def AngleSub(self, angle): self.angle -= angle
            def MoveMent(self, keys, speed):
                keys = keys.split(".")
                if ot_pos:
                    if Build.fell.ButtonPressed(keys[0]): self.MoveY(up*-speed)
                    if Build.fell.ButtonPressed(keys[1]): self.MoveX(left*-speed)
                    if Build.fell.ButtonPressed(keys[2]): self.MoveY(up*speed)
                    if Build.fell.ButtonPressed(keys[3]): self.MoveX(left*speed)
                else:
                    if Build.fell.ButtonPressed(keys[0]): self.MoveY(-speed)
                    if Build.fell.ButtonPressed(keys[1]): self.MoveX(-speed)
                    if Build.fell.ButtonPressed(keys[2]): self.MoveY(speed)
                    if Build.fell.ButtonPressed(keys[3]): self.MoveX(speed)
            def TextureDraw(self):
                if self.pos[0] + self.size[0] > -Build.fell.Camera.pos[0] and self.pos[0] < -Build.fell.Camera.pos[0] + Build.sell.left and \
                    self.pos[1] + self.size[1] > -Build.fell.Camera.pos[1] and self.pos[1] < -Build.fell.Camera.pos[1] + Build.sell.up:
                        try:
                            self.texture.draw(None, pg.Rect(
                                (self.pos[0] + Build.fell.Camera.pos[0] - self.size[0] / 2, self.pos[1] + Build.fell.Camera.pos[1] - self.size[1] / 2), self.size)\
                                if not self.static else pg.Rect((self.pos[0] - self.size[0] / 2, self.pos[1] - self.size[1] / 2), self.size),
                                float(self.angle), (self.size[0] / 2., self.size[1] / 2.))
                        except Exception as f: print(f"\033[31mFATAL ERROR: {f}\033[0m")
        self.fell.Texture = Texture

        def DrawSquare(color=None, x=100, y=100, w=100, h=100, static=False):
            if ot_pos: x, y, w, h = left*x, up*y, left*w, up*h
            if not color is None: self.sell.render.draw_color = color
            if static: self.sell.render.fill_rect(pg.Rect(x, y, w, h))
            else: self.sell.render.fill_rect(pg.Rect(x - Build.fell.Camera.pos[0], y - Build.fell.Camera.pos[1], w, h))
        self.fell.DrawSquare = DrawSquare

        class Square:
            def __init__(self, name, color=None, x=100, y=100, w=100, h=100, angle=0, static=False):
                if ot_pos: x, y, w, h = left*x, up*y, left*w, up*h
                self.scripts = []
                self.type = "Square"
                if logs: print(f"\033[90mCreate {self.type} with parameters:\n- name:",\
                    f" {name};\n- position: {x, y};\n- size: {w, h};\n- angle: {angle};\n- static: {static};\033[0m")
                self.static = static
                surf = pg.Surface((w, h))
                surf.fill(color if color is not None else Build.sell.render.draw_color)
                self.texture = pg._sdl2.video.Texture.from_surface(Build.sell.render, surf)
                del surf
                self.angle = angle
                self.pos = [x, y]
                self.size = [w, h]
                self.color = name if not color is not None else color
                Build.sell.data_Texture[name] = {"tex": self}
                print(f"\033[92mCreate {self.type} completed successfully\033[0m")
            def AddScript(self, script):
                self.scripts.append(script)
                if logs: print(f"\033[90mAdd script {script} to {self.type} completed successfully\033[0m")
            def SquareUpdate(self):
                for script in self.scripts: script.run()
            def RemoveScript(self, script): self.scripts.remove(script)
            def GetScripts(self): return self.scripts
            def get_color(self): return self.color
            def get_name(self): return self.name
            def set_color(self, color): self.color = color
            def get_size(self): return self.size
            def get_type(self): return self.type
            def get_static(self): return self.static
            def get_angle(self): return self.angle
            def set_angle(self, angle): self.angle = angle
            def set_static(self, static): self.static = static
            def get_pos(self): return self.pos
            def get_texture(self): return self.texture
            def set_pos(self, x, y): 
                if ot_pos: x, y = left*x, up*y
                self.pos = [x, y]
            def Move(self, x, y): self.pos[0] += x; self.pos[1] += y
            def MoveY(self, y): self.pos[1] += y
            def MoveX(self, x): self.pos[0] += x
            def AngleAdd(self, angle): self.angle += angle
            def AngleSub(self, angle): self.angle -= angle
            def MoveMent(self, keys, speed):
                keys = keys.split(".")
                if ot_pos:
                    if Build.fell.ButtonPressed(keys[0]): self.MoveY(up*-speed)
                    if Build.fell.ButtonPressed(keys[1]): self.MoveX(left*-speed)
                    if Build.fell.ButtonPressed(keys[2]): self.MoveY(up*speed)
                    if Build.fell.ButtonPressed(keys[3]): self.MoveX(left*speed)
                else:
                    if Build.fell.ButtonPressed(keys[0]): self.MoveY(-speed)
                    if Build.fell.ButtonPressed(keys[1]): self.MoveX(-speed)
                    if Build.fell.ButtonPressed(keys[2]): self.MoveY(speed)
                    if Build.fell.ButtonPressed(keys[3]): self.MoveX(speed)
            def SquareDraw(self):
                if self.pos[0] + self.size[0] > -Build.fell.Camera.pos[0] and self.pos[0] < -Build.fell.Camera.pos[0] + Build.sell.left and \
                    self.pos[1] + self.size[1] > -Build.fell.Camera.pos[1] and self.pos[1] < -Build.fell.Camera.pos[1] + Build.sell.up:
                        try:
                            self.texture.draw(None, pg.Rect(
                                (self.pos[0] + Build.fell.Camera.pos[0], self.pos[1] + Build.fell.Camera.pos[1]), self.size)\
                                if not self.static else pg.Rect((self.pos[0], self.pos[1]), self.size),
                                float(self.angle), (self.size[0] / 2., self.size[1] / 2.))
                        except Exception as f: print(f"\033[31mFATAL ERROR: {f}\033[0m")
        self.fell.Square = Square

        class Text:
            def __init__(self, name, text, x, y, size, angle, color, static=False, center=True):
                if ot_pos: x, y, size = left*x, up*y, up*size
                if text == "": text = " "
                self.name = name
                self.angle = angle
                self.text = text
                self.pos = [x, y]
                self.size = size
                self.color = color
                self.scripts = []
                self.render = pg.font.SysFont(None, int(size)).render(text, True, color)
                self.texture = pg._sdl2.video.Texture.from_surface(Build.sell.render, self.render)
                self.type = "Text"
                if logs: print(f"\033[90mCreate {self.type} with parameters:\n- name:",\
                    f" {name};\n- text: {text};\n- position: {x, y};\n- size: {size};\n- color: {color};\n- static: {static};\n- center: {center};\033[0m")
                self.static = static
                self.center = center
                Build.sell.data_Texture[name] = {"tex": self}
                self.size_Rect = [*self.render.get_size()]
                if logs: print(f"\033[92mCreate {self.type} completed successfully\033[0m")
            def AddScript(self, script):
                self.scripts.append(script)
                if logs: print(f"\033[90mAdd script {script} to {self.type} completed successfully\033[0m")
            def TextUpdate(self):
                for script in self.scripts: script.run()
            def RemoveScript(self, script): self.scripts.remove(script)
            def GetScripts(self): return self.scripts
            def get_color(self): return self.color
            def get_name(self): return self.name
            def set_color(self, color): self.color = color
            def get_size(self): return self.size
            def get_type(self): return self.type
            def get_static(self): return self.static
            def get_pos(self): return self.pos
            def get_texture(self): return self.texture
            def set_text(self, text):
                self.text = text
                self.render = pg.font.SysFont(None, int(self.size)).render(text, True, self.color)
                self.size_Rect = self.render.get_size()
                self.texture = pg._sdl2.video.Texture.from_surface(Build.sell.render, self.render)
            def set_pos(self, x, y): 
                if ot_pos: x, y = left*x, up*y
                self.pos = [x, y]
            def Move(self, x, y): self.pos[0] += x; self.pos[1] += y
            def MoveY(self, y): self.pos[1] += y
            def MoveX(self, x): self.pos[0] += x
            def MoveMent(self, keys, speed):
                keys = keys.split(".")
                if ot_pos:
                    if Build.fell.ButtonPressed(keys[0]): self.MoveY(up*-speed)
                    if Build.fell.ButtonPressed(keys[1]): self.MoveX(left*-speed)
                    if Build.fell.ButtonPressed(keys[2]): self.MoveY(up*speed)
                    if Build.fell.ButtonPressed(keys[3]): self.MoveX(left*speed)
                else:
                    if Build.fell.ButtonPressed(keys[0]): self.MoveY(-speed)
                    if Build.fell.ButtonPressed(keys[1]): self.MoveX(-speed)
                    if Build.fell.ButtonPressed(keys[2]): self.MoveY(speed)
                    if Build.fell.ButtonPressed(keys[3]): self.MoveX(speed)
            def TextDraw(self):
                if self.center:
                    if self.pos[0] + self.size_Rect[0] > -Build.fell.Camera.pos[0] and self.pos[0] < -Build.fell.Camera.pos[0] + Build.sell.left and \
                        self.pos[1] + self.size_Rect[1] > -Build.fell.Camera.pos[1] and self.pos[1] < -Build.fell.Camera.pos[1] + Build.sell.up:
                            try:
                                self.texture.draw(None, pg.Rect(
                                    (self.pos[0] + Build.fell.Camera.pos[0] - self.size_Rect[0] / 2, self.pos[1] + Build.fell.Camera.pos[1] - self.size_Rect[1] / 2), self.size_Rect)\
                                    if not self.static else pg.Rect((self.pos[0] - self.size_Rect[0] / 2, self.pos[1] - self.size_Rect[1] / 2), self.size_Rect),
                                    float(self.angle), (self.size_Rect[0] / 2., self.size_Rect[1] / 2.))
                            except Exception as f: print(f"\033[31mFATAL ERROR: {f}\033[0m")
                else:
                    if self.pos[0] + self.size_Rect[0] > -Build.fell.Camera.pos[0] and self.pos[0] < -Build.fell.Camera.pos[0] + Build.sell.left and \
                        self.pos[1] + self.size_Rect[1] > -Build.fell.Camera.pos[1] and self.pos[1] < -Build.fell.Camera.pos[1] + Build.sell.up:
                            try:
                                self.texture.draw(None, pg.Rect(
                                    (self.pos[0] + Build.fell.Camera.pos[0], self.pos[1] + Build.fell.Camera.pos[1]), self.size_Rect)\
                                    if not self.static else pg.Rect(self.pos, self.size_Rect),
                                    float(self.angle))
                            except Exception as f: print(f"\033[31mFATAL ERROR: {f}\033[0m")
        self.fell.Text = Text

        class Button:
            def __init__(self, name, text, x, y, size, angle, color, func, mouse="mouse down left", static=False, center=True):
                self.type = "Button"
                if ot_pos: x, y, size = left*x, up*y, up*size
                self.angle = angle
                self.center = center
                self.mouse = mouse
                self.func = func
                self.name = name
                self.text = text
                self.x = x
                self.y = y
                self.pos = [x, y]
                self.size = size
                self.color = color
                self.font = pg.font.SysFont(None, int(size))
                self.render = self.font.render(text, True, color)
                texture_noText = pg.Surface((x, y), pg.SRCALPHA)
                texture_noText.fill((255, 255, 255, 255))
                self.texture_noText = pg._sdl2.video.Texture.from_surface(Build.sell.render, texture_noText)
                self.texture = pg._sdl2.video.Texture.from_surface(Build.sell.render, self.render)
                self.type = "Button"
                if logs: print(f"\033[90mCreate {self.type} with parameters:\n- name:",\
                    f" {name};\n- text: {text};\n- position: {x, y};\n- size: {size};\n- color: {color};\n- static: {static};\033[0m")
                self.static = static
                self.size_Rect = [*self.render.get_size()]
                Build.sell.Events[name] = self
                Build.sell.data_Texture[name] = {"tex": self}
                if logs: print(f"\033[92mCreate {self.type} completed successfully\033[0m")
            def AddScript(self, script):
                self.scripts.append(script)
                if logs: print(f"\033[90mAdd script {script} to {self.type} completed successfully\033[0m")
            def TextUpdate(self):
                for script in self.scripts: script.run()
            def RemoveScript(self, script): self.scripts.remove(script)
            def GetScripts(self): return self.scripts
            def GetFunc(self): return self.func
            def get_type(self): return self.type
            def get_name(self): return self.name
            def get_text(self): return self.text
            def get_color(self): return self.color
            def get_size(self): return self.size
            def get_texture(self): return self.texture
            def get_static(self): return self.static
            def get_pos(self): return self.pos
            def get_angle(self): return self.angle
            def get_func(self): return self.func
            def set_func(self, func): self.func = func
            def set_angle(self, angle): self.angle = angle
            def set_color(self, color):
                self.color = color
                self.texture = pg._sdl2.video.Texture.from_surface(Build.sell.render, self.font.render(self.text, True, color).convert_alpha())
            def set_text(self, text):
                self.text = text
                self.texture = pg._sdl2.video.Texture.from_surface(Build.sell.render, self.font.render(text, True, self.color).convert_alpha())
            def set_size(self, size):
                self.size = size
                self.font = pg.SysFont(None, size)
                self.texture = pg._sdl2.video.Texture.from_surface(Build.sell.render, self.font.render(self.text, True, self.color).convert_alpha())
            def set_pos(self, x, y): 
                if ot_pos: x, y = left*x, up*y
                self.pos = [x, y]
            def Move(self, x, y): self.pos[0] += x; self.pos[1] += y
            def MoveY(self, y): self.pos[1] += y
            def MoveX(self, x): self.pos[0] += x
            def is_hovered(self):
                m_pos = pg.mouse.get_pos()
                if self.center: rect = pg.Rect(self.pos[0] - self.size_Rect[0]/2, self.pos[1] - self.size_Rect[1]/2, *self.size_Rect)
                else: rect = pg.Rect(*self.pos, *self.size_Rect)
                return rect.collidepoint(m_pos)
            def MoveMent(self, keys, speed):
                keys = keys.split(".")
                if ot_pos:
                    if Build.fell.ButtonPressed(keys[0]): self.MoveY(up*-speed)
                    if Build.fell.ButtonPressed(keys[1]): self.MoveX(left*-speed)
                    if Build.fell.ButtonPressed(keys[2]): self.MoveY(up*speed)
                    if Build.fell.ButtonPressed(keys[3]): self.MoveX(left*speed)
                else:
                    if Build.fell.ButtonPressed(keys[0]): self.MoveY(-speed)
                    if Build.fell.ButtonPressed(keys[1]): self.MoveX(-speed)
                    if Build.fell.ButtonPressed(keys[2]): self.MoveY(speed)
                    if Build.fell.ButtonPressed(keys[3]): self.MoveX(speed)
            def ButtonDraw(self):
                if self.center:
                    if pg.mouse.get_pos()[0] > self.pos[0] - self.size_Rect[0] / 2 - 10 and pg.mouse.get_pos()[0] < self.pos[0] + self.size_Rect[0] / 2 + 10 and \
                        pg.mouse.get_pos()[1] > self.pos[1] - self.size_Rect[1] / 2 - 10 and pg.mouse.get_pos()[1] < self.pos[1] + self.size_Rect[1] / 2 + 10:
                            self.texture_noText.color = (100, 100, 100)
                    else: self.texture_noText.color = (0, 0, 0)
                    if self.pos[0] + self.size_Rect[0] > -Build.fell.Camera.pos[0] and self.pos[0] < -Build.fell.Camera.pos[0] + Build.sell.left and \
                        self.pos[1] + self.size_Rect[1] > -Build.fell.Camera.pos[1] and self.pos[1] < -Build.fell.Camera.pos[1] + Build.sell.up:
                            try:
                                self.texture_noText.draw(None, pg.Rect(
                                    (self.pos[0] + Build.fell.Camera.pos[0] - self.size_Rect[0] / 2 - 10, self.pos[1] + Build.fell.Camera.pos[1] - self.size_Rect[1] / 2 - 10), (self.size_Rect[0] + 20, self.size_Rect[1] + 10))\
                                    if not self.static else pg.Rect((self.pos[0] - 10 - self.size_Rect[0] / 2, self.pos[1] - 10 - self.size_Rect[1] / 2), (self.size_Rect[0] + 20, self.size_Rect[1] + 20)),
                                    float(self.angle), (self.size_Rect[0] / 2., self.size_Rect[1] / 2.)
                                )
                                self.texture.draw(None, pg.Rect(
                                    (self.pos[0] + Build.fell.Camera.pos[0] - self.size_Rect[0] / 2, self.pos[1] + Build.fell.Camera.pos[1] - self.size_Rect[1] / 2), self.size_Rect)\
                                    if not self.static else pg.Rect((self.pos[0] - self.size_Rect[0] / 2, self.pos[1] - self.size_Rect[1] / 2), self.size_Rect),
                                    float(self.angle), (self.size_Rect[0] / 2., self.size_Rect[1] / 2.))
                            except Exception as f: print(f"\033[31mFATAL ERROR: {f}\033[0m")
                else:
                    if pg.mouse.get_pos()[0] > self.pos[0] and pg.mouse.get_pos()[0] < self.pos[0] + self.size_Rect[0] and \
                        pg.mouse.get_pos()[1] > self.pos[1] and pg.mouse.get_pos()[1] < self.pos[1] + self.size_Rect[1]:
                            self.texture_noText.color = (100, 100, 100)
                    else: self.texture_noText.color = (0, 0, 0)
                    if self.pos[0] + self.size_Rect[0] > -Build.fell.Camera.pos[0] and self.pos[0] < -Build.fell.Camera.pos[0] + Build.sell.left and \
                        self.pos[1] + self.size_Rect[1] > -Build.fell.Camera.pos[1] and self.pos[1] < -Build.fell.Camera.pos[1] + Build.sell.up:
                            try:
                                self.texture_noText.draw(None, pg.Rect(
                                    (self.pos[0] + Build.fell.Camera.pos[0] - 10, self.pos[1] + Build.fell.Camera.pos[1] - 10), (self.size_Rect[0] + 20, self.size_Rect[1] + 10))\
                                    if not self.static else pg.Rect((self.pos[0] - 10, self.pos[1] - 10), (self.size_Rect[0] + 20, self.size_Rect[1] + 20)),
                                    float(self.angle))
                                self.texture.draw(None, pg.Rect(
                                    (self.pos[0] + Build.fell.Camera.pos[0], self.pos[1] + Build.fell.Camera.pos[1]), self.size_Rect)\
                                    if not self.static else pg.Rect(self.pos, self.size_Rect),
                                    float(self.angle))
                            except Exception as f: print(f"\033[31mFATAL ERROR: {f}\033[0m")
        self.fell.Button = Button

        class Input_Field:
            def __init__(self, name, default_text, x, y, w, h, angle, static=False, center=False):
                if ot_pos: x, y, w, h = left*x, up*y, left*w, up*h
                self.name = name
                self.type = "Input_Field"
                self.default_text = default_text
                self.text = ""
                self.Focus = False
                self.angle = angle
                self.x = x
                self.y = y
                self.w = w
                self.h = h
                self.time = 0
                self.static = static
                self.center = center
                self.pos = [x, y]
                self.size = [w, h]
                self.font = pg.font.SysFont(None, int(h))
                self.render_text_pg = pg.font.SysFont(None, int(h)).render(default_text, True, (0, 0, 0))
                self.render_text_pg.fill((255, 255, 255))
                self.render_size = self.render_text_pg.get_size()
                self.render = pg._sdl2.video.Texture.from_surface(Build.sell.render, pg.Surface((w, h), pg.SRCALPHA))
                texture = pg.Surface((left * 0.01, self.render_text_pg.get_size()[1]))
                self.render_text_pg = pg._sdl2.video.Texture.from_surface(Build.sell.render, self.render_text_pg)
                texture.fill((255, 255, 255, 255))
                self.texture = pg._sdl2.video.Texture.from_surface(Build.sell.render, texture); self.texture_size = texture.get_size()
                Fon = pg.Surface((w, h), pg.SRCALPHA)
                Fon.fill((0, 0, 0, 255))
                self.Fon = pg._sdl2.video.Texture.from_surface(Build.sell.render, Fon)
                Build.sell.data_Texture[name] = {"tex": self}
                if logs:
                    print(f"\033[90mCreate {self.type} with name:"\
                        f" {name};\n- default text: {default_text};\n- position: {x, y};\n- size: {w, h};\n- angle: {angle};\n- static: {static};\n- center: {center};\033[0m")
                    print(f"\033[92mCreate {self.type} completed successfully\033[0m")
                self._update_texture()
            
            def Input_FieldDraw(self):
                if self.Focus: self.time += 1
                if self.center:
                    if self.pos[0] - self.w / 2 + self.render_size[0] > -Build.fell.Camera.pos[0] and self.pos[0] - self.w / 2 < -Build.fell.Camera.pos[0] + Build.sell.left and \
                        self.pos[1] - self.h / 2 + self.render_size[1] > -Build.fell.Camera.pos[1] and self.pos[1] - self.h / 2 < -Build.fell.Camera.pos[1] + Build.sell.up:

                        self.Fon.draw(None, pg.Rect(self.x + Build.fell.Camera.pos[0] - self.w / 2, self.y + Build.fell.Camera.pos[1] - self.y / 2, self.w, self.h) if\
                            not self.static else pg.Rect(self.x - self.w / 2, self.y - self.h / 2, self.w, self.h), float(self.angle), (self.w / 2., self.h / 2.))
                        
                        self.render.draw(None, pg.Rect(self.x + Build.fell.Camera.pos[0] - self.w / 2, self.y + Build.fell.Camera.pos[1] + 10 - self.h / 2, *self.render_size) if\
                            not self.static else pg.Rect(self.x - self.w / 2, self.y + 10 - self.h / 2, *self.render_size), float(self.angle), (self.w / 2., self.h / 2.))
                        
                        if (self.time // 100) % 2 == 1 and self.Focus:
                            if self.text == "":
                                self.texture.draw(None, pg.Rect(self.x + Build.fell.Camera.pos[0] - self.w / 2, self.y + Build.fell.Camera.pos[1] + 7.5 - self.h / 2, *self.texture_size) if\
                                    not self.static else pg.Rect(self.x - self.w / 2, self.y + 7.5 - self.h / 2, *self.texture_size), float(self.angle), (self.w / 2., self.h / 2.))
                            else:
                                self.texture.draw(None, pg.Rect(self.x + Build.fell.Camera.pos[0] + self.render_size[0] - self.w / 2, self.y + Build.fell.Camera.pos[1] + 7.5 - self.h / 2, *self.texture_size) if\
                                    not self.static else pg.Rect(self.x + self.render_size[0] - self.w / 2, self.y + 7.5 - self.h / 2, *self.texture_size), float(self.angle), (self.w / 2., self.h / 2.))
                else:
                    if self.pos[0] + self.render_size[0] > -Build.fell.Camera.pos[0] and self.pos[0] < -Build.fell.Camera.pos[0] + Build.sell.left and \
                        self.pos[1] + self.render_size[1] > -Build.fell.Camera.pos[1] and self.pos[1] < -Build.fell.Camera.pos[1] + Build.sell.up:

                        self.Fon.draw(None, pg.Rect(self.x + Build.fell.Camera.pos[0], self.y + Build.fell.Camera.pos[1], self.w, self.h) if\
                            not self.static else pg.Rect(self.x, self.y, self.w, self.h), float(self.angle), (0, 0))
                        
                        self.render.draw(None, pg.Rect(self.x + Build.fell.Camera.pos[0], self.y + Build.fell.Camera.pos[1] + 10, *self.render_size) if\
                            not self.static else pg.Rect(self.x, self.y + 10, *self.render_size), float(self.angle), (0, 0))
                        
                        if (self.time // 100) % 2 == 1 and self.Focus:
                            if self.text == "":
                                self.texture.draw(None, pg.Rect(self.x + Build.fell.Camera.pos[0], self.y + Build.fell.Camera.pos[1] + 7.5, *self.texture_size) if\
                                    not self.static else pg.Rect(self.x, self.y + 7.5, *self.texture_size), float(self.angle), (0, 0))
                            else:
                                self.texture.draw(None, pg.Rect(self.x + Build.fell.Camera.pos[0] + self.render_size[0], self.y + Build.fell.Camera.pos[1] + 7.5, *self.texture_size) if\
                                    not self.static else pg.Rect(self.x + self.render_size[0], self.y + 7.5, *self.texture_size), float(self.angle), (0, 0))
                
            def event(self, events):
                if events.type == pg.MOUSEBUTTONDOWN:
                    if self.pos[0] - self.size[0] / 2 < events.pos[0] < self.pos[0] + self.size[0] / 2 and self.pos[1] - self.size[1] / 2 < events.pos[1] < self.pos[1] + self.size[1] / 2: self.Focus = True
                    else: self.Focus = False

                if not self.Focus: return
                
                is_changed = False
                if events.type == pg.TEXTINPUT: self.text += events.text; is_changed = True
                elif events.type == pg.KEYDOWN:
                    if events.key == pg.K_BACKSPACE: self.text = self.text[:-1]; is_changed = True
                if is_changed: self._update_texture()
            
            def Clear_Input(self): self.text = ""; self._update_texture()
            
            def get_type(self): return self.type
            def get_text(self): return self.text
            def get_name(self): return self.name
            def get_pos(self): return self.pos
            def get_size(self): return self.size
            def get_angle(self): return self.angle
            def get_static(self): return self.static
            def get_center(self): return self.center
            def set_angle(self, angle): self.angle = angle
            def set_static(self, static): self.static = static
            def set_center(self, center): self.center = center
            def set_pos(self, x, y):
                if ot_pos: x, y = left*x, up*y
                self.pos = [x, y]
            def set_size(self, w, h):
                if ot_pos: w, h = left*w, up*h
                self.size = [w, h]
            def set_text(self, text): self.text = text; self._update_texture()
            
            def _update_texture(self):
                if self.text == "":
                    self.render_text_pg = self.font.render(self.default_text, True, (150, 150, 150))
                    self.render_size = self.render_text_pg.get_size()
                    time_text = self.default_text
                else:
                    self.render_text_pg = self.font.render(self.text, True, (255, 255, 255))
                    self.render_size = self.render_text_pg.get_size()
                    time_text = self.text
                self.metrics = self.font.metrics(time_text)
                if self.render_size[0] > self.w:
                    self.render_text_pg = self.font.render(f"{time_text}...", True, (255, 255, 255))
                    self.render_size = self.render_text_pg.get_size()
                    num = [0, 0]
                    while self.render_size[0] - num[1] > self.w:
                        num[0] += 1
                        num[1] += self.metrics[-num[0]][4]
                    if num[0] > 0: self.render_text_pg = self.font.render(f"{time_text[:-num[0]]}...", True, (255, 255, 255))
                    self.render_size = self.render_text_pg.get_size()
                self.render = pg._sdl2.video.Texture.from_surface(Build.sell.render, self.render_text_pg)
        self.fell.Input_Field = Input_Field

        def SetColor(color): self.sell.render.draw_color = color
        self.fell.SetColor = SetColor

        def AllDrawTexture():
            for t in Build.sell.data_Texture.values():
                if t["tex"].get_type() == "Texture": t["tex"].TextureDraw()
        self.fell.AllDrawTexture = AllDrawTexture

        def AllDrawSquare():
            for t in Build.sell.data_Texture.values():
                if t["tex"].get_type() == "Square": t["tex"].SquareDraw()
        self.fell.AllDrawSquare = AllDrawSquare

        def AllDrawText():
            for t in Build.sell.data_Texture.values():
                if t["tex"].get_type() == "Text": t["tex"].TextDraw()
        self.fell.AllDrawText = AllDrawText

        def AllDrawButton():
            for t in Build.sell.data_Texture.values():
                if t["tex"].get_type() == "Button": t["tex"].ButtonDraw()
        self.fell.AllDrawButton = AllDrawButton

        def AllDrawInputField():
            for t in Build.sell.data_Texture.values():
                if t["tex"].get_type() == "Input_Field": t["tex"].Input_FieldDraw()
        self.fell.AllDrawInputField = AllDrawInputField

        def AllDraw():
            for t in Build.sell.data_Texture.values():
                obj = t["tex"]
                obj_type = obj.get_type()
                if obj_type == "Texture": obj.TextureDraw()
                elif obj_type == "Square": obj.SquareDraw()
                elif obj_type == "Text": obj.TextDraw()
                elif obj_type == "Button": obj.ButtonDraw()
                elif obj_type == "Input_Field": obj.Input_FieldDraw()
        self.fell.AllDraw = AllDraw
        def AllUpdate():
            for t in Build.sell.data_Texture.values():
                obj = t["tex"]
                obj_type = obj.get_type()
                if obj_type == "Texture": obj.TextureUpdate()
                elif obj_type == "Square": obj.SquareUpdate()
                elif obj_type == "Text": obj.TextUpdate()
        self.fell.AllUpdate = AllUpdate

        def MoveMent(keys, Texture, speed):
            keys = keys.split(".")
            if ot_pos:
                if Build.fell.ButtonPressed(keys[0]): Texture.MoveY(up*-speed)
                if Build.fell.ButtonPressed(keys[1]): Texture.MoveX(left*-speed)
                if Build.fell.ButtonPressed(keys[2]): Texture.MoveY(up*speed)
                if Build.fell.ButtonPressed(keys[3]): Texture.MoveX(left*speed)
            else:
                if Build.fell.ButtonPressed(keys[0]): Texture.MoveY(-speed)
                if Build.fell.ButtonPressed(keys[1]): Texture.MoveX(-speed)
                if Build.fell.ButtonPressed(keys[2]): Texture.MoveY(speed)
                if Build.fell.ButtonPressed(keys[3]): Texture.MoveX(speed)
        self.fell.MoveMent = MoveMent

        def event(events, list_obj=[]):
            self = Build
            EV = pg.event.get()
            event_dict = self.sell.Event_List_def(EV)
            
            for ev in EV:
                for obj in list_obj:  obj.event(ev)

                if ev.type == pg.FINGERDOWN:  self.sell.touch[ev.finger_id] = {'x': ev.x, 'y': ev.y}
                elif ev.type == pg.FINGERUP:
                    if ev.finger_id in self.sell.touch:  del self.sell.touch[ev.finger_id]
            
            for btn_name in self.sell.Events:
                obj = self.sell.Events[btn_name]
                if event_dict.get(getattr(obj, 'mouse', '')) and obj.is_hovered(): obj.func()
                    
            for event_name in events.keys():
                if event_dict.get(event_name): events[event_name]()
                
        self.fell.Event = event

        def SetScene(name): self.sell.falt = name
        self.fell.ChangeScene = SetScene

        def ChangeScene(name): self.sell.falt = name
        self.fell.ChangeScene = ChangeScene

        def SetFPSValue(fps): self.sell.FPS = fps
        self.fell.SetFPSValue = SetFPSValue

        def GetFPSValue(): return self.sell.FPS
        self.fell.GetFPSValue = GetFPSValue

        def GetFPS(): return self.sell.Clock.get_fps()
        self.fell.GetFPS = GetFPS

        def ClockUse(stabilize_fps=True):
            if stabilize_fps: self.sell.Clock.tick_busy_loop(self.sell.FPS)
            else: self.sell.Clock.tick(self.sell.FPS)
        self.fell.ClockUse = ClockUse

        class Camera:
            def __init__(self):
                self.pos = [0, 0]

            def SetPos(self, x, y):
                if ot_pos: x, y = left*x, up*y
                self.pos = [x, y]
            def GetPos(self): return self.pos

            def Move(self, x, y): self.pos[0] += x; self.pos[1] += y
            def MoveY(self, y): self.pos[1] += y
            def MoveX(self, x): self.pos[0] += x
        self.fell.Camera = Camera()

        def UpdateButtonPressedHow(): self.sell.PressedButton = pg.key.get_pressed()
        self.fell.UpdateButtonPressedHow = UpdateButtonPressedHow
        self.fell.UBPH = UpdateButtonPressedHow

        def ButtonPressed(key):
            if self.sell.PressedButton is None: print("\033[33mWARNING: ButtonPressedHow not updated\033[0m"); return None
            return self.sell.PressedButton[pg.key.key_code(key)]
        self.fell.ButtonPressed = ButtonPressed

        def Get_DeltaTime(): self.sell.DeltaTimeHow = self.sell.Clock.get_time() / 1000.; return self.sell.DeltaTimeHow
        self.fell.Get_DeltaTime = Get_DeltaTime

        def Get_DeltaTimeHow(): return self.sell.DeltaTimeHow
        self.fell.Get_DeltaTimeHow = Get_DeltaTimeHow

        def ClearCache(): self.sell.data_Texture = {}
        self.fell.ClearCache = ClearCache

        def GetCache(): return self.sell.data_Texture
        self.fell.GetCache = GetCache

        if logs: print("\033[92mcompleted successfully\033[0m")

    def NewScene(self, name, func):
        if self.sell.logs: print(f"\033[90mCreating Scene {name}\033[0m")
        name = str(name)
        if not callable(func): print("\033[31mERROR: Code must be a function\033[0m"); return
        self.sell.Scene[name] = func
        if self.sell.logs: print("\033[92mcompleted successfully\033[0m")
    
    def SetScene(self, name):
        if self.sell.logs: print(f"\033[90mChanging Scene{name}\033[0m")
        self.sell.falt = name
        if self.sell.logs: print("\033[92mcompleted successfully\033[0m")

    def ChangeScene(self, name):
        self.sell.falt = name
        if self.sell.logs: print(f"\033[90mChanging Scene {name}\033[0m")
        if self.sell.logs: print("\033[92mcompleted successfully\033[0m")
    
    def SetSHADES(self, SHADERS):
        if self.sell.logs: print(f"\033[90mSetting SHADERS to {SHADERS}\033[0m")
        self.sell.SHADER = SHADERS
        if self.sell.logs: print("\033[92mcompleted successfully\033[0m")

    def ExecuteOneFrame(self, name=None):
        if self.sell.SafeMode:
            try:
                if self.sell.logs: print(f"\033[90mExecuting a single-frame scene {name if not name is None else self.sell.falt}\033[0m")
                if not name is None: self.sell.Scene[name]()
                else: self.sell.Scene[self.sell.falt]()
                if self.sell.logs: print("\033[92mcompleted successfully\033[0m")
            except: ERROR()
        else:
            if self.sell.logs: print(f"\033[90mExecuting a single-frame scene {name if not name is None else self.sell.falt}\033[0m")
            if not name is None: self.sell.Scene[name]()
            else: self.sell.Scene[self.sell.falt]()
            if self.sell.logs: print("\033[92mcompleted successfully\033[0m")
    def ExecuteOnceCode(self, code):
        if self.sell.SafeMode:
            try:
                if self.sell.logs: print(f"\033[90mExecuting a single-frame code\033[0m")
                if callable(code): 
                    if len(inspect.signature(code).parameters) == 1: code(self.fell)
                    else: print("\033[31mERROR: Code must be a function with one parameter\033[0m"); return
                else: print("\033[31mERROR: Code must be a function\033[0m"); return
                if self.sell.logs: print("\033[92mcompleted successfully\033[0m")
            except: ERROR()
        else:
            if self.sell.logs: print(f"\033[90mExecuting a single-frame code\033[0m")
            if callable(code): 
                if len(inspect.signature(code).parameters) == 1: code(self.fell)
                else: print("\033[31mERROR: Code must be a function with one parameter\033[0m"); return
            else: print("\033[31mERROR: Code must be a function\033[0m"); return
            if self.sell.logs: print("\033[92mcompleted successfully\033[0m")

    def SetSafeMode(self, SafeMode):
        self.sell.SafeMode = SafeMode
        if self.sell.logs: print(f"\033[90mSetting SafeMode to {SafeMode}\033[0m")
    def GetSafeMode(self):
        if self.sell.logs: print(f"\033[90mGetting SafeMode\033[0m")
        return self.sell.SafeMode
    
    def SetLogs(self, logs):
        self.sell.logs = logs
        if self.sell.logs: print(f"\033[90mSetting logs to {logs}\033[0m")
    def GetLogs(self):
        if self.sell.logs: print("\033[90mGetting logs\033[0m")
        return self.sell.logs
    
    def SetDeveLoperMode(self, DeveLoperMode):
        self.sell.DeveLoperMode = DeveLoperMode
        if self.sell.logs: print(f"\033[90mSetting DeveLoperMode to {DeveLoperMode}\033[0m")
    
    def GetDeveLoperMode(self):
        if self.sell.logs: print(f"\033[90mGetting DeveLoperMode\033[0m")
        return self.sell.DeveLoperMode
    
    def SetVsync(self, Vsync):
        self.sell.Vsync = Vsync
        if self.sell.logs: print(f"\033[90mSetting Vsync to {Vsync}\033[0m")
    
    def GetVsync(self):
        if self.sell.logs: print(f"\033[90mGetting Vsync\033[0m")
        return self.sell.Vsync
    
    def GetFell(self):
        if self.sell.logs: print(f"\033[90mGetting Fell\033[0m")
        return self.sell
    def _GetSell(self):
        if self.sell.logs: print(f"\033[90mGetting Sell\033[0m")
        return self.sell
    
    def GetVersion(self):
        if self.sell.logs: print(f"\033[90mGetting Version\033[0m")
        return VERSION
    
    def Destroy(self):
        if self.sell.SafeMode:
            self.sell.war = False
            try:
                if self.sell.logs: print(f"\033[90mDestroying Engine Version {VERSION}\033[0m")
                self.sell.screen.destroy()
                del self.sell.render
            except: print("\033[31mFATAL ERROR: An error has fatal error.\033[0m")
        else:
            if self.sell.logs: print(f"\033[90mDestroying Engine Version {VERSION}\033[0m")
            self.sell.screen.destroy()
            del self.sell.render
    def run(self):
        if self.sell.logs: print(f"\033[90mStarting engine with parameters:\n- SafeMode: {self.sell.SafeMode};\n- logs: {\
            self.sell.logs}\033[0m")
        if self.sell.SafeMode:
            try:
                while self.sell.war: self.sell.Scene[self.sell.falt]()
            except: ERROR()
        else:
            while self.sell.war: self.sell.Scene[self.sell.falt]()
        
        if self.sell.logs: print("\033[90mDone.\033[0m")

class ERROR(Build):
    def __init__(self):
                if self.sell.logs: print("\033[31mFATAL ERROR: An error has fatal error.\033[0m")
                try: self.sell.screen.destroy()
                except: ...
                try: del self.sell.render
                except: ...
                screen = pg.display.set_mode((800, 600))
                pg.display.set_caption("Sorry, it looks like an error has fatal error (TT)")
                left, up = pg.display.Info().current_w, pg.display.Info().current_h
                clock = pg.time.Clock()
                ERROR_Font = pg.font.SysFont(None, int(up / 25.))
                w = True
                e = traceback.format_exc()
                errors = []
                errors.append(e)
                er = "\n".join(errors)
                print(er)
                RENDER = []
                Time = 0
                error_lines = er.splitlines() if er else []
                global VERSION
                fatal_errors = [
                    ("FATAL ERROR   :(", (255, 0, 0)),
                    ("", (0, 0, 0)),
                    (f"Platform: {platform.platform()}", (255, 255, 0)),
                    (f"PyGame version: {pg.__version__}", (255, 255, 0)),
                    (f"Python version: {sys.version}", (255, 255, 0)),
                    (f"_sdl2 version: {".".join(map(str, pg.get_sdl_version()))}", (255, 255, 0)),
                    (f"Engine version: {VERSION}", (255, 255, 0)),
                    ("", (0, 0, 0)),
                    (f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", (255, 255, 0)),
                    ("", (0, 0, 0))
                ]

                while w:
                    for ev in pg.event.get():
                        if not error_lines and not fatal_errors:
                            if ev.type == pg.QUIT: w = False; break
                            if ev.type == pg.KEYDOWN: w = False; break
                            if ev.type == pg.MOUSEBUTTONDOWN: w = False; break
                    screen.fill((0, 0, 0))
                    for index in range(len(RENDER)):
                        render = RENDER[index]
                        #screen.blit(render, (left / 2 - render.get_size()[0] / 2, index * render.get_size()[1]))
                        screen.blit(render, (0., index * render.get_size()[1]))
                    
                    if Time == 0:
                        Time = 0
                        if fatal_errors:
                            text, color = fatal_errors[0]
                            RENDER.append(ERROR_Font.render(text, True, color))
                            del fatal_errors[0]
                        elif error_lines:
                            RENDER.append(ERROR_Font.render(error_lines[0], True, (255, 255, 255)))
                            del error_lines[0]
                    else: Time += 1
                    clock.tick(30)
                    pg.display.update()