#!/usr/bin/env python
"""
Colormap-related functions

Authors:
    - Arno Klein, 2016  (arno@mindboggle.info)  http://binarybottle.com

Copyright 2016,  Mindboggle team (http://mindboggle.info), Apache v2.0 License

"""


def distinguishable_colors(ncolors, backgrounds=[[0,0,0],[1,1,1]],
                           save_csv=True, plot_colormap=True, verbose=True):
    """
    Create a colormap of perceptually distinguishable colors.

    This program is a Python program based on Tim Holy's 2010-2011
    BSD-licensed Matlab program "distinguishable_colors.m"
    (https://www.mathworks.com/matlabcentral/fileexchange/
     29702-generate-maximally-perceptually-distinct-colors):

    "This function generates a set of colors which are distinguishable
    by reference to the "Lab" color space, which more closely matches
    human color perception than RGB. Given an initial large list of possible
    RGB colors, it iteratively chooses the entry in the list that is farthest
    (in Lab space) from all previously-chosen entries. While this "greedy"
    algorithm does not yield a global maximum, it is simple and efficient.
    Moreover, the sequence of colors is consistent no matter how many you
    request, which facilitates the users' ability to learn the color order
    and avoids major changes in the appearance of plots when adding or
    removing lines."

    Parameters
    ----------
    ncolors : integer
        number of colors for the colormap
    backgrounds : list of list(s) of 3 elements between 0 and 1
        rgb background colors to initialize and distinguish from
    save_csv : Boolean
        save colormap as csv file?
    plot_colormap : Boolean
        plot colormap as horizontal bar chart?
    verbose : Boolean
        print to stdout?

    Returns
    -------
    colors : numpy ndarray of ndarrays of 3 floats between 0 and 1
        rgb colormap

    Examples
    --------
    >>> from mindboggle.mio.colors import distinguishable_colors
    >>> ncolors = 31
    >>> backgrounds = [[0,0,0],[1,1,1]]
    >>> save_csv = False
    >>> plot_colormap = False
    >>> verbose = False
    >>> colors = distinguishable_colors(ncolors, backgrounds,
    ...     save_csv, plot_colormap, verbose)
    >>> colors[0]
    array([ 0.62068966,  0.06896552,  1.        ])
    >>> colors[1]
    array([ 0.       ,  0.5862069,  0.       ])
    >>> colors[2]
    array([ 0.75862069,  0.20689655,  0.        ])

    """
    import numpy as np
    import matplotlib.pyplot as plt
    from colormath.color_objects import LabColor, AdobeRGBColor
    from colormath.color_conversions import convert_color
    from colormath.color_diff import delta_e_cie2000

    filename = "colormap_of_{0}_distinguishable_colors".format(ncolors)

    # ------------------------------------------------------------------------
    # Generate a sizable number of RGB triples. This represents our space of
    # possible choices. By starting in RGB space, we ensure that all of the
    # colors can be generated by the monitor:
    # ------------------------------------------------------------------------
    n_grid = 30  # number of grid divisions along each axis in RGB space
    x = np.linspace(0, 1, num=n_grid, endpoint=True)
    R, G, B = np.meshgrid(x, x, x)
    ncolors_total = np.size(R)
    RGB = np.vstack([np.ravel(R), np.ravel(G), np.ravel(B)])
    RGB = [[RGB[0][icolor], RGB[1][icolor], RGB[2][icolor]]
           for icolor in range(ncolors_total)]
    if ncolors > ncolors_total:
        raise IOError("You can't readily distinguish that many colors")

    # ------------------------------------------------------------------------
    # Convert to Lab color space which better represents human perception:
    # ------------------------------------------------------------------------
    # https://python-colormath.readthedocs.io/en/latest/illuminants.html
    lab_colors = []
    for rgb in RGB:
        lab = convert_color(AdobeRGBColor(rgb[0],
                                          rgb[1],
                                          rgb[2]), LabColor)
        lab_colors.append(lab)

    bg_lab_colors = []
    for bg_rgb in backgrounds:
        bg_lab = convert_color(AdobeRGBColor(bg_rgb[0],
                                             bg_rgb[1],
                                             bg_rgb[2]), LabColor)
        bg_lab_colors.append(bg_lab)

    # ------------------------------------------------------------------------
    # If the user specified multiple background colors, compute differences
    # between the candidate colors and the background colors:
    # ------------------------------------------------------------------------
    min_dx = np.inf * np.ones(ncolors_total)
    if backgrounds:
        for bg_lab_color in bg_lab_colors:
            # Store difference from closest previously-chosen color:
            for icolor_total, lab_color in enumerate(lab_colors):
                dx = delta_e_cie2000(lab_color, bg_lab_color)
                min_dx[icolor_total] = min(dx, min_dx[icolor_total])

    # ------------------------------------------------------------------------
    # Iteratively pick the color that maximizes the difference
    # with the nearest already-picked color:
    # ------------------------------------------------------------------------
    # Initialize by making the "previous" color equal to the last background:
    last_lab_color = bg_lab_colors[-1]
    colors = np.zeros((ncolors, 3))
    for icolor in range(ncolors):

        # Find the difference of the last color from all colors on the list:
        for icolor_total, lab_color in enumerate(lab_colors):
            dx = delta_e_cie2000(lab_color, last_lab_color)
            min_dx[icolor_total] = min(dx, min_dx[icolor_total])

        # Find the entry farthest from all previously chosen colors:
        imax_dx = np.argmax(min_dx)

        # Store distant color:
        colors[icolor] = RGB[imax_dx]

        # Prepare for next iteration:
        last_lab_color = lab_colors[imax_dx]

    # ------------------------------------------------------------------------
    # Plot the colormap as a horizontal bar chart:
    # ------------------------------------------------------------------------
    if plot_colormap:
        if verbose:
            print("RGB values:")
        plt.figure(ncolors, figsize=(5, 10))
        for icolor in range(ncolors):
            ax = plt.subplot(ncolors, 1, icolor + 1)
            plt.axis("off")
            rgb = colors[icolor]
            #rgb = [[rgb.rgb_r, rgb.rgb_g, rgb.rgb_b]]
            if verbose:
                print(rgb)
            plt.barh(0, 50, 1, 0, color=rgb)
            plt.savefig(filename + ".png")
        if verbose:
            print("Colormap image saved to {0}".format(filename + ".png"))

    # ------------------------------------------------------------------------
    # Save the colormap as a csv file:
    # ------------------------------------------------------------------------
    if save_csv:
        np.savetxt(filename + ".csv", colors, fmt='%.18e', delimiter=',',
                   newline='\n', header='')
        if verbose:
            print("Colormap saved to {0}".format(filename + ".csv"))

    return colors


