
# This file contains the slider widget

impo
rt pygame
from transcendence.graphics.widget import Box
from transcendence.graphics.button import Button
from transcendence.graphics.progressbar import ProgressBar
import transcendence.graphics as graphics
from transcendence import util

class Slider(Box):
    """A slider widget presents a stepped sliding scale, beginning at min_value and ending at max_value
.
    min_value can be the larger one to have an inverted scale. The slider can be horizontal or vertical."""

    def __init__(self, size, report, **kwargs):
        background = kwargs.pop("background", graphics.colors.background)
        borders = kwargs.pop("borders", ())
        self._min_value = kwargs.pop("min_value", 0)
        self._max_value = kwargs.pop("max_value", 100)
        self._current_value = kwargs.pop("current_value", 0)
        self._slider_units = kwargs.pop("slider_units", 5)
        self.orientation = kwargs.pop("orientation", graphics.HORIZONTAL)
        self.slider_color = kwargs.pop("slider_color", graphics.colors.med_highlight)
        self.slider_hover_color = kwargs.pop("slider_hover_color", graphics.colors.strong_highlight)
        super().__init__(size, background=background, borders=borders, **kwargs)
        self.report = report
        self.recalculate_slider()
        self.hover_over_slider = False

    def add_own_handlers(self):
        self.parent.add_mouse_handler(self, None, self.mouse_leave, self.mouse_move, self.mouse_button_press, None)

    def remove_own_handlers(self):
        self.parent.remove_mouse_handler(self)

    def recalculate_slider(self):
        self.slider_rect = pygame.Rect(0,0,0,0)
        self.recalculate_slider_size()
        self.recalculate_slider_pos()

    def recalculate_slider_pos(self):
        i = 0 if self.orientation == graphics.HORIZONTAL else 1
        pos = [0,0]
        if self.max_value != self.min_value:
            frac = (self.current_value - self.min_value) / (self.max_value - self.min_value)
            pos[i] = int(frac * (self.size[i] - self.slider_size[i]))
        self.slider_pos = tuple(pos)
        self.slider_rect.topleft = self.slider_pos

    def recalculate_slider_size(self):
        i = 0 if self.orientation == graphics.HORIZONTAL else 1
        size = list(self.size)
        rel = self.slider_units / (abs(self.max_value - self.min_value) + self.slider_units)
        size[i] = int(rel * self.size[i])
        self.slider_size = tuple(size)
        self.slider_rect.size = self.slider_size

    def current_value_changed(self):
        self.recalculate_slider_pos()
        self.report(self.current_value)
        self.needs_redraw = True

    def limits_changed(self):
        top = max(self.min_value, self.max_value)
        bottom = min(self.min_value, self.max_value)
        self.current_value = max(bottom, min(top, self.current_value))
        self.recalculate_slider_pos()
        self.recalculate_slider_size()
        self.needs_redraw = True

    slider_units = util.call_on_change("_slider_units", recalculate_slider)
    min_value = util.call_on_change("_min_value", limits_changed)
    max_value = util.call_on_change("_max_value", limits_changed)
    current_value = util.call_on_change("_current_value", current_value_changed)

    def find_value(self, x):
        """Given an x (or y) coordinate in global coordinates, find the value this represents."""
        if self.min_value == self.max_value:
            return self.min_value
        i = 0 if self.orientation == graphics.HORIZONTAL else 1
        lx = x - self.slider_size[i] // 2
        width = self.size[i] - self.slider_size[i]
        lx = min(width, max(0, lx))
        return int(lx / width * (self.max_value - self.min_value) + self.min_value)

    def mouse_button_press(self, mx, my, button):
        if button == 1:
            self.current_value = self.find_value(mx if self.orientation == graphics.HORIZONTAL else my)
            self.hover_over_slider = True
            return True
        elif button == 4: # Mouse wheel up
            self.step(self.orientation != graphics.HORIZONTAL, 10)
            return True
        elif button == 5:
            self.step(self.orientation == graphics.HORIZONTAL, 10)
            return True
        return False

    def mouse_move(self, mx, my, dx, dy, lb):
        if lb:
            self.current_value = self.find_value(mx if self.orientation == graphics.HORIZONTAL else my)
        if self.slider_rect.collidepoint(mx, my) != self.hover_over_slider:
            self.hover_over_slider = not self.hover_over_slider
            self.needs_redraw = True

    def mouse_leave(self, mx, my):
        if self.hover_over_slider:
            self.hover_over_slider = False
            self.needs_redraw = True

    def step(self, left=True, dist = 1):
        """Increment or decrement the current value by 1"""
        s = util.sign(self.max_value - self.min_value)
        if not left:
            s = -s
        top = max(self.min_value, self.max_value)
        bottom = min(self.min_value, self.max_value)
        self.current_value = max(bottom, min(top, self.current_value - s * dist))

    def redraw_self(self):
        if self.background:
            self.surface.fill(self.background)

        col = self.slider_hover_color if self.hover_over_slider else self.slider_color
        self.surface.fill(col, self.slider_pos + self.slider_size)

        self.draw_borders()

class BarSlider(ProgressBar):

    def __init__(self, size, report, **kwargs):
        # maximum value that can be set by clicking, for research sliders
        self.clamp_value = kwargs.get("clamp_value", None)

        self.hover_background = kwargs.get("hover_background", graphics.colors.background)
        self.hover_bar_color = kwargs.get("hover_bar_color", graphics.colors.strong_highlight)
        self.hover_text_color = kwargs.get("hover_text_color", graphics.colors.white)
        super().__init__(size, **kwargs)
        self.regular_bar_color = self.bar_color
        self.regular_background = self.background
        self.regular_text_color = self.label.text_color if self.label else None
        self.inside = False
        self.report = report

    def add_own_handlers(self):
        self.parent.add_mouse_handler(self, self.mouse_enter, self.mouse_leave, self.mouse_move,
            self.mouse_button_press, None)

    def remove_own_handlers(self):
        self.parent.remove_mouse_handler(self)

    def current_value_changed(self):
        self.recalculate_bar()
        self.report(self.current_value)

    current_value = util.call_on_change("_current_value", current_value_changed)

    def set_colors(self):
        if self.inside:
            self.background = self.hover_background
            self.bar_color = self.hover_bar_color
            if self.label:
                self.label.text_color = self.hover_text_color
        else:
            self.background = self.regular_background
            self.bar_color = self.regular_bar_color
            if self.label:
                self.label.text_color = self.regular_text_color

    def mouse_enter(self, mx, my, lb):
        self.inside = True
        self.set_colors()

    def mouse_leave(self, mx, my):
        self.inside = False
        self.set_colors()

    def find_value(self, x):
        if self.min_value == self.max_value:
            return self.min_value
        i = 0 if self.orientation == graphics.HORIZONTAL else 1
        lx = min(self.size[i], max(0, x))
        val = int(lx / self.size[i] * (self.max_value - self.min_value) + self.min_value)
        if self.clamp_value and abs(val - self.min_value) > abs(self.clamp_value - self.min_value):
            return self.clamp_value
        else:
            return val

    def mouse_move(self, mx, my, dx, dy, lb):
        if not lb:
            return
        self.current_value = self.find_value(mx if self.orientation == graphics.HORIZONTAL else my)

    def mouse_button_press(self, mx, my, button):
        if button == 1:
            self.current_value = self.find_value(mx if self.orientation == graphics.HORIZONTAL else my)
            return True
        return False

      