from ursina import Entity, Text, camera, color, mouse, BoxCollider, Sequence, Func, Vec2, Vec3, scene, Default, Audio
from ursina import color as color_module
from ursina.models.procedural.quad import Quad
from ursina.shaders.unlit_shader import unlit_shader

from ursina.scripts.property_generator import generate_properties_for_class

@generate_properties_for_class()
class Button(Entity):

    default_color = color.black90
    default_highlight_color = None
    default_model = Quad
    default_radius = .1
    default_texture = ''

    def __init__(self,
        text='', parent=camera.ui, model=Default, radius=Default, origin=(0,0), texture=Default, color=Default, collider='box',
            text_color=Default, text_origin=(0,0), text_size=1, highlight_text_size=None, highlight_text_color=None,
            highlight_scale=1, pressed_scale=1,
            disabled=False, shader=unlit_shader, **kwargs):
        super().__init__(parent=parent)

        for key in ('scale', 'scale_x', 'scale_y', 'scale_z', 'world_scale', 'world_scale_x', 'world_scale_y', 'world_scale_z'):
            if key in kwargs:   # set the scale before model for correct corners
                setattr(self, key, kwargs[key])

        self.radius = Button.default_radius if radius is Default else radius
        self.model = Button.default_model if model is Default else model

        self.origin = origin

        self.texture = Button.default_texture if texture is Default else texture
        self.color = color = Button.default_color if color is Default else color

        self.highlight_color = self.color.tint(.2) if Button.default_highlight_color is None else Button.default_highlight_color
        self.pressed_color = self.color.tint(-.2)
        self.highlight_scale = highlight_scale    # multiplier
        self.pressed_scale = pressed_scale     # multiplier
        self.highlight_sound = None
        self.pressed_sound = None
        self.highlight_text_size = highlight_text_size if highlight_text_size is not None else text_size
        self.collider = collider
        self.disabled = disabled    # Used for temporary deactivating buttons without making them invisible, and still have collision.

        self.text_entity = None
        self.text_origin = text_origin
        text_color = text_color if text_color is not Default else color_module.text_color
        self.highlight_text_color = highlight_text_color if highlight_text_color is not None else text_color
        if text:
            self.text = text
        self.text_size = text_size
        self.text_color = text_color

        for key, value in kwargs.items():
            setattr(self, key, value)


    def text_getter(self):
        if self.text_entity:
            return self.text_entity.text
        return ''

    def text_setter(self, value):
        if not isinstance(value, str):
            raise TypeError('Text must be a string')

        if not self.text_entity:
            self.text_entity = Text(text=value, parent=self.model, position=Vec3(self.text_origin[0],self.text_origin[1],-.01), origin=self.text_origin, add_to_scene_entities=False)
            self.text_entity.world_parent = self
            self.text_entity.world_scale = Vec3(20 * self.text_size)

        else:
            self.text_entity.text = value


    def text_origin_getter(self):
        if self.text_entity:
            return self.text_entity.origin

        return getattr(self, '_text_origin', (0,0))

    def text_origin_setter(self, value):
        self._text_origin = value
        if not self.text_entity:
            return

        self.text_entity.origin = value
        self.text_entity.world_parent = self.model
        self.text_entity.position = value
        # self.text_entity.x += self.model.radius * self.scale_y/self.scale_x * (-value[0]*2)
        # self.text_entity.y += self.model.radius * self.scale_y/self.scale_x * (-value[1]*2)
        self.text_entity.origin = value
        self.text_entity.world_parent = self


    def text_color_setter(self, value, temp=False):
        if not temp:
            self._text_color = value
        if self.text_entity:
            self.text_entity.color = value

    def icon_getter(self):
        return getattr(self, 'icon_entity', None)

    def icon_setter(self, value):
        if value and not hasattr(self, 'icon_entity'):
            self.icon_entity = Entity(parent=self.model, name=f'button_icon_entity_{value}', model='quad', z=-.1, add_to_scene_entities=False)
        self.icon_entity.texture = value
        longest_side = max(self.icon_entity.texture.width, self.icon_entity.texture.height)
        aspect_ratio = self.icon_entity.texture.width / self.icon_entity.texture.height
        if aspect_ratio == 1:
            return

        if self.icon_entity.texture.width < self.icon_entity.texture.height:
            self.icon_entity.scale_x = self.icon_entity.scale_y * aspect_ratio
        else:
            self.icon_entity.scale_y = self.icon_entity.scale_x / aspect_ratio


    def icon_world_scale_getter(self):
        if not self.icon:
            return None
        return self.icon_entity.world_scale

    def icon_world_scale_setter(self, value):
        if self.icon:
            self.icon_entity.world_scale = value

    def text_size_getter(self):
        return getattr(self, '_text_size', 1)

    def text_size_setter(self, value, temp=False):
        if not temp:
            self._text_size = value
        if self.text_entity:
            self.text_entity.world_scale = Vec3(value * 20)


    def origin_getter(self):
        return getattr(self, '_origin', Vec3.zero)

    def origin_setter(self, value):
        if hasattr(self, 'text_entity') and self.text_entity:
            self.text_entity.world_parent = self.model
            super().origin_setter(value)
            self.text_entity.world_parent = self
        else:
            super().origin_setter(value)

        if isinstance(self.collider, BoxCollider):    # update collider position by making a new one
            self.collider = 'box'



    def input(self, key):
        if self.disabled or not self.model:
            return

        if key == 'left mouse down':
            if self.hovered:
                self.model.setColorScale(self.pressed_color)
                self.model.setScale(Vec3(self.pressed_scale, self.pressed_scale, 1))
                if self.pressed_sound:
                    if isinstance(self.pressed_sound, Audio):
                        self.pressed_sound.play()
                    elif isinstance(self.pressed_sound, str):
                        Audio(self.pressed_sound, auto_destroy=True, ignore_paused=self.ignore_paused)

        if key == 'left mouse up':
            if self.hovered:
                self.model.setColorScale(self.highlight_color)
                self.model.setScale(Vec3(self.highlight_scale, self.highlight_scale, 1))
            else:
                self.model.setColorScale(self.color)
                self.model.setScale(Vec3(1,1,1))


    def on_mouse_enter(self):   # Handles color tinting, scale change, audio and Tooltip when hovering the Button
        if not self.disabled:
            if self.model:
                self.model.setColorScale(self.highlight_color)
                if self.highlight_scale != 1:
                    self.model.setScale(Vec3(self.highlight_scale, self.highlight_scale, 1))

            if self.text:
                self.text_size_setter(self.highlight_text_size, temp=True)
                self.text_color_setter(self.highlight_text_color, temp=True)

            if self.highlight_sound:
                if isinstance(self.highlight_sound, Audio):
                    self.highlight_sound.play()
                elif isinstance(self.highlight_sound, str):
                    Audio(self.highlight_sound, auto_destroy=True, ignore_paused=self.ignore_paused)

        if hasattr(self, 'tooltip') and self.tooltip:
            self.tooltip.enabled = True


    def on_mouse_exit(self):    # Handles color tinting, scale change, audio and Tooltip when unhovering the Button
        if not self.disabled:
            if self.model:
                self.model.setColorScale(self.color)
                if not mouse.left and self.highlight_scale != 1:
                    self.model.setScale(Vec3(1,1,1))

            if self.text:
                self.text_size_setter(self.text_size, temp=True)
                self.text_color_setter(self.text_color, temp=True)

        if hasattr(self, 'tooltip') and self.tooltip:
            self.tooltip.enabled = False


    def fit_to_text(self, radius=.1, padding=Vec2(Text.size*1.5, Text.size)):
        if not self.text_entity or not self.text_entity.text or self.text_entity.text == '':
            return

        self.text_entity.world_parent = scene
        self.original_parent = self.parent
        self.parent = self.text_entity
        self.scale = Vec2(self.text_entity.width*self.text_entity.world_scale_x, self.text_entity.height*self.text_entity.world_scale_y) * Text.size * 2
        # self.scale = Vec2(self.text_entity.width, self.text_entity.height) * Text.size * 2
        self.scale += Vec2(*padding)
        self.position += self.text_origin * self.scale.xy * .5

        self.model = self.model.__class__
        self.parent = self.original_parent
        self.text_entity.world_parent = self



