##
# Special thanks to @krey for the python3 support
##

import numpy as np
import colorsys
import colorlover as cl
import operator
import copy

from collections import deque
from six import string_types
from IPython.display import HTML, display

from .utils import inverseDict
from .auth import get_config_file


class CufflinksError(Exception):
    pass


def to_rgba(color, alpha):
    """
    Converts from hex|rgb to rgba

    Parameters:
    -----------
            color : string
                    Color representation on hex or rgb
            alpha : float
                    Value from 0 to 1.0 that represents the
                    alpha value.

    Example:
            to_rgba('#E1E5ED',0.6)
            to_rgba('#f03',0.7)
            to_rgba('rgb(23,23,23)',.5)
    """
    if type(color) == tuple:
        color, alpha = color
    color = color.lower()
    if 'rgba' in color:
        cl = list(eval(color.replace('rgba', '')))
        if alpha:
            cl[3] = alpha
        return 'rgba' + str(tuple(cl))
    elif 'rgb' in color:
        r, g, b = eval(color.replace('rgb', ''))
        return 'rgba' + str((r, g, b, alpha))
    else:
        return to_rgba(hex_to_rgb(color), alpha)


def hex_to_rgb(color):
    """
    Converts from hex to rgb

    Parameters:
    -----------
            color : string
                    Color representation on hex or rgb

    Example:
            hex_to_rgb('#E1E5ED')
            hex_to_rgb('#f03')
    """
    color = normalize(color)
    color = color[1:]
    # return 'rgb'+str(tuple(ord(c) for c in color.decode('hex')))
    return 'rgb' + str((int(color[0:2], base=16), int(color[2:4], base=16), int(color[4:6], base=16)))


def normalize(color):
    """
    Returns an hex color

    Parameters:
    -----------
            color : string
                    Color representation in rgba|rgb|hex

    Example:
            normalize('#f03')
    """
    if type(color) == tuple:
        color = to_rgba(*color)
    if 'rgba' in color:
        return rgb_to_hex(rgba_to_rgb(color))
    elif 'rgb' in color:
        return rgb_to_hex(color)
    elif '#' in color:
        if len(color) == 7:
            return color
        else:
            color = color[1:]
            return '#' + ''.join([x * 2 for x in list(color)])
    else:
        try:
            return normalize(cnames[color.lower()])
        except:
            raise CufflinksError('Not a valid color: ' + color)


def rgb_to_hex(color):
    """
    Converts from rgb to hex

    Parameters:
    -----------
            color : string
                    Color representation on hex or rgb

    Example:
            rgb_to_hex('rgb(23,25,24)')
    """
    rgb = eval(color.replace('rgb', ''))
    # return '#'+''.join(map(chr, rgb)).encode('hex')
    return '#' + ''.join(['{0:02x}'.format(x).upper() for x in rgb])


def rgba_to_rgb(color, bg='rgb(255,255,255)'):
    """
    Converts from rgba to rgb

    Parameters:
    -----------
            color : string
                    Color representation in rgba
            bg : string
                    Color representation in rgb

    Example:
            rgba_to_rgb('rgb(23,25,24,.4)''
    """
    def c_tup(c):
        return eval(c[c.find('('):])
    color = c_tup(color)
    bg = hex_to_rgb(normalize(bg))
    bg = c_tup(bg)
    a = color[3]
    r = [int((1 - a) * bg[i] + a * color[i]) for i in range(3)]
    return 'rgb' + str(tuple(r))


def hex_to_hsv(color):
    """
    Converts from hex to hsv

    Parameters:
    -----------
            color : string
                    Color representation on color

    Example:
            hex_to_hsv('#ff9933')
    """
    color = normalize(color)
    color = color[1:]
    # color=tuple(ord(c)/255.0 for c in color.decode('hex'))
    color = (int(color[0:2], base=16) / 255.0, int(color[2:4],
                                                   base=16) / 255.0, int(color[4:6], base=16) / 255.0)
    return colorsys.rgb_to_hsv(*color)


def color_range(color, N=20):
    """
    Generates a scale of colours from a base colour

    Parameters:
    -----------
            color : string
                    Color representation in hex
            N   : int
                    number of colours to generate

    Example:
            color_range('#ff9933',20)
    """
    color = normalize(color)
    org = color
    color = hex_to_hsv(color)
    HSV_tuples = [(color[0], x, color[2]) for x in np.arange(0, 1, 2.0 / N)]
    HSV_tuples.extend([(color[0], color[1], x)
                       for x in np.arange(0, 1, 2.0 / N)])
    hex_out = []
    for c in HSV_tuples:
        c = colorsys.hsv_to_rgb(*c)
        c = [int(_ * 255) for _ in c]
        # hex_out.append("#"+"".join([chr(x).encode('hex') for x in c]))
        hex_out.append("#" + "".join(['{0:02x}'.format(x) for x in c]))
    if org not in hex_out:
        hex_out.append(org)
    hex_out.sort()
    return hex_out


def color_table(color, N=1, sort=False, sort_values=False, inline=False, as_html=False):
    """
    Generates a colour table

    Parameters:
    -----------
            color : string | list | dict
                    Color representation in rgba|rgb|hex
                    If a list of colors is passed then these
                    are displayed in a table
            N   : int
                    number of colours to generate
                    When color is not a list then it generaes
                    a range of N colors
            sort : bool
                    if True then items are sorted
            sort_values : bool
                    if True then items are sorted by color values.
                    Only applies if color is a dictionary
            inline : bool
                    if True it returns single line color blocks
            as_html : bool
                    if True it returns the HTML code

    Example:
            color_table('#ff9933')
            color_table(cufflinks.cnames)
            color_table(['pink','salmon','yellow'])
    Note:
            This function only works in iPython Notebook