def label_adjacency_matrix(label_file, ignore_values=[-1, 999], add_value=0,
                           save_table=True, output_format='csv',
                           verbose=True):
    """
    Extract surface or volume label boundaries, find unique label pairs,
    and write adjacency matrix (useful for constructing a colormap).

    Each row of the (upper triangular) adjacency matrix corresponds to an
    index to a unique label, where each column has a 1 if the label indexed
    by that column is adjacent to the label indexed by the row.

    Parameters
    ----------
    label_file : string
        path to VTK surface file or nibabel-readable volume file with labels
    ignore_values : list of integers
        labels to ignore
    add_value : integer
        value to add to labels
    matrix : pandas dataframe
        adjacency matrix
    save_table : Boolean
        output table file?
    output_format : string
        format of adjacency table file name (currently only 'csv')
    verbose : Boolean
        print to stdout?

    Returns
    -------
    labels : list
        label numbers
    matrix : pandas DataFrame
        adjacency matrix
    output_table : string
        adjacency table file name

    Examples
    --------
    >>> from mindboggle.mio.colors import label_adjacency_matrix
    >>> from mindboggle.mio.fetch_data import prep_tests
    >>> urls, fetch_data = prep_tests()
    >>> ignore_values = [-1, 0]
    >>> add_value = 0
    >>> save_table = False
    >>> output_format = 'csv'
    >>> verbose = False
    >>> label_file = fetch_data(urls['left_manual_labels'], '', '.vtk')
    >>> labels, matrix, output_table = label_adjacency_matrix(label_file,
    ...     ignore_values, add_value, save_table, output_format, verbose)
    >>> matrix.lookup([20,21,22,23,24,25,26,27,28,29],
    ...               [35,35,35,35,35,35,35,35,35,35])
    array([ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  1.,  1.,  1.])

    >>> label_file = fetch_data(urls['freesurfer_labels'], '', '.nii.gz')
    >>> labels, matrix, output_table = label_adjacency_matrix(label_file,
    ...     ignore_values, add_value, save_table, output_format, verbose)
    >>> matrix.lookup([4,5,7,8,10,11,12,13,14,15], [4,4,4,4,4,4,4,4,4,4])
    array([ 1.,  1.,  0.,  0.,  0.,  1.,  0.,  0.,  1.,  0.])

    """
    import numpy as np
    import pandas as pd
    from nibabel import load
    from scipy import ndimage

    from mindboggle.guts.mesh import find_neighbors
    from mindboggle.guts.segment import extract_borders
    from mindboggle.mio.vtks import read_vtk

    # Use Mindboggle's extract_borders() function for surface VTK files:
    if label_file.endswith('.vtk'):
        f1,f2,f3, faces, labels, f4, npoints, f5 = read_vtk(label_file,
                                                            True, True)
        neighbor_lists = find_neighbors(faces, npoints)
        return_label_pairs = True
        indices_borders, label_pairs, f1 = extract_borders(list(range(npoints)),
            labels, neighbor_lists, ignore_values, return_label_pairs)

        output_table = 'adjacent_surface_labels.' + output_format

    # Use scipy to dilate volume files to find neighboring labels:
    elif label_file.endswith('.nii.gz'):

        L = load(label_file).get_data()
        unique_volume_labels = np.unique(L)

        label_pairs = []
        for label in unique_volume_labels:

            if label not in ignore_values:

                B = L * np.logical_xor(ndimage.binary_dilation(L==int(label)),
                                       (L==int(label)))
                neighbor_labels = np.unique(np.ravel(B))

                for neigh in neighbor_labels:
                    if neigh > 0 and neigh in unique_volume_labels:
                    #        and neigh%2==(int(label)%2):
                        label_pairs.append([int(label), int(neigh)])

        output_table = 'adjacent_volume_labels.' + output_format

    else:
        raise IOError("Use appropriate input file type.")

    # Find unique pairs (or first two of each list):
    pairs = []
    for pair in label_pairs:
        new_pair = [int(pair[0]) + add_value,
                    int(pair[1]) + add_value]
        if new_pair not in pairs:
            pairs.append(new_pair)

    # Write adjacency matrix:
    unique_labels = np.unique(pairs)
    nlabels = np.size(unique_labels)
    matrix = np.zeros((nlabels, nlabels))
    for pair in pairs:
        index1 = [i for i, x in enumerate(unique_labels) if x == pair[0]]
        index2 = [i for i, x in enumerate(unique_labels) if x == pair[1]]
        matrix[index1, index2] = 1

    df1 = pd.DataFrame({'ID': unique_labels}, index=None)
    df2 = pd.DataFrame(matrix, index=None)
    df2.columns = unique_labels
    matrix = pd.concat([df1, df2], axis=1)

    if save_table:
        if output_format == 'csv':
            matrix.to_csv(output_table, index=False)
            if verbose:
                print("Adjacency matrix saved to {0}".format(output_table))
        else:
            raise IOError("Set appropriate output file format.")
    else:
        output_table = None

    labels = list(unique_labels)

    return labels, matrix, output_table


