from ursina import *


@generate_properties_for_class()
class Slider(Entity):
    def __init__(self, min=0, max=1, default=None, height=Text.size, text='', dynamic=False, radius=Text.size/2, bar_color=color.black66, **kwargs):
        super().__init__(add_to_scene_entities=False) # add later, when __init__ is done
        self.parent = camera.ui
        self.vertical = False
        self.min = min
        self.max = max

        if default is None:
            default = min
        self.default = default
        self.step = 0                   # for example, setting .step to 1, would make the slider snap to the closest integer.
        self.height = height

        self.on_value_changed = None    # set this to a function you want to be called when the slider changes
        self.setattr = None             # set this to (object, 'attrname') to set that value when the slider changes

        self.label = Text(parent=self, origin=(0.5, 0), x=-0.025, text=text)

        self.bg = Entity(parent=self, model=Quad(scale=(.525, height), radius=radius, segments=3),
            origin_x=-0.25, collider='box', color=bar_color)

        self.knob = Draggable(parent=self, min_x=0, max_x=.5, min_y=0, max_y=.5, step=self.step,
            model=Quad(radius=Text.size/2, scale=(Text.size, height)), collider='box', color=color.light_gray,
            text='0', text_origin=(0, -.55), z=-.1)

        def bg_click():
            self.knob.x = mouse.point[0]
            self.knob.start_dragging()
        self.bg.on_click = bg_click

        def drop():
            self.knob.z = -.1
            if self.setattr:
                if isinstance(self.setattr[0], dict):   # set value of dict
                    self.setattr[0][self.setattr[1]] = self.value
                else:                                   # set value of Entity
                    setattr(self.setattr[0], self.setattr[1], self.value)

            if self.on_value_changed:
                self.on_value_changed()


        self.knob.drop = drop
        self._prev_value = self.default
        self.value = self.default
        self.dynamic = dynamic    # if set to True, will call on_value_changed() while dragging. if set to False, will only call on_value_changed() after dragging.


        self.knob.text_entity.text = str(round(self.default, 2))

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

        if self.vertical:
            self.rotation_z = -90
            self.knob.lock = (1,0,0)
            self.knob.text_entity.rotation_z = 90
            self.knob.text_entity.position = (.015,0)
        else:
            self.knob.lock = (0,1,1)
            self.knob.text_entity.y = height/2

        scene.entities.append(self)


    def value_getter(self):
        val = lerp(self.min, self.max, self.knob.x * 2)
        if isinstance(self.step, int) and not self.step == 0:
            val = int(round(val, 0))

        return val

    def value_setter(self, value, call_on_value_changed=True):
        self.knob.x = (value - self.min) / (self.max - self.min) / 2
        self.slide(call_on_value_changed=call_on_value_changed)


    def step_setter(self, value):
        self._step = value
        self.knob.step = value / (self.max-self.min) / 2

    def update(self):
        if self.knob.dragging:
            self.slide()

    def slide(self, call_on_value_changed=True):
        t = self.knob.x / .5

        if self.step > 0:
            if isinstance(self.step, int) or self.step.is_integer():
                self.knob.text_entity.text = str(self.value)

        if call_on_value_changed and self.dynamic and self._prev_value != t:
            if self.on_value_changed:
                self.on_value_changed()

            if self.setattr:
                target_object, attr = self.setattr
                setattr(target_object, attr, self.value)

            self._prev_value = t

        invoke(self._update_text, delay=1/60)

    def _update_text(self):
            self.knob.text_entity.text = str(round(self.value, 2))


    def __setattr__(self, name, value):
        if name == 'eternal':
            try:
                self.label.eternal = value
                self.bg.eternal = value
                self.knob.eternal = value
            except:
                pass
        try:
            super().__setattr__(name, value)
        except Exception as e:
            return e



class ThinSlider(Slider):
    def __init__(self, *args, **kwargs):
        kwargs['height'] = Text.size
        super().__init__(*args, **kwargs)
        self.bg.model = Quad(scale=(.525, Text.size/5), radius=Text.size/10, segments=3)
        self.bg.origin = self.bg.origin
        self.bg.color = color.text_color
        self.bg.highlight_color = color.text_color
        self.knob.color = lerp(color.text_color, color.inverse(color.text_color), .1)
        self.label.color = color.text_color




if __name__ == '__main__':
    app = Ursina()

    box = Entity(model='cube', origin_y=-.5, scale=1, color=color.orange)

    def scale_box():
        box.scale_y = slider.value
        print(thin_slider.value)

    slider = Slider(0, 20, default=10, height=Text.size*3, y=-.4, step=1, on_value_changed=scale_box, vertical=True)

    thin_slider = ThinSlider(text='height', dynamic=True, on_value_changed=scale_box)

    thin_slider.label.origin = (0,0)
    thin_slider.label.position = (.25, -.1)

    app.run()
