# adjuster.py Tiny control knob (rotary potentiometer) widget

# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2021 Peter Hinch

from gui.core.ugui import LinearIO, display
from gui.widgets.label import Label
import math

TWOPI = 2 * math.pi
# Null function
dolittle = lambda *_: None

# *********** CONTROL KNOB CLASS ***********


class Adjuster(LinearIO):
    def __init__(self, writer, row, col, *,
                 value=0.0, fgcolor=None, bgcolor=None, color=None,
                 prcolor=None, callback=dolittle, args=[]):
        height = writer.height  # Match a user-linked Label
        super().__init__(writer, row, col, height, height,
                         fgcolor, bgcolor, False, value,
                         True, prcolor)
        super()._set_callbacks(callback, args)
        radius = height / 2
        self.arc = 1.5 * math.pi  # Usable angle of control
        self.radius = radius
        self.xorigin = col + radius
        self.yorigin = row + radius
        self.color = color
        self.draw = True  # Ensure a redraw on next refresh
        # Run callback (e.g. to set dynamic colors)
        self.callback(self, *self.args)

    def show(self):
        if super().show(False):  # Honour bgcolor
            arc = self.arc
            radius = self.radius
            if self.color is not None:
                display.fillcircle(self.xorigin, self.yorigin, radius, self.color)
            display.circle(self.xorigin, self.yorigin, radius, self.fgcolor)
            display.circle(self.xorigin, self.yorigin, radius, self.fgcolor)
            self._drawpointer(self._value, self.fgcolor)  # draw new

    def _drawpointer(self, value, color):
        arc = self.arc
        length = self.radius - 1
        angle = value * arc - arc / 2
        x_end = int(self.xorigin + length * math.sin(angle))
        y_end = int(self.yorigin - length * math.cos(angle))
        display.line(int(self.xorigin), int(self.yorigin), x_end, y_end, color)

# This class combines an Adjuster with one or two labels. Numerous layout
# options exist: users may wish to write a version of this example with
# different visual characteristics.
# The map_func enables each instance to have its own mapping of Adjuster value
# to perform offset, scaling, log mapping etc.
# The object's value is that of the Adjuster, in range 0.0-1.0. The scaled
# value is retrieved with .mapped_value()
class FloatAdj(Adjuster):
    def __init__(self, wri, row, col, *,
                 lbl_width=60, value=0.0, color=None,
                 fstr="{:4.2f}", map_func=lambda v: v, text="",
                 callback=dolittle, args=[]):

        self.map_func = map_func
        self.facb = callback
        self.fargs = args
        self.fstr = fstr

        self.lbl = Label(wri, row, col, lbl_width, bdcolor=color)
        super().__init__(wri, row, self.lbl.mcol + 2, value=value,
                         fgcolor=color, callback=self.cb)
        if text:
            self.legend = Label(wri, row, self.mcol + 2, text)
            self.mcol = self.legend.mcol  # For relative positioning

    def cb(self, adj):  # Runs on init and when value changes
        self.lbl.value(self.fstr.format(self.mapped_value()))
        self.facb(self, *self.fargs)  # Run user callback

    def mapped_value(self):
        return self.map_func(self.value())