def group_colors(colormap, colormap_name, description='', adjacency_matrix=[],
                 IDs=[], names=[], groups=[],
                 save_text_files=True, plot_colors=True,
                 plot_graphs=True, out_dir='.', verbose=True):
    """
    This greedy algoritm reorders a colormap so that labels assigned to
    the same group have more similar colors, but within a group (usually
    of adjacent labels), the colors are reordered so that adjacent labels
    have dissimilar colors:

    1. Convert colormap to Lab color space
       which better represents human perception.
    2. Load a binary (or weighted) adjacency matrix, where each row or column
       represents a label, and each value signifies whether (or the degree
       to which) a given pair of labels are adjacent.
       If a string (file) is provided instead of a numpy ndarray:
            column 0 = label "ID" number
            column 1 = label "name"
            column 2 = "group" number (each label is assigned to a group)
            columns 3... = label adjacency matrix
    3. Sort labels by decreasing number of adjacent labels (adjacency sum).
    4. Order label groups by decreasing maximum adjacency sum.
    5. Create a similarity matrix for pairs of colors.
    6. Sort colors by decreasing perceptual difference from all other colors.
    7. For each label group:
        7.1. Select unpicked colors for group that are similar to the first
             unpicked color (unpicked colors were sorted above by decreasing
             perceptual difference from all other colors).
        7.2. Reorder subgraph colors according to label adjacency sum
             (decreasing number of adjacent labels).
    8. Assign new colors.

    For plotting graphs and colormap:

    1. Convert the matrix to a graph, where each node represents a label
       and each edge represents the adjacency value between connected nodes.
    2. Break up the graph into subgraphs, where each subgraph contains labels
       assigned the same group number (which usually means they are adjacent).
    3. Plot the colormap and colored sub/graphs.

    NOTE: Requires pydotplus

    Parameters
    ----------
    colormap : string or numpy ndarray of ndarrays of 3 floats between 0 and 1
        csv file containing rgb colormap, or colormap array
    colormap_name : string
        name of colormap
    description : string
        description of colormap
    adjacency_matrix : string or NxN numpy ndarray (N = number of labels)
        csv file containing label adjacency matrix or matrix itself
    IDs : list of integers
        label ID numbers
    names : list of strings
        label names
    groups : list of integers
        label group numbers (one per label)
    save_text_files : Boolean
        save colormap as csv and json files?
    plot_colors : Boolean
        plot colormap as horizontal bar chart?
    plot_graphs : Boolean
        plot colormap as graphs?
    out_dir : string
        output directory path
    verbose : Boolean
        print to stdout?

    Returns
    -------
    colors : numpy ndarray of ndarrays of 3 floats between 0 and 1
        rgb colormap

    Examples
    --------
    >>> # Get colormap:
    >>> from mindboggle.mio.colors import distinguishable_colors
    >>> colormap = distinguishable_colors(ncolors=31,
    ...     backgrounds=[[0,0,0],[1,1,1]],
    ...     save_csv=False, plot_colormap=False, verbose=False)
    >>> # Get adjacency matrix:
    >>> from mindboggle.mio.colors import label_adjacency_matrix
    >>> from mindboggle.mio.fetch_data import prep_tests
    >>> urls, fetch_data = prep_tests()
    >>> label_file = fetch_data(urls['left_manual_labels'], '', '.vtk')
    >>> IDs, adjacency_matrix, output_table = label_adjacency_matrix(label_file,
    ...     ignore_values=[-1, 0], add_value=0, save_table=False,
    ...     output_format='', verbose=False)
    >>> adjacency_matrix = adjacency_matrix.values
    >>> adjacency_matrix = adjacency_matrix[:, 1::]
    >>> # Reorganize colormap:
    >>> from mindboggle.mio.colors import group_colors
    >>> from mindboggle.mio.labels import DKTprotocol
    >>> dkt = DKTprotocol()
    >>> colormap_name = "DKT31colormap"
    >>> description = "Colormap for DKT31 human brain cortical labels"
    >>> save_text_files = True
    >>> plot_colors = False
    >>> plot_graphs = False
    >>> out_dir = '.'
    >>> verbose = False
    >>> #IDs = dkt.DKT31_numbers
    >>> names = dkt.DKT31_names #dkt.left_cerebrum_cortex_DKT31_names
    >>> groups = dkt.DKT31_groups
    >>> colors = group_colors(colormap, colormap_name, description,
    ...     adjacency_matrix, IDs, names, groups,
    ...     save_text_files, plot_colors, plot_graphs, out_dir, verbose)
    >>> colors[0]
    [0.7586206896551724, 0.20689655172413793, 0.0]
    >>> colors[1]
    [0.48275862068965514, 0.4482758620689655, 0.48275862068965514]
    >>> colors[2]
    [0.3448275862068966, 0.3103448275862069, 0.034482758620689655]
    >>> colors[-1]
    [0.7931034482758621, 0.9655172413793103, 0.7931034482758621]

    No groups / subgraphs:

    >>> groups = []
    >>> colors = group_colors(colormap, colormap_name, description,
    ...     adjacency_matrix, IDs, names, groups,
    ...     save_text_files, plot_colors, plot_graphs, out_dir, verbose)
    >>> colors[0]
    [0.5172413793103449, 0.8275862068965517, 1.0]
    >>> colors[1]
    [0.13793103448275862, 0.0, 0.24137931034482757]
    >>> colors[2]
    [0.3793103448275862, 0.27586206896551724, 0.48275862068965514]
    >>> colors[-1]
    [0.6206896551724138, 0.48275862068965514, 0.3448275862068966]

    """
    import os
    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import networkx as nx
    from colormath.color_diff import delta_e_cie2000
    from colormath.color_objects import LabColor, AdobeRGBColor
    from colormath.color_conversions import convert_color
    import itertools

    from mindboggle.mio.colors import write_json_colormap, write_xml_colormap

    # ------------------------------------------------------------------------
    # Set parameters for graph layout and output files:
    # ------------------------------------------------------------------------
    if plot_graphs:
        graph_node_size = 1000
        graph_edge_width = 2
        graph_font_size = 10
        subgraph_node_size = 3000
        subgraph_edge_width = 5
        subgraph_font_size = 18
        axis_buffer = 10
        graph_image_file = os.path.join(out_dir, "label_graph.png")
        subgraph_image_file_pre = os.path.join(out_dir, "label_subgraph")
        subgraph_image_file_post = ".png"

    if plot_colors:
        colormap_image_file = os.path.join(out_dir, 'label_colormap.png')

    if save_text_files:
        colormap_csv_file = os.path.join(out_dir, 'label_colormap.csv')
        colormap_json_file = os.path.join(out_dir, 'label_colormap.json')
        colormap_xml_file = os.path.join(out_dir, 'label_colormap.xml')

    run_permutations = False

    # ------------------------------------------------------------------------
    # Load colormap:
    # ------------------------------------------------------------------------
    if verbose:
        print("Load colormap and convert to CIELAB color space.")
    if isinstance(colormap, np.ndarray):
        colors = colormap
    elif isinstance(colormap, str):
        colors = pd.read_csv(colormap, sep=',', header=None)
        colors = colors.values
    else:
        raise IOError("Please use correct format for colormap.")
    nlabels = np.shape(colors)[0]
    new_colors = np.copy(colors)

    if len(IDs) == 0:
        IDs = range(nlabels)
    if len(names) == 0:
        names = [str(x) for x in range(nlabels)]
    if len(groups) == 0:
        groups = [1 for x in range(nlabels)]

    # ------------------------------------------------------------------------
    # Convert to Lab color space which better represents human perception:
    # ------------------------------------------------------------------------
    # https://python-colormath.readthedocs.io/en/latest/illuminants.html
    lab_colors = []
    for rgb in colors:
        lab_colors.append(convert_color(AdobeRGBColor(rgb[0], rgb[1], rgb[2]),
                                        LabColor))

    # ------------------------------------------------------------------------
    # Load label adjacency matrix:
    # ------------------------------------------------------------------------
    if np.size(adjacency_matrix):
        if verbose:
            print("Load label adjacency matrix.")
        if isinstance(adjacency_matrix, np.ndarray):
            adjacency_values = adjacency_matrix
        # If a string (file) is provided instead of a numpy ndarray:
        #    column 0 = label "ID" number
        #    column 1 = label "name"
        #    column 2 = "group" number (each label is assigned to a group)
        #    columns 3... = label adjacency matrix
        elif isinstance(adjacency_matrix, str):
            matrix = pd.read_csv(adjacency_matrix, sep=',', header=None)
            matrix = matrix.values
            IDs = matrix.ID
            names = matrix.name
            groups = matrix.group
            adjacency_values = matrix[[str(x) for x in IDs]].values
        else:
            raise IOError("Please use correct format for adjacency matrix.")
        if np.shape(adjacency_values)[0] != nlabels:
            raise IOError("The colormap and label adjacency matrix don't "
                          "have the same number of labels.")

        # Normalize adjacency values:
        adjacency_values = adjacency_values / np.max(adjacency_values)
    else:
        plot_graphs = False

    # ------------------------------------------------------------------------
    # Sort labels by decreasing number of adjacent labels (adjacency sum):
    # ------------------------------------------------------------------------
    if np.size(adjacency_matrix):
        adjacency_sums = np.sum(adjacency_values, axis = 1)  # sum rows
        isort_labels = np.argsort(adjacency_sums)[::-1]
    else:
        isort_labels = range(nlabels)

    # ------------------------------------------------------------------------
    # Order label groups by decreasing maximum adjacency sum:
    # ------------------------------------------------------------------------
    label_groups = np.unique(groups)
    if np.size(adjacency_matrix):
        max_adjacency_sums = []
        for label_group in label_groups:
            igroup = [i for i,x in enumerate(groups) if x == label_group]
            max_adjacency_sums.append(max(adjacency_sums[igroup]))
        label_groups = label_groups[np.argsort(max_adjacency_sums)[::-1]]

    # ------------------------------------------------------------------------
    # Convert adjacency matrix to graph for plotting:
    # ------------------------------------------------------------------------
    if plot_graphs:
        adjacency_graph = nx.from_numpy_matrix(adjacency_values)
        for inode in range(nlabels):
            adjacency_graph.node[inode]['ID'] = IDs[inode]
            adjacency_graph.node[inode]['label'] = names[inode]
            adjacency_graph.node[inode]['group'] = groups[inode]

    # ------------------------------------------------------------------------
    # Create a similarity matrix for pairs of colors:
    # ------------------------------------------------------------------------
    if verbose:
        print("Create a similarity matrix for pairs of colors.")
    dx_matrix = np.zeros((nlabels, nlabels))
    for icolor1 in range(nlabels):
        for icolor2 in range(nlabels):
            dx_matrix[icolor1,icolor2] = delta_e_cie2000(lab_colors[icolor1],
                                                         lab_colors[icolor2])
    # ------------------------------------------------------------------------
    # Sort colors by decreasing perceptual difference from all other colors:
    # ------------------------------------------------------------------------
    icolors_to_pick = list(np.argsort(np.sum(dx_matrix, axis = 1))[::-1])

    # ------------------------------------------------------------------------
    # Loop through label groups:
    # ------------------------------------------------------------------------
    for label_group in label_groups:
        if verbose:
            print("Labels in group {0}...".format(label_group))
        igroup = [i for i,x in enumerate(groups) if x == label_group]
        N = len(igroup)

        # --------------------------------------------------------------------
        # Select unpicked colors for group that are similar to the first
        # unpicked color (unpicked colors were sorted above by decreasing
        # perceptual difference from all other colors):
        # --------------------------------------------------------------------
        isimilar = np.argsort(dx_matrix[icolors_to_pick[0],
                                        icolors_to_pick])[0:N]
        icolors_to_pick_copy = icolors_to_pick.copy()
        group_colors = [list(colors[icolors_to_pick[i]]) for i in isimilar]
        if run_permutations:
            group_lab_colors = [lab_colors[icolors_to_pick[i]]
                                for i in isimilar]
        for iremove in isimilar:
            icolors_to_pick.remove(icolors_to_pick_copy[iremove])

        # --------------------------------------------------------------------
        # Reorder group colors according to label adjacency sum
        # (decreasing number of adjacent labels):
        # --------------------------------------------------------------------
        isort_group_labels = np.argsort(isort_labels[igroup])
        group_colors = [group_colors[i] for i in isort_group_labels]

        # --------------------------------------------------------------------
        # Compute differences between every pair of colors within group:
        # --------------------------------------------------------------------
        weights = False
        if run_permutations:
            permutation_max = np.zeros(N)
            NxN_matrix = np.zeros((N, N))
            # ----------------------------------------------------------------
            # Extract group adjacency submatrix:
            # ----------------------------------------------------------------
            neighbor_matrix = adjacency_values[igroup, :][:, igroup]
            if not weights:
                neighbor_matrix = (neighbor_matrix > 0).astype(np.uint8)
            # ----------------------------------------------------------------
            # Permute colors and color pair differences:
            # ----------------------------------------------------------------
            DEmax = 0
            permutations = [np.array(s) for s
                            in itertools.permutations(range(0, N), N)]
            if verbose:
                print(" ".join([str(N),'labels,',
                                str(len(permutations)),
                                'permutations:']))
            for permutation in permutations:
                delta_matrix = NxN_matrix.copy()
                for i1 in range(N):
                  for i2 in range(N):
                    if (i2 > i1) and (neighbor_matrix[i1, i2] > 0):
                      delta_matrix[i1,i2] = delta_e_cie2000(group_lab_colors[i1],
                                                            group_lab_colors[i2])
                if weights:
                    DE = np.sum((delta_matrix * neighbor_matrix))
                else:
                    DE = np.sum(delta_matrix)
                # ------------------------------------------------------------
                # Store color permutation with maximum adjacency cost:
                # ------------------------------------------------------------
                if DE > DEmax:
                    DEmax = DE
                    permutation_max = permutation
                # ------------------------------------------------------------
                # Reorder group colors by the maximum adjacency cost:
                # ------------------------------------------------------------
                group_colors = [group_colors[x] for x in permutation_max]
                new_colors[isimilar] = group_colors

        # --------------------------------------------------------------------
        # Assign new colors:
        # --------------------------------------------------------------------
        else:
            new_colors[isimilar] = group_colors

        # --------------------------------------------------------------------
        # Draw a figure of the colored subgraph:
        # --------------------------------------------------------------------
        if plot_graphs:
            plt.figure(label_group)
            subgraph = adjacency_graph.subgraph(igroup)

            # Layout:
            pos = nx.nx_pydot.graphviz_layout(subgraph,
                                              prog="neato")
            nx.draw(subgraph, pos, node_size=subgraph_node_size,
                    width=subgraph_edge_width, alpha=0.5,
                    with_labels=False)

            # Labels:
            labels={}
            for iN in range(N):
                labels[subgraph.nodes()[iN]] = \
                    subgraph.node[subgraph.nodes()[iN]]['label']
            nx.draw_networkx_labels(subgraph, pos, labels,
                                    font_size=subgraph_font_size,
                                    font_color='black')
            # Nodes:
            nodelist = list(subgraph.node.keys())
            for iN in range(N):
                nx.draw_networkx_nodes(subgraph, pos,
                    node_size=subgraph_node_size,
                    nodelist=[nodelist[iN]],
                    node_color=group_colors[iN])

            # Figure:
            ax = plt.gca().axis()
            plt.gca().axis([ax[0]-axis_buffer, ax[1]+axis_buffer,
                            ax[2]-axis_buffer, ax[3]+axis_buffer])
            plt.savefig(subgraph_image_file_pre + str(int(label_group)) +
                        subgraph_image_file_post)
            #plt.show()

    # ------------------------------------------------------------------------
    # Plot the entire graph (without colors):
    # ------------------------------------------------------------------------
    if plot_graphs:
        plt.figure(nlabels)

        # Graph:
        pos = nx.nx_pydot.graphviz_layout(adjacency_graph, prog="neato")
        nx.draw(adjacency_graph, pos,
                node_color='yellow',
                node_size=graph_node_size,
                width=graph_edge_width,
                with_labels=False)

        # Labels:
        labels={}
        for ilabel in range(nlabels):
            labels[ilabel] = adjacency_graph.node[ilabel]['label']
        nx.draw_networkx_labels(adjacency_graph, pos, labels,
                                font_size=graph_font_size,
                                font_color='black')
        # # Nodes:
        # nodelist = list(adjacency_graph.node.keys())
        # for icolor, new_color in enumerate(new_colors):
        #     nx.draw_networkx_nodes(subgraph, pos,
        #                            node_size=graph_node_size,
        #                            nodelist=[nodelist[icolor]],
        #                            node_color=new_color)

        plt.savefig(graph_image_file)
        plt.show()

    # ------------------------------------------------------------------------
    # Plot the subgraphs (colors):
    # ------------------------------------------------------------------------
    if plot_graphs:
        for label_group in label_groups:
            plt.figure(label_group)
            plt.show()

    # ------------------------------------------------------------------------
    # Plot the colormap as a horizontal bar chart:
    # ------------------------------------------------------------------------
    if plot_colors:
        plt.figure(nlabels, figsize=(5, 10))
        for ilabel in range(nlabels):
            ax = plt.subplot(nlabels, 1, ilabel + 1)
            plt.axis("off")
            rgb = new_colors[ilabel]
            plt.barh(0, 50, 1, 0, color=rgb)
        plt.savefig(colormap_image_file)
        plt.show()

    # ------------------------------------------------------------------------
    # Save new colormap as text files:
    # ------------------------------------------------------------------------
    if save_text_files:

        # ------------------------------------------------------------------------
        # Save new colormap as a csv file:
        # ------------------------------------------------------------------------
        np.savetxt(colormap_csv_file, new_colors, fmt='%.18e', delimiter=',',
                   newline='\n', header='')

        # ------------------------------------------------------------------------
        # Save new colormap as a json file:
        # ------------------------------------------------------------------------
        write_json_colormap(colormap=new_colors, label_numbers=IDs,
                            label_names=names,
                            colormap_file=colormap_json_file,
                            colormap_name=colormap_name,
                            description=description)

        # ------------------------------------------------------------------------
        # Save new colormap as an xml file:
        # ------------------------------------------------------------------------
        write_xml_colormap(colormap=new_colors, label_numbers=IDs,
                           colormap_file=colormap_xml_file,
                           colormap_name=colormap_name)

    # ------------------------------------------------------------------------
    # Return new colors:
    # ------------------------------------------------------------------------
    colors = new_colors.tolist()

    return colors


