
# This file contains the slider widget

import 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

        