if __name__ == '__main__':
    from ursina import *
    app = Ursina()

    # # test settging default colors
    # Button.default_highlight_color = color.blue
    Button.default_color = color.white
    Button.default_model = NineSlice
    Button.default_texture = 'nineslice_rainbow'
    Button.default_radius = .5

    b = Button(model='quad', scale=.05, x=-.5, color=color.lime, text='text_size\ntest', text_size=.5, text_color=color.black)
    b.on_click = Sequence(Wait(.5), Func(print, 'aaaaaa'), )

    b = Button(parent=camera.ui, text='hello world!', scale=.25)
    b = Button(text='hello world!', icon='sword', scale=.25, text_origin=(-.5,0), x=.5)
    # b.fit_to_text()
    b.on_click = application.quit # assign a function to the button.
    b.tooltip = Tooltip('exit')

    par = Entity(parent=camera.ui, scale=.2, y=-.2)
    b = Button(parent=par, text='test', scale_x=1, origin=(-.5,.5))
    b.text ='new text'
    print(b.text_entity)

    Button(text='sound', scale=.2, position=(-.25,-.2), color=color.pink, highlight_sound='blip_1', pressed_sound=Audio('coin_1', autoplay=False))

    Button('highlight\ntest', scale=(.2,.1), highlight_color=color.magenta, highlight_text_color=color.cyan, highlight_scale=1.2, highlight_text_size=1.2, position=(.2))
    # Entity(model='quad', parent=b, x=1)
    Text('Text size\nreference', x=.15)
    # b.eternal = True
    def input(key):

        if key == 'd':
            scene.clear()

        if key == 'space':
            b.text = 'updated text'

    Sky()
    app.run()