def write_json_colormap(colormap, label_numbers, label_names=[],
                        colormap_file='', colormap_name='', description=''):
    """
    Write colormap to json format.

    Parameters
    ----------
    colormap : numpy ndarray or list of lists of string and floats
        label, 1, red, green, blue
    label_names : list of strings
        label names
    label_numbers : list of integers
        label numbers
    colormap_file : string
        output json file name
    colormap_name : string
        name of colormap
    description : string
        description of colormap

    Examples
    --------
    >>> from mindboggle.mio.colors import write_xml_colormap
    >>> from mindboggle.mio.labels import DKTprotocol
    >>> dkt = DKTprotocol()
    >>> colormap = dkt.colormap_normalized
    >>> colormap = [[x[2], x[3], x[4]] for x in colormap]
    >>> label_numbers = dkt.label_numbers
    >>> label_names = dkt.label_names
    >>> colormap_file = ''
    >>> colormap_name = "DKT31colormap"
    >>> description = "Colormap for DKT31 human brain cortical labels"
    >>> colormap[0]
    [0.803921568627451, 0.24313725490196078, 0.3058823529411765]
    >>> write_json_colormap(colormap, label_numbers, label_names,
    ...     colormap_file, colormap_name, description)
    """

    if not colormap_file:
        colormap_file = 'label_colormap.json'
    if not colormap_name:
        colormap_name = 'Colormap'

    f = open(colormap_file, 'w')
    f.write("{\n")
    f.write('    "name": "{0}",\n'.format(colormap_name))
    f.write('    "description": "{0}",\n'.format(description))
    f.write('    "colormap": [\n')


    for icolor, color in enumerate(colormap):

        if icolor == len(colormap) - 1:
            end_comma = ''
        else:
            end_comma = ','

        f.write('    {0}"ID": "{1}", "name": "{2}", '
                '"red": "{3}", "green": "{4}", "blue": "{5}"{6}{7}\n'.
                format("{", label_numbers[icolor], label_names[icolor],
                       color[0], color[1], color[2], "}", end_comma))
    f.write(']}')
    f.close()


