from colorsys import hls_to_rgb, rgb_to_hls, rgb_to_hsv, hsv_to_rgb
from random import uniform, randint

DEFAULT_LIGHTNESS = 0.5
DEFAULT_SATURATION = 1
DEFAULT_VARIANCE = 0.2


def get_next_HSV_color(cur_color, offset_hue=0, offset_value=0, offset_saturation=0):
    red = int(cur_color[0:2], base=16)
    green = int(cur_color[2:4], base=16)
    blue = int(cur_color[4:6], base=16)

    hue, saturation, value = rgb_to_hsv(red, green, blue)
    new_hue = hue + offset_hue
    new_value = value + offset_value
    new_saturation = saturation - offset_saturation

    # new_hue = hue + offset_hue
    # new_lightness = lightness + offset_lightness
    # new_saturation = saturation + offset_saturation

    # red, green, blue = map(
    #     lambda v: int(v * 255),
    #     hls_to_rgb(
    #         new_hue,
    #         new_lightness,
    #         new_saturation,
    #     ),
    # )

    red, green, blue = hsv_to_rgb(new_hue, new_saturation, new_value)

    # red, green, blue = hls_to_rgb(hue_variant, lightness, saturation)
    res = f"{int(red):02x}{int(green):02x}{int(blue):02x}"

    return res


def get_next_color(cur_color, offset_hue=0, offset_lightness=0, offset_saturation=0):
    red = int(cur_color[0:2], base=16)
    green = int(cur_color[2:4], base=16)
    blue = int(cur_color[4:6], base=16)

    hue, lightness, saturation = rgb_to_hls(red, green, blue)
    lightness = lightness / 255
    if saturation < 0.1:
        saturation = 1

    new_hue = hue + offset_hue
    new_lightness = lightness + offset_lightness
    new_saturation = saturation + offset_saturation

    if new_hue > 1: new_hue = offset_hue
    if new_hue < 0: new_hue = 1
    if new_lightness > 1: new_lightness = offset_lightness
    if new_lightness < 0: new_lightness = 1
    if new_saturation > 1: new_saturation = offset_saturation
    if new_saturation < 0: new_saturation = 1

    red, green, blue = map(
        lambda v: int(v * 255),
        hls_to_rgb(
            new_hue,
            new_lightness,
            new_saturation,
        ),
    )

    res = f"{red:02x}{green:02x}{blue:02x}"

    return res


class Huetify(object):
    lightness: float
    saturation: float
    variance: float
    half_variance: float

    def __init__(
            self,
            lightness=DEFAULT_LIGHTNESS,
            saturation=DEFAULT_SATURATION,
            variance=DEFAULT_VARIANCE,
    ) -> None:
        self.lightness = lightness
        self.saturation = saturation
        self.variance = variance
        self.half_variance = variance / 2.0

    def huetify_to_rgb_hex(self, hue) -> str:
        hue_variant = uniform(
            hue - self.half_variance,
            hue + self.half_variance,
        )
        red, green, blue = map(
            lambda v: int(v * 255),
            hls_to_rgb(
                hue_variant,
                self.lightness,
                self.saturation,
            ),
        )
        return f"{red:02x}{green:02x}{blue:02x}"

    def huetify_next_variant_to_rgb_hex(self, cur_variant):
        hue_variant = cur_variant + self.half_variance
        red, green, blue = map(
            lambda v: int(v * 255),
            hls_to_rgb(
                hue_variant,
                self.lightness,
                self.saturation,
            ),
        )
        return red, green, blue

    @property
    def reddish(self):
        return self.huetify_to_rgb_hex(0)

    @property
    def greenish(self):
        return self.huetify_to_rgb_hex(0.333)

    @property
    def blueish(self):
        return self.huetify_to_rgb_hex(0.666)

    def blue_colors(self, cur_variant=None):
        if not cur_variant:
            cur_variant = 0.666 - self.half_variance
        return self.huetify_next_variant_to_rgb_hex(cur_variant=cur_variant)

    @property
    def yellowish(self):
        return self.huetify_to_rgb_hex(0.166)

    @property
    def random_color(self):
        ch = randint(1, 4)
        if ch == 1:
            return self.reddish
        elif ch == 2:
            return self.greenish
        elif ch == 3:
            return self.greenish
        else:
            return self.yellowish
