# -*- coding: utf-8 -*-

# -*- coding: utf-8 -*-

import colorsys
import numpy

class Color(object):

    red = None
    green = None
    blue = None

    hue = None
    saturation = None
    value = None

    alpha = None

    shortcuts = { "h":"hue", "s":"saturation", "v":"value", "r":"red", "g":"green", "b":"blue" }
    attributes_range = { "hue":360, "saturation":1, "value":1, "red":255, "green":255, "blue":255 }

    def __init__(self, red=None, green=None, blue=None, hue=None, saturation=None, value=None, alpha=None):

        rgb_passed = bool(red)|bool(green)|bool(blue)
        hsv_passed = bool(hue)|bool(saturation)|bool(value)

        if not alpha:
            alpha = 0.0

        if rgb_passed and hsv_passed:
            raise ValueError("Color can't be initialized with RGB and HSV at the same time.")

        elif hsv_passed:

            if not hue:
                hue = 0.0
            if not saturation:
                saturation = 0.0
            if not value:
                value = 0.0

            super(Color, self).__setattr__('hue', hue)
            super(Color, self).__setattr__('saturation', saturation)
            super(Color, self).__setattr__('value', value)
            self._update_rgb()

        else:

            if not red:
                red = 0
            if not green:
                green = 0
            if not blue:
                blue = 0

            super(Color, self).__setattr__('red', red)
            super(Color, self).__setattr__('green', green)
            super(Color, self).__setattr__('blue', blue)
            self._update_hsv()

        super(Color, self).__setattr__('alpha', alpha)


    def __getattr__(self, key):

        if key == "hue" or key == "h": return self.hue
        if key == "saturation" or key == "s": return self.saturation
        if key == "value" or key == "v": return self.value
        if key == "red" or key == "r": return self.red
        if key == "green" or key == "g": return self.green
        if key == "blue" or key == "b": return self.blue
        else: return None

    def translate_shortcut( self, p_shortcut ):
        return self.shortcuts[ p_shortcut ]

    def get_attributes_range( self, key ):
        return self.attributes_range[ key ]

    def __setattr__(self, key, value):

        if key in ('red', 'green', 'blue'):
            if value > 255.0:
                value = value % 255.0
            super(Color, self).__setattr__(key, value)
            self._update_hsv()

        elif key in ('hue', 'saturation', 'value'):
            if key == 'hue' and (value >= 360.0 or value < 0):
                value = value % 360.0
            elif key != 'hue' and value > 1.0:
                value = 1.0
            super(Color, self).__setattr__(key, value)
            self._update_rgb()

        else:
            if key == 'alpha' and value > 1.0: # TODO: Might this be more fitting in another place?
                value = 1.0

            super(Color, self).__setattr__(key, value)


    def __repr__(self):

        return '<%s: red %f, green %f, blue %f, hue %f, saturation %f, value %f, alpha %f>' % (
                self.__class__.__name__,
                self.red,
                self.green,
                self.blue,
                self.hue,
                self.saturation,
                self.value,
                self.alpha
            )


    def __str__(self):
        return "%d %d %d" % (
            int(round(self.red * self.alpha)),
            int(round(self.green * self.alpha)),
            int(round(self.blue * self.alpha)),
        )


    def blend(self, other, mode='normal'):

        if self.alpha != 1.0: # no clue how to blend with a translucent bottom layer
            self.red = self.red * self.alpha
            self.green = self.green * self.alpha
            self.blue = self.blue * self.alpha

            self.alpha = 1.0

        if mode == 'normal':
            own_influence = 1.0 - other.alpha
            self.red = (self.red * own_influence) + (other.red * other.alpha)
            self.green = (self.green * own_influence) + (other.green * other.alpha)
            self.blue = (self.blue * own_influence) + (other.blue * other.alpha)


    def lighten(self, other):

        if isinstance(other, int) or isinstance(other, float):
            other = Color(red=other, green=other, blue=other, alpha=1.0)

        if self.alpha != 1.0:
            self.red = self.red * self.alpha
            self.green = self.green * self.alpha
            self.blue = self.blue * self.alpha

            self.alpha = 1.0

        red = self.red + (other.red * other.alpha)
        green = self.green + (other.green * other.alpha)
        blue = self.blue + (other.blue * other.alpha)

        if red > 255.0:
            red = 255.0

        if green > 255.0:
            green = 255.0

        if blue > 255.0:
            blue = 255.0

        self.red = red
        self.green = green
        self.blue = blue


    def darken(self, other):

        if isinstance(other, int) or isinstance(other, float):
            other = Color(red=other, green=other, blue=other, alpha=1.0)

        red = self.red - other.red
        green = self.green - other.green
        blue = self.blue - other.blue

        if red < 0:
            red = 0

        if green < 0:
            green = 0

        if blue < 0:
            blue = 0

        self.red = red
        self.green = green
        self.blue = blue


    def hex(self):
        return "%.2X%.2X%.2X" % (self.red, self.green, self.blue)

    def _update_hsv(self):

        hue, saturation, value = colorsys.rgb_to_hsv(self.red/255.0, self.green/255.0, self.blue/255.0)
        super(Color, self).__setattr__('hue', hue * 360.0)
        super(Color, self).__setattr__('saturation', saturation)
        super(Color, self).__setattr__('value', value)


    def _update_rgb(self):

        red, green, blue = colorsys.hsv_to_rgb(self.hue / 360.0, self.saturation, self.value)
        super(Color, self).__setattr__('red', red * 255.0)
        super(Color, self).__setattr__('green', green * 255.0)
        super(Color, self).__setattr__('blue', blue * 255.0)

    def copy(self):
        return Color( self.r, self.g, self.b )

    def rgb_tripel(self, p_int=False):
        if p_int:
            return (int(self.red), int(self.green), int(self.blue))
        else:
            return (self.red, self.green, self.blue)


def bin_ranges(length, num_bins):

        length = numpy.float64(length)
        bin_range = []
        current_range = length / (2 ** num_bins -1)

        lower = 0
        upper = current_range -1
        if upper < lower:
            upper = lower
        bin_range.append([int(round(lower)), int(round(upper))])

        for i in range(1, num_bins):

            current_range *=2

            lower = upper + 1
            upper = lower + current_range - 1
            if upper < lower:
                upper = lower

            bin_range.append([int(round(lower)), int(round(upper))])

        return bin_range


def octave_amplitudes(amplitudes, num_bins):

    bins = numpy.zeros(num_bins)
    i = 0
    ranges = bin_ranges(len(amplitudes), num_bins)
    for lower, upper in ranges:

        if lower == upper:
            bin = amplitudes[lower]

        else:
            #bin = amplitudes[lower:upper].mean()
            #bin = sum(amplitudes[lower:upper]) / float(len(amplitudes[lower:upper])) # average instead of mean
            #bin = numpy.max(amplitudes[lower:upper]) # this is an ugly hack instead of doing, like, statistical analysis or anything.
            bin = (numpy.max(amplitudes[lower:upper]) + numpy.mean(amplitudes[lower:upper])) / 2 # mean and max mixed

        if numpy.isnan(bin):
            bin = numpy.float64(0)

        bins[i] = bin
        i += 1

    return bins