def write_xml_colormap(colormap, label_numbers, colormap_file='',
                       colormap_name=''):
    """
    Write colormap to xml format.

    Parameters
    ----------
    colormap : numpy ndarray or list of lists of string and floats
        label, 1, red, green, blue
    label_numbers : list of integers
        label numbers
    colormap_file : string
        output xml file name
    colormap_name : string
        name of colormap

    Examples
    --------
    >>> from mindboggle.mio.colors import write_xml_colormap
    >>> from mindboggle.mio.labels import DKTprotocol
    >>> dkt = DKTprotocol()
    >>> colormap = dkt.colormap_normalized
    >>> colormap = [[x[2], x[3], x[4]] for x in colormap]
    >>> label_numbers = dkt.label_numbers
    >>> colormap_file = ''
    >>> colormap_name = 'DKT31colormap'
    >>> colormap[0]
    [0.803921568627451, 0.24313725490196078, 0.3058823529411765]
    >>> write_xml_colormap(colormap, label_numbers, colormap_file,
    ...     colormap_name)
    """

    if not colormap_file:
        colormap_file = 'label_colormap.xml'
    if not colormap_name:
        colormap_name = 'Colormap'

    f = open(colormap_file,'w')
    f.write('''
<ColorMap name="{0}" space="RGB">
    <NaN r="0" g="0" b="0"/>
    <Point x="-1" o="0"  r="0" g="0" b="0"/>
'''.format(colormap_name))

    for icolor, color in enumerate(colormap):
        f.write('''    <Point x="{0}" o="1" r="{1}" g="{2}" b="{3}"/>
        '''.format(label_numbers[icolor], color[0], color[1], color[2]))

    f.write('''
</ColorMap>
''')
    f.close()

def viridis_colormap():
    """
    https://github.com/BIDS/colormap/blob/master/colormaps.py

    Returns
    -------
    viridis : list of lists of three floats
        viridis colormap

    """
    viridis = [[0.267004, 0.004874, 0.329415],
        [0.268510, 0.009605, 0.335427],
        [0.269944, 0.014625, 0.341379],
        [0.271305, 0.019942, 0.347269],
        [0.272594, 0.025563, 0.353093],
        [0.273809, 0.031497, 0.358853],
        [0.274952, 0.037752, 0.364543],
        [0.276022, 0.044167, 0.370164],
        [0.277018, 0.050344, 0.375715],
        [0.277941, 0.056324, 0.381191],
        [0.278791, 0.062145, 0.386592],
        [0.279566, 0.067836, 0.391917],
        [0.280267, 0.073417, 0.397163],
        [0.280894, 0.078907, 0.402329],
        [0.281446, 0.084320, 0.407414],
        [0.281924, 0.089666, 0.412415],
        [0.282327, 0.094955, 0.417331],
        [0.282656, 0.100196, 0.422160],
        [0.282910, 0.105393, 0.426902],
        [0.283091, 0.110553, 0.431554],
        [0.283197, 0.115680, 0.436115],
        [0.283229, 0.120777, 0.440584],
        [0.283187, 0.125848, 0.444960],
        [0.283072, 0.130895, 0.449241],
        [0.282884, 0.135920, 0.453427],
        [0.282623, 0.140926, 0.457517],
        [0.282290, 0.145912, 0.461510],
        [0.281887, 0.150881, 0.465405],
        [0.281412, 0.155834, 0.469201],
        [0.280868, 0.160771, 0.472899],
        [0.280255, 0.165693, 0.476498],
        [0.279574, 0.170599, 0.479997],
        [0.278826, 0.175490, 0.483397],
        [0.278012, 0.180367, 0.486697],
        [0.277134, 0.185228, 0.489898],
        [0.276194, 0.190074, 0.493001],
        [0.275191, 0.194905, 0.496005],
        [0.274128, 0.199721, 0.498911],
        [0.273006, 0.204520, 0.501721],
        [0.271828, 0.209303, 0.504434],
        [0.270595, 0.214069, 0.507052],
        [0.269308, 0.218818, 0.509577],
        [0.267968, 0.223549, 0.512008],
        [0.266580, 0.228262, 0.514349],
        [0.265145, 0.232956, 0.516599],
        [0.263663, 0.237631, 0.518762],
        [0.262138, 0.242286, 0.520837],
        [0.260571, 0.246922, 0.522828],
        [0.258965, 0.251537, 0.524736],
        [0.257322, 0.256130, 0.526563],
        [0.255645, 0.260703, 0.528312],
        [0.253935, 0.265254, 0.529983],
        [0.252194, 0.269783, 0.531579],
        [0.250425, 0.274290, 0.533103],
        [0.248629, 0.278775, 0.534556],
        [0.246811, 0.283237, 0.535941],
        [0.244972, 0.287675, 0.537260],
        [0.243113, 0.292092, 0.538516],
        [0.241237, 0.296485, 0.539709],
        [0.239346, 0.300855, 0.540844],
        [0.237441, 0.305202, 0.541921],
        [0.235526, 0.309527, 0.542944],
        [0.233603, 0.313828, 0.543914],
        [0.231674, 0.318106, 0.544834],
        [0.229739, 0.322361, 0.545706],
        [0.227802, 0.326594, 0.546532],
        [0.225863, 0.330805, 0.547314],
        [0.223925, 0.334994, 0.548053],
        [0.221989, 0.339161, 0.548752],
        [0.220057, 0.343307, 0.549413],
        [0.218130, 0.347432, 0.550038],
        [0.216210, 0.351535, 0.550627],
        [0.214298, 0.355619, 0.551184],
        [0.212395, 0.359683, 0.551710],
        [0.210503, 0.363727, 0.552206],
        [0.208623, 0.367752, 0.552675],
        [0.206756, 0.371758, 0.553117],
        [0.204903, 0.375746, 0.553533],
        [0.203063, 0.379716, 0.553925],
        [0.201239, 0.383670, 0.554294],
        [0.199430, 0.387607, 0.554642],
        [0.197636, 0.391528, 0.554969],
        [0.195860, 0.395433, 0.555276],
        [0.194100, 0.399323, 0.555565],
        [0.192357, 0.403199, 0.555836],
        [0.190631, 0.407061, 0.556089],
        [0.188923, 0.410910, 0.556326],
        [0.187231, 0.414746, 0.556547],
        [0.185556, 0.418570, 0.556753],
        [0.183898, 0.422383, 0.556944],
        [0.182256, 0.426184, 0.557120],
        [0.180629, 0.429975, 0.557282],
        [0.179019, 0.433756, 0.557430],
        [0.177423, 0.437527, 0.557565],
        [0.175841, 0.441290, 0.557685],
        [0.174274, 0.445044, 0.557792],
        [0.172719, 0.448791, 0.557885],
        [0.171176, 0.452530, 0.557965],
        [0.169646, 0.456262, 0.558030],
        [0.168126, 0.459988, 0.558082],
        [0.166617, 0.463708, 0.558119],
        [0.165117, 0.467423, 0.558141],
        [0.163625, 0.471133, 0.558148],
        [0.162142, 0.474838, 0.558140],
        [0.160665, 0.478540, 0.558115],
        [0.159194, 0.482237, 0.558073],
        [0.157729, 0.485932, 0.558013],
        [0.156270, 0.489624, 0.557936],
        [0.154815, 0.493313, 0.557840],
        [0.153364, 0.497000, 0.557724],
        [0.151918, 0.500685, 0.557587],
        [0.150476, 0.504369, 0.557430],
        [0.149039, 0.508051, 0.557250],
        [0.147607, 0.511733, 0.557049],
        [0.146180, 0.515413, 0.556823],
        [0.144759, 0.519093, 0.556572],
        [0.143343, 0.522773, 0.556295],
        [0.141935, 0.526453, 0.555991],
        [0.140536, 0.530132, 0.555659],
        [0.139147, 0.533812, 0.555298],
        [0.137770, 0.537492, 0.554906],
        [0.136408, 0.541173, 0.554483],
        [0.135066, 0.544853, 0.554029],
        [0.133743, 0.548535, 0.553541],
        [0.132444, 0.552216, 0.553018],
        [0.131172, 0.555899, 0.552459],
        [0.129933, 0.559582, 0.551864],
        [0.128729, 0.563265, 0.551229],
        [0.127568, 0.566949, 0.550556],
        [0.126453, 0.570633, 0.549841],
        [0.125394, 0.574318, 0.549086],
        [0.124395, 0.578002, 0.548287],
        [0.123463, 0.581687, 0.547445],
        [0.122606, 0.585371, 0.546557],
        [0.121831, 0.589055, 0.545623],
        [0.121148, 0.592739, 0.544641],
        [0.120565, 0.596422, 0.543611],
        [0.120092, 0.600104, 0.542530],
        [0.119738, 0.603785, 0.541400],
        [0.119512, 0.607464, 0.540218],
        [0.119423, 0.611141, 0.538982],
        [0.119483, 0.614817, 0.537692],
        [0.119699, 0.618490, 0.536347],
        [0.120081, 0.622161, 0.534946],
        [0.120638, 0.625828, 0.533488],
        [0.121380, 0.629492, 0.531973],
        [0.122312, 0.633153, 0.530398],
        [0.123444, 0.636809, 0.528763],
        [0.124780, 0.640461, 0.527068],
        [0.126326, 0.644107, 0.525311],
        [0.128087, 0.647749, 0.523491],
        [0.130067, 0.651384, 0.521608],
        [0.132268, 0.655014, 0.519661],
        [0.134692, 0.658636, 0.517649],
        [0.137339, 0.662252, 0.515571],
        [0.140210, 0.665859, 0.513427],
        [0.143303, 0.669459, 0.511215],
        [0.146616, 0.673050, 0.508936],
        [0.150148, 0.676631, 0.506589],
        [0.153894, 0.680203, 0.504172],
        [0.157851, 0.683765, 0.501686],
        [0.162016, 0.687316, 0.499129],
        [0.166383, 0.690856, 0.496502],
        [0.170948, 0.694384, 0.493803],
        [0.175707, 0.697900, 0.491033],
        [0.180653, 0.701402, 0.488189],
        [0.185783, 0.704891, 0.485273],
        [0.191090, 0.708366, 0.482284],
        [0.196571, 0.711827, 0.479221],
        [0.202219, 0.715272, 0.476084],
        [0.208030, 0.718701, 0.472873],
        [0.214000, 0.722114, 0.469588],
        [0.220124, 0.725509, 0.466226],
        [0.226397, 0.728888, 0.462789],
        [0.232815, 0.732247, 0.459277],
        [0.239374, 0.735588, 0.455688],
        [0.246070, 0.738910, 0.452024],
        [0.252899, 0.742211, 0.448284],
        [0.259857, 0.745492, 0.444467],
        [0.266941, 0.748751, 0.440573],
        [0.274149, 0.751988, 0.436601],
        [0.281477, 0.755203, 0.432552],
        [0.288921, 0.758394, 0.428426],
        [0.296479, 0.761561, 0.424223],
        [0.304148, 0.764704, 0.419943],
        [0.311925, 0.767822, 0.415586],
        [0.319809, 0.770914, 0.411152],
        [0.327796, 0.773980, 0.406640],
        [0.335885, 0.777018, 0.402049],
        [0.344074, 0.780029, 0.397381],
        [0.352360, 0.783011, 0.392636],
        [0.360741, 0.785964, 0.387814],
        [0.369214, 0.788888, 0.382914],
        [0.377779, 0.791781, 0.377939],
        [0.386433, 0.794644, 0.372886],
        [0.395174, 0.797475, 0.367757],
        [0.404001, 0.800275, 0.362552],
        [0.412913, 0.803041, 0.357269],
        [0.421908, 0.805774, 0.351910],
        [0.430983, 0.808473, 0.346476],
        [0.440137, 0.811138, 0.340967],
        [0.449368, 0.813768, 0.335384],
        [0.458674, 0.816363, 0.329727],
        [0.468053, 0.818921, 0.323998],
        [0.477504, 0.821444, 0.318195],
        [0.487026, 0.823929, 0.312321],
        [0.496615, 0.826376, 0.306377],
        [0.506271, 0.828786, 0.300362],
        [0.515992, 0.831158, 0.294279],
        [0.525776, 0.833491, 0.288127],
        [0.535621, 0.835785, 0.281908],
        [0.545524, 0.838039, 0.275626],
        [0.555484, 0.840254, 0.269281],
        [0.565498, 0.842430, 0.262877],
        [0.575563, 0.844566, 0.256415],
        [0.585678, 0.846661, 0.249897],
        [0.595839, 0.848717, 0.243329],
        [0.606045, 0.850733, 0.236712],
        [0.616293, 0.852709, 0.230052],
        [0.626579, 0.854645, 0.223353],
        [0.636902, 0.856542, 0.216620],
        [0.647257, 0.858400, 0.209861],
        [0.657642, 0.860219, 0.203082],
        [0.668054, 0.861999, 0.196293],
        [0.678489, 0.863742, 0.189503],
        [0.688944, 0.865448, 0.182725],
        [0.699415, 0.867117, 0.175971],
        [0.709898, 0.868751, 0.169257],
        [0.720391, 0.870350, 0.162603],
        [0.730889, 0.871916, 0.156029],
        [0.741388, 0.873449, 0.149561],
        [0.751884, 0.874951, 0.143228],
        [0.762373, 0.876424, 0.137064],
        [0.772852, 0.877868, 0.131109],
        [0.783315, 0.879285, 0.125405],
        [0.793760, 0.880678, 0.120005],
        [0.804182, 0.882046, 0.114965],
        [0.814576, 0.883393, 0.110347],
        [0.824940, 0.884720, 0.106217],
        [0.835270, 0.886029, 0.102646],
        [0.845561, 0.887322, 0.099702],
        [0.855810, 0.888601, 0.097452],
        [0.866013, 0.889868, 0.095953],
        [0.876168, 0.891125, 0.095250],
        [0.886271, 0.892374, 0.095374],
        [0.896320, 0.893616, 0.096335],
        [0.906311, 0.894855, 0.098125],
        [0.916242, 0.896091, 0.100717],
        [0.926106, 0.897330, 0.104071],
        [0.935904, 0.898570, 0.108131],
        [0.945636, 0.899815, 0.112838],
        [0.955300, 0.901065, 0.118128],
        [0.964894, 0.902323, 0.123941],
        [0.974417, 0.903590, 0.130215],
        [0.983868, 0.904867, 0.136897],
        [0.993248, 0.906157, 0.143936]]

    return viridis