import locale

import matplotlib.patches as mpatches
import plotly.graph_objects as go
from matplotlib import pyplot as plt
from matplotlib.legend_handler import HandlerTuple
from matplotlib.lines import Line2D
from plotly.subplots import make_subplots

from reho.model.sub_problem import *
from reho.plotting.utils import *
from reho.plotting import sankey


__doc__ = """
Contains ready-to-use representations for results generated by REHO.
"""


################################################################################################################################################################
# Plotly
################################################################################################################################################################


def plot_performance(results, plot='costs', indexed_on='Scn_ID', label='EN_long', add_annotation=True, per_m2=False, additional_costs=None, additional_gwp=None,
                     scc=0.177,
                     filename=None, export_format='html', scaling_factor=1, return_df=False):
    """
    Plots performance based on REHO results.

    Parameters
    ----------
    results: dict
        Dictionary of REHO results.
    plot: str
        Choose among those three possibilities:

        - 'costs' for the economic performance indicators,
        - 'gwp' for the global warming potential indicators,
        - 'combined' for a combination of the two indicators, where the emissions are converted into costs using the ``scc`` parameter.
    indexed_on: str
        Whether the results should be grouped on *Scn_ID* or *Pareto_ID*.
    label: str
        Indicates the language to use for the plot. Choose among 'FR_long', 'FR_short', 'EN_long', 'EN_short'.
    add_annotation: bool
        Adds the numerical values along the bar plots.
    per_m2: bool
        Set to True to obtain the results divided by the total ERA.
    additional_costs: dict
        Additional costs to include (choose between 'isolation', 'mobility', and 'ict') and scaling values.
    additional_gwp: dict
        Additional gwp to include (choose between 'isolation', 'mobility', and 'ict') and scaling values.
    scc: float
        Carbon externalities, expressed in *CHF/kgCO2*. Default value is the *Social Cost of Carbon*, from `Rennert, 2022 <https://www.nature.com/articles/s41586-022-05224-9>`_.
    filename: str
        Name of the file to be saved.
    export_format: str
        Can be either 'html', 'png', or 'pdf'.
    scaling_factor: int/float
        Scales linearly the REHO results for the plot.
    return_df: bool
        A dataframe can be returned for further post-processing or reporting purposes.

    Returns
    ----------
    plotly.graph_objs.Figure
        The generated plotly figure.
    pd.DataFrame
        (Optional) A dataframe for further post-processing or reporting purposes.
    """

    if additional_gwp is None:
        additional_gwp = {}
    if additional_costs is None:
        additional_costs = {}
    sc = list(results.keys())[0]
    id = list(results[sc].keys())[0]
    era = results[sc][id]['df_Buildings'].ERA

    df_Economics = dict_to_df(results, 'df_Economics')

    change_data = pd.DataFrame()
    change_data.index = ['x_axis_1', 'x_axis_2', 'y_axis', 'keyword', 'total', 'unites', 'scc_legend']
    decimal = 0
    lang = re.split('_', label)[0]

    if plot == 'costs':
        change_data['FR'] = ['CAPEX', 'OPEX', 'Coûts [CHF/an]', 'Coûts', 'TOTEX', ' CHF', '']
        change_data['EN'] = ['CAPEX', 'OPEX', 'Costs [CHF/y]', 'Costs', 'TOTEX', ' CHF', '']
        df_costs = df_Economics.xs('costs', level='Perf_type')
        if per_m2:
            df_costs = df_costs / era.sum()
            change_data.loc['y_axis']['FR'] = "Coûts [CHF/m2/an]"
            change_data.loc['y_axis']['EN'] = "Costs [CHF/m2/y]"
            decimal = 1
        indexes, data_capacities, data_resources = prepare_dfs(df_costs, indexed_on, neg=True, additional_data=additional_costs, scaling_factor=scaling_factor)

        data_resources = data_resources.drop("avoided", level='type')

        data_scc_resources = pd.DataFrame(0, columns=[indexes], index=data_resources.index)
        data_scc_capacities = pd.DataFrame(0, columns=[indexes], index=data_capacities.index)

        showlegend = False

    elif plot == 'gwp':
        change_data['FR'] = ['Capacités', 'Ressources', 'Émissions [kgCO2/an]', 'Émissions', 'Total', ' kgCO2', '']
        change_data['EN'] = ['Capacities', 'Resources', 'Emissions [kgCO2/y]', 'Emissions', 'Total', ' kgCO2', '']
        df_impact = df_Economics.xs('impact', level='Perf_type')
        if per_m2:
            df_impact = df_impact / era.sum()
            change_data.loc['y_axis']['FR'] = "Émissions [kgCO2/m2/an]"
            change_data.loc['y_axis']['EN'] = "Emissions [kgCO2/m2/y]"
            decimal = 1
        indexes, data_capacities, data_resources = prepare_dfs(df_impact, indexed_on, neg=True, additional_data=additional_gwp, scaling_factor=scaling_factor)

        data_resources = data_resources.drop("avoided", level='type')

        data_scc_resources = pd.DataFrame(0, columns=[indexes], index=data_resources.index)
        data_scc_capacities = pd.DataFrame(0, columns=[indexes], index=data_capacities.index)

        showlegend = False

    elif plot == 'combined':
        change_data['FR'] = ['Capacités', 'Ressources', 'Coûts [CHF/an]', 'Coûts', 'TOTEX', ' CHF', 'Impact carbone']
        change_data['EN'] = ['Capacities', 'Resources', 'Costs [CHF/y]', 'Costs', 'TOTEX', ' CHF', 'Carbon impact']

        df_costs = df_Economics.xs('costs', level='Perf_type')
        df_impact = df_Economics.xs('impact', level='Perf_type')
        if per_m2:
            df_costs = df_costs / era.sum()
            df_impact = df_impact / era.sum()
            change_data.loc['y_axis']['FR'] = "Coûts [CHF/m2/an]"
            change_data.loc['y_axis']['EN'] = "Costs [CHF/m2/y]"
            decimal = 1
        indexes, data_capacities, data_resources = prepare_dfs(df_costs, indexed_on, neg=True,
                                                               additional_data=additional_costs,
                                                               scaling_factor=scaling_factor)
        indexes, data_scc_capacities, data_scc_resources = prepare_dfs(df_impact, indexed_on, neg=True,
                                                                       additional_data=additional_gwp,
                                                                       scaling_factor=scaling_factor)

        data_resources = data_resources.drop("avoided", level='type')
        data_scc_resources = data_scc_resources.drop("avoided", level='type')

        data_scc_resources[indexes] = data_scc_resources[indexes] * scc
        data_scc_capacities[indexes] = data_scc_capacities[indexes] * scc

        showlegend = True

    sum_resources = data_resources[indexes].sum(axis=0).reset_index(drop=True)
    sum_capacities = data_capacities[indexes].sum(axis=0).reset_index(drop=True)
    sum_scc_resources = data_scc_resources[indexes].sum(axis=0).reset_index(drop=True)
    sum_scc_capacities = data_scc_capacities[indexes].sum(axis=0).reset_index(drop=True)
    combined_resources = sum_resources + sum_scc_resources
    combined_capacities = sum_capacities + sum_scc_capacities

    x1 = list(range(len(indexes)))
    x2 = [x + 1 / 3 for x in x1]
    xtick = [x + 1 / 6 for x in x1]

    text_capacities = ["<b>" + change_data.loc['x_axis_1', lang] + "</b><br>" + str(custom_round(cp, decimal))
                       for cp in combined_capacities]
    text_resources = ["<b>" + change_data.loc['x_axis_2', lang] + "</b><br>" + str(custom_round(op, decimal))
                      for op in combined_resources]
    pos_resources = data_resources[indexes][data_resources[indexes] > 0].sum(axis=0).astype(int).reset_index(drop=True) + data_scc_resources[indexes][
        data_scc_resources[indexes] > 0].sum(axis=0).astype(int).reset_index(drop=True)

    fig = go.Figure()
    neg_resources = combined_resources - pos_resources
    text_placeholder = 0.04 * max(max(combined_capacities - neg_resources + combined_resources),
                                  max(combined_capacities + combined_resources + neg_resources),
                                  max(combined_resources))

    if add_annotation:
        for i in range(len(indexes)):
            fig.add_annotation(x=x2[i], y=-text_placeholder,
                               text=text_resources[i], font=dict(size=10),
                               textangle=0, align='center', valign='top',
                               showarrow=False)
            fig.add_annotation(x=x1[i], y=-text_placeholder,
                               text=text_capacities[i], font=dict(size=10),
                               textangle=0, align='center', valign='top',
                               showarrow=False
                               )
            fig.add_annotation(x=xtick[i], y=max(combined_capacities[i], pos_resources[i],
                                                 combined_capacities[i] + combined_resources[i]) + text_placeholder,
                               text="<b>Total</b><br>" + str(custom_round(combined_capacities[i] + combined_resources[i], decimal)) + change_data.loc[
                                   'unites', lang],
                               font=dict(size=10, color=cm['darkblue']),
                               textangle=0, align='center', valign='top',
                               showarrow=False
                               )
    for line, tech in data_capacities.iterrows():
        if tech.loc[indexes].sum() > 0:
            fig.add_trace(
                go.Bar(name=tech[label],
                       x=x1,
                       y=tech[indexes],
                       marker_color=tech["ColorPastel"],
                       width=1 / 3,
                       hovertemplate=f'<b>{tech[label]}</b><br>{change_data.loc["keyword", lang]}: %{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
                       legendgroup='group1',
                       legendgrouptitle_text=change_data.loc['x_axis_1', lang],
                       showlegend=True)
            )
        if data_scc_capacities.loc[line, indexes].sum() > 0:
            fig.add_trace(
                go.Bar(name='Impact - ' + data_scc_capacities.loc[line, label],
                       x=x1,
                       y=data_scc_capacities.loc[line, indexes],
                       marker_color=tech["ColorPastel"],
                       marker_pattern_shape="x",
                       width=1 / 3,
                       hovertemplate=f'<b>{tech[label]}</b><br>{change_data.loc["keyword", lang]}: %{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
                       legendgroup='group1',
                       showlegend=False)
            )
    for line, layer in data_resources.iterrows():
        if abs(layer.loc[indexes].sum()) > 0:
            fig.add_trace(
                go.Bar(name=layer[label],
                       x=x2,
                       y=layer[indexes],
                       marker_color=layer["ColorPastel"],
                       width=1 / 3,
                       hovertemplate=f'<b>{layer[label]}</b><br>{change_data.loc["keyword", lang]}: %{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
                       legendgroup='group2',
                       legendgrouptitle_text=change_data.loc['x_axis_2', lang],
                       showlegend=True)
            )
        if abs(data_scc_resources.loc[line, indexes].sum()) > 0:
            fig.add_trace(
                go.Bar(name='Impact - ' + data_scc_resources.loc[line, label],
                       x=x2,
                       y=data_scc_resources.loc[line, indexes],
                       marker_color=layer["ColorPastel"],
                       marker_pattern_shape="x",
                       width=1 / 3,
                       hovertemplate=f'<b>{layer[label]}</b><br>{change_data.loc["keyword", lang]}: %{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
                       legendgroup='group2',
                       showlegend=False)
            )

    fig.add_trace(
        go.Bar(
            name=change_data.loc['total', lang],
            x=xtick,
            y=sum_capacities + sum_resources,
            marker_color=cm['lightblue'],
            width=1 / 6,
            hovertemplate=f'<b>Total</b><br>%{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
            legendgroup='group3',
            legendgrouptitle_text='Total',
            showlegend=showlegend)
    )

    fig.add_trace(
        go.Bar(
            name=change_data.loc['scc_legend', lang],
            x=xtick,
            y=sum_scc_capacities + sum_scc_resources,
            marker_color=cm['lightblue'],
            marker_pattern_shape="x",
            width=1 / 6,
            hovertemplate=f'<b>Total</b><br>%{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
            legendgroup='group3',
            legendgrouptitle_text='Total',
            showlegend=showlegend)
    )

    fig.update_layout(barmode="relative",
                      bargap=0,
                      template='plotly_white',
                      margin=dict(l=50, r=50, t=50, b=50),
                      xaxis=dict(
                          tickmode='array',
                          tickvals=xtick,
                          ticktext=indexes),
                      yaxis=dict(title=change_data.loc['y_axis', lang])
                      )

    if filename is not None:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        if export_format == 'html':
            fig.write_html(filename + '.' + export_format)
        if export_format == 'png' or export_format == 'pdf':
            fig.write_image(filename + '.' + export_format)

    if return_df:
        return fig, pd.concat([data_capacities, data_resources])
    else:
        return fig


def plot_expenses(results, plot='costs', indexed_on='Scn_ID', label='EN_long', premium_version=None, per_m2=False, additional_costs={}, additional_gwp={},
                  scc=0.177,
                  filename=None, export_format='html', scaling_factor=1, return_df=False):
    """
    Plots expenses based on REHO results.

    Parameters
    ----------
    results: dict
        Dictionary of REHO results.
    plot: str
        Choose among those three possibilities:

        - 'costs' for the economic performance indicators,
        - 'gwp' for the global warming potential indicators,
        - 'combined' for a combination of the two indicators, where the emissions are converted into costs using the ``scc`` parameter.
    indexed_on: str
        Whether the results should be grouped on *Scn_ID* or *Pareto_ID*.
    label: str
        Indicates the language to use for the plot. Choose among 'FR_long', 'FR_short', 'EN_long', 'EN_short'.
    premium_version : list
        If enabled, it should be an array containing the retail price and feed-in price of electricity.
    per_m2: bool
        Set to True to obtain the results divided by the total ERA.
    additional_costs: dict
        Additional costs to include (choose between 'isolation', 'mobility', and 'ict') and scaling values.
    additional_gwp: dict
        Additional gwp to include (choose between 'isolation', 'mobility', and 'ict') and scaling values.
    scc: float
        Carbon externalities, expressed in *CHF/kgCO2*. Default value is the *Social Cost of Carbon*, from `Rennert, 2022 <https://www.nature.com/articles/s41586-022-05224-9>`_.
    filename: str
        Name of the file to be saved.
    export_format: str
        Can be either 'html', 'png', or 'pdf'.
    scaling_factor: int/float
        Scales linearly the REHO results for the plot.
    return_df: bool
        A dataframe can be returned for further post-processing or reporting purposes.

    Returns
    ----------
    plotly.graph_objs.Figure
        The generated plotly figure.
    pd.DataFrame
        (Optional) A dataframe for further post-processing or reporting purposes.
    """

    sc = list(results.keys())[0]
    id = list(results[sc].keys())[0]
    era = results[sc][id]['df_Buildings'].ERA

    df_Economics = dict_to_df(results, 'df_Economics')

    change_data = pd.DataFrame()
    change_data.index = ['x_axis_1', 'x_axis_2', 'y_axis', 'keyword', 'total', 'unites', 'leg_1', 'leg_2', 'scc_legend']
    decimal = 0
    lang = re.split('_', label)[0]

    if plot == 'costs':
        change_data['FR'] = ['Coûts', 'Revenus', '[CHF/an]', 'Coûts', 'Total', ' CHF', 'Capacités', 'Ressources', '']
        change_data['EN'] = ['Costs', 'Revenues', '[CHF/y]', 'Costs', 'Total', ' CHF', 'Capacities', 'Resources', '']
        df_costs = df_Economics.xs('costs', level='Perf_type')
        if per_m2:
            df_costs = df_costs / era.sum()
            change_data.loc['y_axis']['FR'] = "Coûts [CHF/m2/an]"
            change_data.loc['y_axis']['EN'] = "Costs [CHF/m2/y]"
            decimal = 1
        indexes, data_capacities, data_resources = prepare_dfs(df_costs, indexed_on, neg=False,
                                                               premium_version=premium_version, additional_data=additional_costs, scaling_factor=scaling_factor)

        data_scc_resources = pd.DataFrame(0, columns=[indexes], index=data_resources.index)
        data_scc_capacities = pd.DataFrame(0, columns=[indexes], index=data_capacities.index)

    elif plot == 'gwp':
        change_data['FR'] = ['Émissions', 'Évitées', 'Émissions [kgCO2/an]', 'Émissions', 'Total', ' kgCO2', 'Capacités', 'Ressources', '']
        change_data['EN'] = ['Emissions', 'Avoided', 'Emissions [kgCO2/y]', 'Emissions', 'Total', ' kgCO2', 'Capacities', 'Resources', '']
        df_impact = df_Economics.xs('impact', level='Perf_type')
        if per_m2:
            df_impact = df_impact / era.sum()
            change_data.loc['y_axis']['FR'] = "Émissions [kgCO2/m2/an]"
            change_data.loc['y_axis']['EN'] = "Emissions [kgCO2/m2/y]"
            decimal = 1
        indexes, data_capacities, data_resources = prepare_dfs(df_impact, indexed_on, neg=False,
                                                               premium_version=premium_version, additional_data=additional_gwp, scaling_factor=scaling_factor)

        data_scc_resources = pd.DataFrame(0, columns=[indexes], index=data_resources.index)
        data_scc_capacities = pd.DataFrame(0, columns=[indexes], index=data_capacities.index)

    elif plot == 'combined':
        change_data['FR'] = ['Coûts', 'Revenus', 'Coûts [CHF/an]', 'Coûts', 'Total', ' CHF', 'Capacités', 'Ressources', 'Impact carbone']
        change_data['EN'] = ['Costs', 'Revenues', 'Costs [CHF/y]', 'Costs', 'Total', ' CHF', 'Capacities', 'Resources', 'Carbon impact']

        df_costs = df_Economics.xs('costs', level='Perf_type')
        df_impact = df_Economics.xs('impact', level='Perf_type')
        if per_m2:
            df_costs = df_costs / era.sum()
            df_impact = df_impact / era.sum()
            change_data.loc['y_axis']['FR'] = "Coûts [CHF/m2/an]"
            change_data.loc['y_axis']['EN'] = "Costs [CHF/m2/y]"
            decimal = 1
        indexes, data_capacities, data_resources = prepare_dfs(df_costs, indexed_on, neg=False,
                                                               additional_data=additional_costs,
                                                               scaling_factor=scaling_factor)
        indexes, data_scc_capacities, data_scc_resources = prepare_dfs(df_impact, indexed_on, neg=False,
                                                                       additional_data=additional_gwp,
                                                                       scaling_factor=scaling_factor)
        data_scc_resources[indexes] = data_scc_resources[indexes] * scc
        data_scc_capacities[indexes] = data_scc_capacities[indexes] * scc

    costs = pd.concat([data_capacities, data_resources.xs('costs', level='type')], keys=['investment', 'operation'], names=['Category'])
    revenues = data_resources.loc[['avoided', 'revenues'], :]
    costs = costs[costs[indexes].sum(axis=1) > 0]
    revenues = revenues[revenues[indexes].sum(axis=1) > 0]
    totex = costs[indexes].sum(axis=0) - revenues[indexes].sum(axis=0)
    revenues = revenues.reindex(columns=costs.columns.tolist())

    costs_scc = pd.concat([data_scc_capacities, data_scc_resources.xs('costs', level='type')], keys=['investment', 'operation'], names=['Category'])
    revenues_scc = data_scc_resources.loc[['avoided', 'revenues'], :]
    totex_scc = costs_scc[indexes].sum(axis=0) - revenues_scc[indexes].sum(axis=0)
    revenues_scc = revenues_scc.reindex(columns=costs_scc.columns.tolist())

    sum_costs = costs[indexes].sum(axis=0).reset_index(drop=True)
    sum_revenues = revenues[indexes].sum(axis=0).reset_index(drop=True)
    sum_scc_costs = costs_scc[indexes].sum(axis=0).reset_index(drop=True)
    sum_scc_revenues = revenues_scc[indexes].sum(axis=0).reset_index(drop=True)

    x1 = list(range(len(indexes)))
    x2 = [x + 1 / 3 for x in x1]
    xtick = [x + 1 / 6 for x in x1]
    combined_costs = sum_costs + sum_scc_costs
    combined_revenues = sum_revenues + sum_scc_revenues
    combined_totex = totex.values + totex_scc.values
    text_revenues = ["<b>" + change_data.loc['x_axis_2', lang] + "</b><br>" + str(custom_round(cp, decimal)) + change_data.loc['unites', lang]
                     for cp in combined_revenues]
    text_costs = ["<b>" + change_data.loc['x_axis_1', lang] + "</b><br>" + str(custom_round(op, decimal)) + change_data.loc['unites', lang]
                  for op in combined_costs]
    text_totex = ["<b>" + change_data.loc['total', lang] + "</b><br>" + str(custom_round(tot, decimal)) + change_data.loc['unites', lang]
                  for tot in combined_totex]

    fig = go.Figure()
    for i in range(len(indexes)):
        fig.add_annotation(x=x2[i], y=-0.04 * max(max(combined_revenues), max(combined_costs)),
                           text=text_revenues[i], font=dict(size=10),
                           textangle=0, align='center', valign='top',
                           showarrow=False)
        fig.add_annotation(x=x1[i], y=-0.04 * max(max(combined_revenues), max(combined_costs)),
                           text=text_costs[i], font=dict(size=10),
                           textangle=0, align='center', valign='top',
                           showarrow=False
                           )
        fig.add_annotation(x=xtick[i], y=combined_costs[i] + 0.04 * max(max(combined_revenues), max(combined_costs)),
                           text=text_totex[i],
                           font=dict(size=10, color=cm['darkblue']),
                           textangle=0, align='center', valign='top',
                           showarrow=False
                           )

    df_scc = costs_scc.xs('investment', level='Category')
    for line, tech in costs.xs('investment', level='Category').iterrows():
        if tech.loc[indexes].sum() > 0:
            fig.add_trace(
                go.Bar(name=tech[label],
                       x=x1,
                       y=tech[indexes],
                       marker_color=tech["ColorPastel"],
                       width=1 / 3,
                       hovertemplate=f'<b>{tech[label]}</b><br>{change_data.loc["keyword", lang]}: %{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
                       legendgroup='group1',
                       legendgrouptitle_text=change_data.loc['leg_1', lang],
                       showlegend=True)
            )
        if line in df_scc.index and df_scc.loc[line, indexes].sum() > 0:
            fig.add_trace(
                go.Bar(name='Impact - ' + df_scc.loc[line, label],
                       x=x1,
                       y=df_scc.loc[line, indexes],
                       marker_color=tech["ColorPastel"],
                       marker_pattern_shape="x",
                       width=1 / 3,
                       hovertemplate=f'<b>{tech[label]}</b><br>{change_data.loc["keyword", lang]}: %{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
                       legendgroup='group1',
                       showlegend=False)
            )
    df_scc = costs_scc.xs('operation', level='Category')
    for line, layer in costs.xs('operation', level='Category').iterrows():
        fig.add_trace(
            go.Bar(name=layer[label],
                   x=x1,
                   y=layer[indexes],
                   marker_color=layer["ColorPastel"],
                   width=1 / 3,
                   hovertemplate=f'<b>{layer[label]}</b><br>{change_data.loc["keyword", lang]}: %{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
                   legendgroup='group2',
                   legendgrouptitle_text=change_data.loc['leg_2', lang],
                   showlegend=True)
        )
        if line in df_scc.index and df_scc.loc[line, indexes].sum() > 0:
            fig.add_trace(
                go.Bar(name='Impact - ' + df_scc.loc[line, label],
                       x=x1,
                       y=df_scc.loc[line, indexes],
                       marker_color=layer["ColorPastel"],
                       marker_pattern_shape="x",
                       width=1 / 3,
                       hovertemplate=f'<b>{layer[label]}</b><br>{change_data.loc["keyword", lang]}: %{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
                       legendgroup='group1',
                       showlegend=False)
            )

    fig.add_trace(
        go.Bar(name=change_data.loc['total', lang],
               x=x2,
               y=combined_totex,
               opacity=0,
               width=1 / 6,
               showlegend=False)
    )

    df_scc = revenues_scc
    for line, layer in revenues.iterrows():
        fig.add_trace(
            go.Bar(name=layer[label],
                   x=x2,
                   y=layer[indexes],
                   marker=dict(color=layer["ColorPastel"]),
                   width=1 / 3,
                   hovertemplate=f'<b>{layer[label]}</b><br>{change_data.loc["keyword", lang]}: %{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
                   legendgroup='group2',
                   legendgrouptitle_text=change_data.loc['x_axis_2', lang],
                   showlegend=True)
        )
        if line in df_scc.index and df_scc.loc[line, indexes].sum() > 0:
            fig.add_trace(
                go.Bar(name='Impact - ' + df_scc.loc[line, label],
                       x=x2,
                       y=df_scc.loc[line, indexes],
                       marker_color=layer["ColorPastel"],
                       marker_pattern_shape="x",
                       width=1 / 3,
                       hovertemplate=f'<b>{layer[label]}</b><br>{change_data.loc["keyword", lang]}: %{{y:.{decimal}f}}{change_data.loc["unites", lang]}',
                       legendgroup='group1',
                       showlegend=False)
            )

    fig.update_layout(barmode="relative",
                      bargap=0,
                      template='plotly_white',
                      margin=dict(l=50, r=50, t=50, b=50),
                      xaxis=dict(
                          tickmode='array',
                          tickvals=xtick,
                          ticktext=indexes),
                      yaxis=dict(title=change_data.loc['y_axis', lang])
                      )

    if filename is not None:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        if export_format == 'html':
            fig.write_html(filename + '.' + export_format)
        if export_format == 'png' or export_format == 'pdf':
            fig.write_image(filename + '.' + export_format)

    if return_df:
        return fig, pd.concat([costs, revenues])
    else:
        return fig


def plot_sankey(df_Results, label='EN_long', color='ColorPastel', filename=None, export_format='html', scaling_factor=1, return_df=False):
    """
    Plots a Sankey plot based on the results DataFrame.

    Parameters
    ----------
    df_Results: pd.DataFrame
        DataFrame coming from REHO results (already extracted from the desired *Scn_ID* or *Pareto_ID*).
    label: str
        Indicate the language to use for the plot. Choose among 'FR_long', 'FR_short', 'EN_long', 'EN_short'.
    color: str
        Indicate the color set to use for the plot. Choose among 'ColorPastel', 'ColorFlash'.
    filename: str
        Name of the file to be saved.
    export_format: str
        Can be either 'html', 'png', or 'pdf'.
    scaling_factor: int/float
        Scales linearly the REHO results for the plot.
    return_df: bool
        A dataframe can be returned for further post-processing or reporting purposes.

    Returns
    ----------
    plotly.graph_objs.Figure
        The generated plotly figure.
    pd.DataFrame
        (Optional) A dataframe for further post-processing or reporting purposes.
    
    """
    source, target, value, label_, color_ = sankey.df_sankey(df_Results, label=label, color=color, precision=2,
                                                             units='MWh', display_label_value=True,
                                                             scaling_factor=scaling_factor)

    fig = go.Figure(data=[go.Sankey(
        orientation="h",
        valueformat=".2f",
        valuesuffix=" MWh",
        node=dict(
            pad=15,
            thickness=20,
            line=dict(color="black", width=0.5),
            label=label_,
            color=color_,
        ),
        link=dict(
            source=source,
            target=target,
            value=value
        ))])

    if filename is not None:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        if export_format == 'html':
            fig.write_html(filename + '.' + export_format)
        if export_format == 'png' or export_format == 'pdf':
            fig.write_image(filename + '.' + export_format)

    if return_df:
        df = pd.DataFrame()
        df["source"] = [label_[int(s)].split("\n")[0] for s in source]
        df["target"] = [label_[int(t)].split("\n")[0] for t in target]
        df["Energy [MWh/yr]"] = value
        return fig, df
    else:
        return fig


def plot_profiles(df_Results, units_to_plot, style='plotly', label='EN_long', color='ColorPastel', resolution='weekly', plot_curtailment=False,
                  filename=None, export_format='html', return_df=False):
    """
    Plots an hourly profile for an entire year of operation.

    Parameters
    ----------
    df_Results: pd.DataFrame
        DataFrame coming from REHO results (already extracted from the desired *Scn_ID* or *Pareto_ID*).
    units_to_plot: list
        Units to be plotted.
    style: str
        Choose between 'plotly' or 'matplotlib'.
    label: str
        Indicate the language to use for the plot. Choose among 'FR_long', 'FR_short', 'EN_long', 'EN_short'.
    color: str
        Indicate the color set to use for the plot. Choose among 'ColorPastel', 'ColorFlash'.
    resolution: str
        Moving average possible, choose between 'monthly', 'weekly', and 'daily'.
    plot_curtailment: bool
        PV curtailment can optionally be plotted.
    filename: str
        Name of the file to be saved.
    export_format: str
        Can be either 'html', 'png', or 'pdf'.
    return_df: bool
        A dataframe can be returned for further post-processing or reporting purposes.

    Returns
    ----------
    plotly.graph_objs.Figure
        The generated plotly figure.
    pd.DataFrame
        (Optional) A dataframe for further post-processing or reporting purposes.

    """
    if resolution == 'monthly':
        items_average = 730
    elif resolution == 'weekly':
        items_average = 168
    elif resolution == 'daily':
        items_average = 24
    else:
        items_average = 1

    units_demand = []
    units_supply = []
    for unit in units_to_plot:
        if unit == "PV":
            units_supply.append(unit)
        elif unit in ["Battery", "EV_district"]:
            units_demand.append(unit)
            units_supply.append(unit)
        else:
            units_demand.append(unit)

    # Grids
    import_elec = df_Results['df_Grid_t'].xs(('Electricity', 'Network'), level=('Layer', 'Hub')).Grid_supply[:-2]
    export_elec = df_Results['df_Grid_t'].xs(('Electricity', 'Network'), level=('Layer', 'Hub')).Grid_demand[:-2]
    ng = df_Results['df_Grid_t'].xs(('NaturalGas', 'Network'), level=('Layer', 'Hub')).Grid_supply[:-2]

    import_profile = np.array([])
    export_profile = np.array([])
    ng_profile = np.array([])
    for i in range(1, 366):
        id = df_Results['df_Index'].PeriodOfYear[i * 24]
        import_profile = np.concatenate((import_profile, import_elec.xs(id)))
        export_profile = np.concatenate((export_profile, export_elec.xs(id)))
        ng_profile = np.concatenate((ng_profile, ng.xs(id)))

    import_profile = moving_average(import_profile, items_average)
    export_profile = moving_average(export_profile, items_average)
    ng_profile = moving_average(ng_profile, items_average)

    # Units
    demands = dict()
    supplies = dict()
    curtailments = dict()
    for unit in units_to_plot:
        df_aggregated = df_Results['df_Unit_t'][df_Results['df_Unit_t'].index.get_level_values('Unit').str.contains(unit)]
        if unit in units_demand:
            demand = df_aggregated.droplevel('Layer').Units_demand[:-2].groupby(['Period', 'Time']).sum()
            demands[unit] = np.array([])
            for i in range(1, 366):
                t = df_Results['df_Index'].PeriodOfYear[i * 24]
                demands[unit] = np.concatenate((demands[unit], demand.xs(t)))
        if unit in units_supply:
            supply = df_aggregated.droplevel('Layer').Units_supply[:-2].groupby(['Period', 'Time']).sum()
            supplies[unit] = np.array([])
            for i in range(1, 366):
                t = df_Results['df_Index'].PeriodOfYear[i * 24]
                supplies[unit] = np.concatenate((supplies[unit], supply.xs(t)))
        if unit == 'PV' and plot_curtailment:
            curtailment = df_aggregated.droplevel('Layer').Units_curtailment[:-2].groupby(['Period', 'Time']).sum()
            curtailments[unit] = np.array([])
            for i in range(1, 366):
                t = df_Results['df_Index'].PeriodOfYear[i * 24]
                curtailments[unit] = np.concatenate((curtailments[unit], curtailment.xs(t)))

    for unit in units_demand:
        demands[unit] = moving_average(demands[unit], items_average)
    for unit in units_supply:
        supplies[unit] = moving_average(supplies[unit], items_average)
    if plot_curtailment:
        curtailments['PV'] = moving_average(curtailments['PV'], items_average)

    idx = list(range(1, len(import_profile) + 1))

    title = 'Energy profiles with a ' + resolution + ' moving average'
    obj_x = 'Time [hours]'
    obj_y = '[kWh]'

    if style == 'matplotlib':
        fig, ax = plt.subplots()
        ax.plot(idx, import_profile, color=layout.loc['Electrical_grid', color],
                label=layout.loc['Electrical_grid', label])
        ax.plot(idx, -export_profile, color=layout.loc['Electrical_grid_feed_in', color],
                label=layout.loc['Electrical_grid_feed_in', label])
        ax.plot(idx, ng_profile, color=layout.loc['NaturalGas', color], label=layout.loc['NaturalGas', label],
                alpha=0.5)
        for unit in units_demand:
            ax.plot(idx, demands[unit], linestyle='--', label=layout.loc[unit, label], color=layout.loc[unit, color])
        for unit in units_supply:
            ax.plot(idx, -supplies[unit], label=layout.loc[unit, label], color=layout.loc[unit, color])
        if plot_curtailment:
            ax.plot(idx, -curtailments['PV'], linestyle='.', label=layout.loc['Curtailment', label],
                    color=layout.loc['Curtailment', color])

        ax.set_title(title)
        ax.set_xlabel(obj_x)
        ax.set_ylabel(obj_y)
        ax.legend(loc='best')

        if filename is not None:
            if not os.path.isdir(os.path.dirname(filename)):
                os.makedirs(os.path.dirname(filename))
            if export_format == 'png' or export_format == 'pdf':
                plt.tight_layout()
                plt.savefig((filename + '.' + export_format), format=export_format, dpi=300)

        return plt

    else:
        fig = go.Figure()

        fig.add_trace(go.Scatter(
            x=idx,
            y=import_profile,
            mode="lines",
            name=layout.loc['Electrical_grid', label],
            line=dict(color=layout.loc['Electrical_grid', color])
        ))

        if export_profile.any() > 0:
            fig.add_trace(go.Scatter(
                x=idx,
                y=-export_profile,
                mode="lines",
                name=layout.loc['Electrical_grid_feed_in', label],
                line=dict(color=layout.loc['Electrical_grid_feed_in', color], dash='dash')
            ))

        if ng_profile.any() > 0:
            fig.add_trace(go.Scatter(
                x=idx,
                y=ng_profile,
                mode="lines",
                name=layout.loc['NaturalGas', label],
                line=dict(color=layout.loc['NaturalGas', color])
            ))

        for unit in units_demand:
            if demands[unit].any() > 0:
                fig.add_trace(go.Scatter(
                    x=idx,
                    y=demands[unit],
                    mode="lines",
                    name=layout.loc[unit, label],
                    line=dict(color=layout.loc[unit, color])
                ))
        for unit in units_supply:
            if supplies[unit].any() > 0:
                fig.add_trace(go.Scatter(
                    x=idx,
                    y=-supplies[unit],
                    mode="lines",
                    name=layout.loc[unit, label],
                    line=dict(color=layout.loc[unit, color], dash='dash')
                ))
        if plot_curtailment:
            fig.add_trace(go.Scatter(
                x=idx,
                y=-curtailments['PV'],
                mode="lines",
                name=layout.loc['Curtailment', label],
                line=dict(color=layout.loc['Curtailment', color], dash='dot')
            ))

        fig.update_layout(
            title_text=title,
            xaxis_title=obj_x,
            yaxis_title=obj_y,
            font=dict(
                size=16,
            )
        )

        if filename is not None:
            if not os.path.isdir(os.path.dirname(filename)):
                os.makedirs(os.path.dirname(filename))
            if export_format == 'html':
                fig.write_html(filename + '.' + export_format)
            if export_format == 'png' or export_format == 'pdf':
                fig.write_image(filename + '.' + export_format)

        if return_df:
            return fig, pd.DataFrame()
        else:
            return fig


def plot_resources(results, label='EN_long', color='ColorPastel', filename=None, export_format='html'):
    """
    Under construction.
    """
    
    scenarios = list(results.keys())
    df_annuals = dict_to_df(results, 'df_Annuals')
    pareto_id = list(results[scenarios[0]].keys())[0]
    df_annuals = df_annuals.loc[(slice(None), pareto_id, slice(None), 'Network')]
    idx = df_annuals.index.levels[1].tolist()
    layers = ['NaturalGas', 'Oil', 'Electricity', 'Wood', 'Data']
    df_resources = pd.DataFrame(0, index=['NaturalGas', 'Oil', 'Electrical_grid', 'Wood', ],
                                columns=['scenario', 'Supply'])
    df_resources = df_resources.merge(layout, left_index=True, right_on='Name')
    df_resources = df_resources.rename({'Electrical_grid': 'Electricity'})
    for scn in scenarios:
        df_resources.loc[:, ['scenario', 'Supply']] = ([scn, df_annuals.loc[scn]['Supply_MWh']])
    df_resources.fillna(0, inplace=True)
    df_resources.reset_index(inplace=True)
    fig = go.Figure()
    for i, layer in enumerate(layers):
        df_to_plot = df_resources[df_resources['Name'] == layer]
        if ~df_to_plot.empty:
            fig.add_trace(
                go.Bar(x=df_to_plot['scenario'], y=df_to_plot['Supply'], name=df_to_plot.iloc[0][label],
                       marker=dict(color=df_to_plot[color]),
                       hovertemplate='<b>' + df_to_plot[label] + '</b>' +
                                     '<br>%{y:.2f} MWh'
                       )
            )
    fig.update_layout(
        barmode="group",
        template='plotly_white',
        yaxis=dict(title="MWh"),
    )

    if filename is not None:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        if export_format == 'html':
            fig.write_html(filename + '.' + export_format)
        if export_format == 'png' or export_format == 'pdf':
            fig.write_image(filename + '.' + export_format)

    return fig


def plot_sunburst_eud(results, label='EN_long', filename=None, export_format='html', scaling_factor=1, return_df=False):
    """
    Plots a Sunburst for End Use Demand (EUD) based on REHO results, grouped by buildings' class.

    Parameters
    ----------
    results: dict
        Dictionary of REHO results.
    label: str
        Indicate the language to use for the plot. Choose among 'FR_long', 'FR_short', 'EN_long', 'EN_short'.
    filename: str
        Name of the file to be saved.
    export_format: str
        Can be either 'html', 'png', or 'pdf'.
    scaling_factor: int/float
        Scales linearly the REHO results for the plot.
    return_df: bool
        A dataframe can be returned for further post-processing or reporting purposes.

    Returns
    ----------
    plotly.graph_objs.Figure
        The generated plotly figure.
    pd.DataFrame
        (Optional) A dataframe for further post-processing or reporting purposes.

    """

    def add_class(row):
        ratio = [float(s) for s in str(row['ratio']).split("/")]
        class_ = row['id_class'].split("/")
        serie = pd.Series(ratio, index=class_).groupby(level=0).sum()
        for key, value in correspondance_dict.items():
            data_to_plot[value].update(data_to_plot[value] + serie * row[key])

    correspondance_dict = {'ERA': 'area_per_class',
                           'SH': 'sh_per_class',
                           'DHW': 'dhw_per_class',
                           'Electricity': 'elec_per_class'}
    classes = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII']
    df_buildings = dict_to_df(results, 'df_Buildings')
    df_annuals = dict_to_df(results, 'df_Annuals').reset_index().pivot(index=['Scn_ID', 'Pareto_ID', 'Hub'],
                                                                       columns='Layer', values='Demand_MWh')
    df_buildings = df_buildings.reset_index().merge(df_annuals, on=['Scn_ID', 'Pareto_ID', 'Hub']).set_index(
        ['Scn_ID', 'Pareto_ID', 'Hub'])
    data_to_plot = pd.DataFrame(0, index=classes, columns=['area_per_class', 'sh_per_class', 'dhw_per_class',
                                                           'elec_per_class'])
    class_names = pd.read_csv(os.path.join(path_to_plotting, 'sia380_1.csv'), index_col='id_class', sep=";")
    data_to_plot = data_to_plot.merge(class_names, left_index=True, right_on='id_class')

    if 'FR' in label.upper().split("_"):
        hover_text = 'Demande en énergie'
        liaison = ' du '
    else:
        hover_text = 'End Use Demand'
        liaison = ' of '

    scenarios = list(results.keys())
    paretos = list(results[scenarios[0]].keys())

    df_buildings.loc[(scenarios[0], paretos[0])].apply(add_class, axis=1)
    child_name = []
    parents_name = []
    text_template = []
    hover_template = []
    values_sun = []
    for i in range(len(classes)):
        [child_name.append(element) for element in [data_to_plot.iloc[i]['class_' + label], "SH", "DHW", "Elec"]]
        [parents_name.append(element) for element in
         ["Total", data_to_plot.iloc[i]['class_' + label], data_to_plot.iloc[i]['class_' + label],
          data_to_plot.iloc[i]['class_' + label]]]
        [values_sun.append(element) for element in [
            data_to_plot.iloc[i]['sh_per_class'] + data_to_plot.iloc[i]['dhw_per_class'] +
            data_to_plot.iloc[i]['elec_per_class'],
            data_to_plot.iloc[i]['sh_per_class'], data_to_plot.iloc[i]['dhw_per_class'],
            data_to_plot.iloc[i]['elec_per_class']]]
        [text_template.append(element) for element in ['%{label}<br>%{percentParent:.2%}',
                                                       '%{label}<br>%{percentParent:.2%}',
                                                       '%{label}<br>%{percentParent:.2%}',
                                                       '%{label}<br>%{percentParent:.2%}']]
        [hover_template.append(element) for element in
         ['<i>%{label}</i><br><b>' + hover_text + ': </b>%{value} MWh<br>%{percentParent:.2%}' + liaison + '%{parent}',
          '<i>%{label}</i><br><b>' + hover_text + ': </b>%{value} MWh<br>%{percentRoot:.2%}' + liaison + '%{root}',
          '<i>%{label}</i><br><b>' + hover_text + ': </b>%{value} MWh<br>%{percentRoot:.2%}' + liaison + '%{root}',
          '<i>%{label}</i><br><b>' + hover_text + ': </b>%{value} MWh<br>%{percentRoot:.2%}' + liaison + '%{root}']]

    values_sun = [round(val * scaling_factor, 2) for val in values_sun]
    fig = go.Figure(go.Sunburst(
        labels=child_name,
        parents=parents_name,
        values=values_sun,
        branchvalues='total',
        name=hover_text,
        hovertemplate=hover_template,
        texttemplate=text_template,
    ))

    fig.update_layout(
        sunburstcolorway=["#413D3A", "#CAC7C7", "#B51F1F", "#007480", "#00A79F", "#FEA993"],
        extendsunburstcolors=True)

    if filename is not None:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        if export_format == 'html':
            fig.write_html(filename + '.' + export_format)
        if export_format == 'png' or export_format == 'pdf':
            fig.write_image(filename + '.' + export_format)

    if return_df:
        df = pd.DataFrame()
        df["Energy Use"] = child_name
        df["Building Type"] = parents_name
        df["Energy Demand"] = values_sun
        return fig, df

    return fig


def plot_unit_monthly(results, unit_to_plot, label='EN_short', filename=None, export_format='html'):
    """
    Generates a monthly bar plot showing the mean energy produced per hour and the installed power for a specific unit.

    Parameters
    ----------
    results: dict
        Dictionary of REHO results.
    unit_to_plot: dict
        Specify the unit to plot and Scn_ID / Pareto_ID from which it should be found.
    label: str
        Indicates the language to use for the plot. Choose among 'FR_long', 'FR_short', 'EN_long', 'EN_short'.
    filename: str
        Name of the file to be saved.
    export_format: str
        Can be either 'html', 'png', or 'pdf'.

    Returns
    ----------
    plotly.graph_objs.Figure
        The generated plotly figure.
    pd.DataFrame
        (Optional) A dataframe for further post-processing or reporting purposes.

    Examples
    --------
    >>> reho_results = pd.read_pickle('results/progressive_scenario.pickle')
    >>> unit_to_plot = {'Unit': 'NG_Boiler', 'Scn_ID': 'fossil', 'Pareto_ID': False}
    >>> plot_unit_monthly(reho_results, unit_to_plot, label='FR_long', filename="my_plot", export_format='png').show()

    """

    def monthly_average_balance(results, unit_to_plot):
        """
        Return data to plot a monthly heat balance.

        :return: pd.Series of House_Q_heating, -House_Q_cooling, House_Q_convection, HeatGains, SolarGains

        """

        df_Unit_t_t = remove_building_from_index(results['df_Unit_t'])
        pos_units = df_Unit_t_t.index.get_level_values('Unit').unique()
        pos_columns_bui = ['House_Q_heating', 'House_Q_cooling', 'House_Q_convection', 'HeatGains', 'SolarGains']

        df_period_time = df_Unit_t_t.index.to_frame()[['Period', 'Time']].reset_index(drop=True)

        df_type = ''
        if unit_to_plot in pos_units:
            df_type = 'unit'
        elif unit_to_plot in pos_columns_bui:
            df_type = 'building'

        # For unit
        if df_type == 'unit':
            if unit_to_plot == 'PV':
                column_to_plot = 'Units_supply'
            else:
                column_to_plot = 'Units_demand'
            df_to_extract = df_Unit_t_t.xs(unit_to_plot, level='Unit').droplevel('Layer')[column_to_plot][:-2]

        elif df_type == 'building':
            # Extract the data
            df_Weather = results['df_Weather']
            df_buildings_th_feature = results['df_Buildings'][['U_h', 'ERA']]

            if unit_to_plot == 'House_Q_convection':
                df_to_extract = pd.DataFrame(0, index=df_period_time, columns=[unit_to_plot])
                # For each building: calculation of the heat flux
                # and addition of the flux of each building in df_heat_building_t
                for b in list(df_buildings_th_feature.index):
                    df_to_extract['House_Q_convection'] = df_buildings_th_feature.loc[b, 'U_h'] * \
                                                          df_buildings_th_feature.loc[b, 'ERA'] * \
                                                          (df_Weather.T_ext - df_Weather.T_in)
                df_to_extract = df_to_extract[:-2]
            else:
                df_to_extract = results['df_Buildings_t'][unit_to_plot][:-2]
        else:
            # Case where the unit is not referenced in df_Unit_t such as for WaterTank
            if 'df_Streams_t' in results.dict_config.keys():
                df_Streams_t = remove_building_from_index(results['df_Streams_t'])
                df_to_extract = df_Streams_t.xs(unit_to_plot, level='Unit')['Streams_Q'][:-2]
            else:
                idx = pd.MultiIndex.from_frame(df_period_time)
                units_mult = remove_building_from_index(results['df_Unit']).reset_index().groupby('Unit').sum()[
                    'Units_Mult']
                df_to_extract = pd.Series(units_mult, index=idx)

        month_values = monthly_average(results, df_to_extract)

        return month_values

    design = layout.loc[unit_to_plot['Unit']]

    # Filter the right results dictionary from the REHO results dictionary
    if 'Scn_ID' not in unit_to_plot.keys() or not unit_to_plot['Scn_ID']:
        scn_id = list(results.keys())[0]
        if 'Pareto_ID' not in unit_to_plot.keys() or not unit_to_plot['Pareto_ID']:
            pareto_id = list(results[scn_id].keys())[0]
        else:
            pareto_id = unit_to_plot['Pareto_ID']
    else:
        scn_id = unit_to_plot['Scn_ID']
        pareto_id = 0
    unit_to_plot = unit_to_plot['Unit']
    df_Results = results[scn_id][pareto_id]

    month_values = monthly_average_balance(df_Results, unit_to_plot)
    sized = remove_building_from_index(df_Results['df_Unit']).loc[unit_to_plot]['Units_Mult']

    title_y = 'Energy [kWh/h]'
    power = 'Power installed [kW]'
    energy = 'Mean energy produced per hour [kWh/h]'
    title = 'Power installed and energy produced for {}'.format(design.loc[label])

    if 'FR' in label:
        locale.setlocale(locale.LC_TIME, 'fr_FR.UTF-8')
        title_y = 'Energie [kWh/h]'
        power = 'Puissance installée [kW]'
        energy = 'Energie moyenne produite par heure [kWh/h]'
        title = 'Puissance installée et énergie produite pour {}'.format(design.loc[label])
    if 'short' in label:
        month_ticks = list(calendar.month_abbr)[1:]
    else:
        month_ticks = list(calendar.month_name)[1:]

    # month_ticks = [month.encode('latin1').decode('utf-8') for month in month_ticks]

    fig = go.Figure(
        go.Bar(
            name=energy,
            x=list(range(1, 13)), y=month_values,
            width=1, showlegend=False,
            hovertemplate="<b>" + design.loc[label] + "</b><br>%{y:.0f} kWh/h",
            marker=dict(color=design.loc['ColorPastel']),
        ),
        go.Layout(
            template='plotly_white',
            bargap=0,
            xaxis=dict(
                tickmode='array',
                tickvals=list(range(1, 13)),
                ticktext=month_ticks
            ),
            yaxis=dict(title=title_y),
            title=title
        )
    )
    if sized is not None:
        max_y = sized
        fig.add_trace(
            go.Bar(
                name=power,
                x=[6.5], y=[max_y],
                width=12, showlegend=False,
                hovertemplate="<b>" + design.loc[label] + "</b><br>%{y:.0f} kW",
                marker=dict(color=design.loc['ColorPastel'], opacity=0.3),
            )
        )
        fig.update_layout(yaxis=dict(range=[0, max_y]))

    if filename is not None:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        if export_format == 'html':
            fig.write_html(filename + '.' + export_format)
        if export_format == 'png' or export_format == 'pdf':
            fig.write_image(filename + '.' + export_format)

    return fig


def plot_pareto(results, label='EN_long', color='ColorPastel', return_df=False):
    """
    Plots a Pareto front based on REHO results.

    Parameters
    ----------
    results: dict
        Dictionary of REHO results.
    label: str
        Indicates the language to use for the plot. Choose among 'FR_long', 'FR_short', 'EN_long', 'EN_short'.
    color: str
    Indicate the color set to use for the plot. Choose among 'ColorPastel', 'ColorFlash'.
    return_df: bool
        A dataframe can be returned for further post-processing or reporting purposes.

    Returns
    ----------
    plotly.graph_objs.Figure
        The generated plotly figure.
    pd.DataFrame
        (Optional) A dataframe for further post-processing or reporting purposes.
    """

    df_performance = dict_to_df(results, 'df_Performance').loc[
        (slice(None), slice(None), 'Network'), ["Costs_op", "Costs_inv", "Costs_grid_connection", "Costs_rep", "GWP_op", "GWP_constr"]].reset_index(
        ['Scn_ID', 'Hub'])
    era = dict_to_df(results, 'df_Buildings').loc[(slice(None), 1, slice(None))].ERA.sum()
    df_performance["CAPEX"] = df_performance["Costs_inv"] + df_performance["Costs_rep"]
    df_performance["OPEX"] = df_performance["Costs_op"] + df_performance["Costs_grid_connection"]
    df_performance["TOTEX"] = df_performance["CAPEX"] + df_performance["OPEX"]
    df_performance["GWP"] = df_performance["GWP_op"] + df_performance["GWP_constr"]

    fig = make_subplots(specs=[[{"secondary_y": True}]])

    fig.add_trace(go.Scatter(
        x=list(df_performance.index),
        y=df_performance["CAPEX"].round(2),
        marker=dict(color=layout.loc["CAPEX", color]),
        line=dict(dash='dash'),
        mode="lines+markers",
        name="CAPEX",
    ))

    fig.add_trace(go.Scatter(
        x=list(df_performance.index),
        y=df_performance["OPEX"].round(2),
        marker=dict(color=layout.loc["OPEX", color]),
        line=dict(dash='dash'),
        mode="lines+markers",
        name="OPEX",
    ))

    fig.add_trace(go.Scatter(
        x=list(df_performance.index),
        y=df_performance["TOTEX"].round(2),
        marker=dict(color=layout.loc["TOTEX", color]),
        mode="lines+markers",
        name="TOTEX",
    ))

    fig.add_trace(go.Scatter(
        x=list(df_performance.index),
        y=df_performance["GWP"].round(2),
        marker=dict(size=10, color=layout.loc["GWP", color], symbol='diamond'),
        mode="markers+text",
        name="GWP",
        text=df_performance["GWP"].round(2),
        textposition="top right",
        yaxis="y2",
    ))

    fig.update_layout(
        template='plotly_white',
        xaxis=dict(
            title="Scenario",
        ),
        yaxis=dict(
            title="Costs [CHF/yr]",
            titlefont=dict(
                color=layout.loc["TOTEX", color]
            ),
            tickfont=dict(
                color=layout.loc["TOTEX", color]
            )
        ),
        yaxis2=dict(
            title="GWP [kgkgCO2/yr]",
            titlefont=dict(
                color=layout.loc["GWP", color]
            ),
            tickfont=dict(
                color=layout.loc["GWP", color]
            )
        )
    )

    if return_df:
        df = df_performance[['CAPEX', 'OPEX', 'TOTEX', 'GWP']].round(2)
        df["Scenario"] = np.arange(1, len(df) + 1)
        return fig, df[["Scenario", 'CAPEX', 'OPEX', 'TOTEX', 'GWP']]
    else:
        return fig


def plot_pareto_by_objectives(results, name_list=None, objectives=["CAPEX", "OPEX"], style='plotly', annotation="TOTEX", annot_offset=0, legend=True,
                              filename=None, export_format='png'):
    """
    # TO DO
    """
    df_performance_dict = {}
    # for results in results_list:
    df_performance = dict_to_df(results, 'df_Performance').loc[
        (slice(None), slice(None), 'Network'), ["Costs_op", "Costs_inv", "Costs_grid_connection", "Costs_rep",
                                                "GWP_op", "GWP_constr"]].reset_index(['Scn_ID', 'Hub'])
    era = dict_to_df(results, 'df_Buildings').loc[(slice(None), 1, slice(None))].ERA.sum()
    df_performance["CAPEX"] = df_performance["Costs_inv"] + df_performance["Costs_rep"]
    df_performance["OPEX"] = df_performance["Costs_op"] + df_performance["Costs_grid_connection"]
    df_performance["TOTEX"] = df_performance["CAPEX"] + df_performance["OPEX"]
    df_performance["GWP"] = df_performance["GWP_op"] + df_performance["GWP_constr"]
    df_performance_dict[df_performance.loc[1, "Scn_ID"]] = df_performance[["CAPEX", "OPEX", "TOTEX", "GWP"]].sort_values(by=objectives[0]) / era

    if name_list is None:
        name_list = list(df_performance_dict.keys())

    if objectives[0] == "CAPEX":
        obj_x = "CAPEX [CHF/m$^2$yr]"
    elif objectives[0] == "TOTEX":
        obj_x = "TOTEX [CHF/m$^2$yr]"
    if objectives[1] == "OPEX":
        obj_y = "OPEX [CHF/m$^2$yr]"
    elif objectives[1] == "GWP":
        obj_y = "GWP [kgkgCO2/m$^2$yr]"

    if style == 'matplotlib':

        fig, ax = plt.subplots()

        for i, scenario in enumerate(list(df_performance_dict.keys())):
            ax.plot(df_performance_dict[scenario][objectives[0]], df_performance_dict[scenario][objectives[1]],
                    marker='.', linestyle='--', color=cm[list(cm.keys())[i + 3]])
            for sc_i in df_performance_dict[scenario].index:
                if annotation is not None:
                    value = format((df_performance_dict[scenario].loc[sc_i, annotation]), '.2f')
                    ax.annotate(str(sc_i) + ": " + str(value),
                                xy=(df_performance_dict[scenario].loc[sc_i, objectives[0]] + annot_offset,
                                    df_performance_dict[scenario].loc[sc_i, objectives[1]] + annot_offset),
                                color=cm[list(cm.keys())[i + 3]],
                                size=10)

        plt.title(objectives[0] + "-" + objectives[1] + " Pareto")
        plt.xlabel(obj_x)
        plt.ylabel(obj_y)

        if legend:
            plt.legend(labels=name_list, loc='upper right', fancybox=True, shadow=True)

        if filename is not None:
            if not os.path.isdir(os.path.dirname(filename)):
                os.makedirs(os.path.dirname(filename))
            if export_format == 'png' or export_format == 'pdf':
                plt.tight_layout()
                plt.savefig((filename + '.' + export_format), format=export_format, dpi=300)

        return plt

    else:

        fig = go.Figure()

        for scenario in df_performance_dict.keys():
            fig.add_trace(go.Scatter(
                x=df_performance_dict[scenario][objectives[0]].round(2),
                y=df_performance_dict[scenario][objectives[1]].round(2),
                mode="lines+markers+text",
                name=scenario,
                text=df_performance_dict[scenario][annotation].round(2),
                textposition="top right"
            ))

        if objectives[0] == "CAPEX":
            obj_x = "CAPEX [CHF/m2/yr]"
        elif objectives[0] == "TOTEX":
            obj_x = "TOTEX [CHF/m2/yr]"

        if objectives[1] == "OPEX":
            obj_y = "OPEX [CHF/m2/yr]"
        elif objectives[1] == "GWP":
            obj_y = "GWP [kgkgCO2/m2/yr]"

        fig.update_layout(
            title_text=objectives[0] + "-" + objectives[1] + " Pareto",
            xaxis_title=obj_x,
            yaxis_title=obj_y,
            font=dict(
                size=16,
            )
        )

        if filename is not None:
            if not os.path.isdir(os.path.dirname(filename)):
                os.makedirs(os.path.dirname(filename))
            if export_format == 'html':
                fig.write_html(filename + '.' + export_format)
            if export_format == 'png' or export_format == 'pdf':
                fig.write_image(filename + '.' + export_format)

        return fig


################################################################################################################################################################
# Matplotlib
################################################################################################################################################################


def plot_pareto_units(results, objectives=["CAPEX", "OPEX"], label='EN_long', color='ColorPastel', opex_line=False, title=None, filename=None,
                      export_format='png'):
    """
    TO DO
    """
    fig, ax = plt.subplots()
    fig.set_size_inches(10, 5)

    if not isinstance(results, list):
        results = [results]
    nb_pareto = len(results)

    for id_res, res in enumerate(results):
        scenario = list(res.keys())[0]
        ids = list(res[scenario].keys())
        era = res[scenario][ids[0]]['df_Buildings'].ERA.sum()

        df_unit = dict_to_df(res, 'df_Unit')
        df_performance = dict_to_df(res, 'df_Performance')

        df_performance = df_performance.xs((scenario, 'Network'), level=('Scn_ID', 'Hub'))
        df_performance["CAPEX"] = df_performance["Costs_inv"] + df_performance["Costs_rep"]
        df_performance["OPEX"] = df_performance["Costs_op"] + df_performance["Costs_grid_connection"]
        df_performance["TOTEX"] = df_performance["CAPEX"] + df_performance["OPEX"]
        df_performance["GWP"] = df_performance["GWP_op"] + df_performance["GWP_constr"]
        df_performance = df_performance.sort_values(by=objectives[0])

        if objectives[0] == "CAPEX":
            units_stack = "Costs_Unit_inv"
        elif objectives[0] == "TOTEX":
            units_stack = "GWP_Unit_constr"

        if objectives[1] == "OPEX":
            grid_stack = "Costs_op"
        if objectives[1] == "GWP":
            grid_stack = "GWP_op"

        PV = df_unit[df_unit.index.get_level_values('Unit').str.contains('PV')]
        PV = PV.groupby(level='Pareto_ID', sort=False).sum() / era

        EH = df_unit[df_unit.index.get_level_values('Unit').str.contains('ElectricalHeater')]
        EH = EH.groupby(level='Pareto_ID', sort=False).sum() / era

        BAT = df_unit[df_unit.index.get_level_values('Unit').str.contains('Battery')]
        BAT = BAT.groupby(level='Pareto_ID', sort=False).sum() / era
        BAT["Costs_Unit_inv"] = BAT["Costs_Unit_inv"] + df_performance["Costs_rep"] / era

        BO = df_unit[df_unit.index.get_level_values('Unit').str.contains('NG_Boiler')]
        BO = BO.groupby(level='Pareto_ID', sort=False).sum() / era

        HP = df_unit[df_unit.index.get_level_values('Unit').str.contains('HeatPump')]
        HP = HP.groupby(level='Pareto_ID', sort=False).sum() / era

        HS = df_unit[df_unit.index.get_level_values('Unit').str.contains('Tank')]
        HS = HS.groupby(level='Pareto_ID', sort=False).sum() / era

        idx = np.arange(0, 1, 1 / (len(PV.index)))
        if opex_line:
            width = 0.4 * 2 / (len(PV.index) * nb_pareto)
            shift_list = [[0], [-0.5, 0.5], [-1, 0, 1]][nb_pareto - 1]
            shift = shift_list[id_res]
            hatch = ["", "//", "."]
            linestyle = ["-", "--", ":"]
        else:
            width = 0.4 * 1 / len(PV.index)
            shift = -0.5
            hatch = [""]

        # Plotting
        ax.bar((idx + shift * width), BO[units_stack], label=layout.loc['Boiler', label], width=width,
               color=layout.loc['Boiler', color],
               edgecolor='black', hatch=hatch[id_res])
        ax.bar((idx + shift * width), HP[units_stack], bottom=BO[units_stack], label=layout.loc['HeatPump_Air', label],
               width=width,
               color=layout.loc['HeatPump_Air', color],
               edgecolor='black', hatch=hatch[id_res])
        ax.bar((idx + shift * width), EH[units_stack], bottom=HP[units_stack] + BO[units_stack],
               label=layout.loc['ElectricalHeater', label], width=width, color=layout.loc['ElectricalHeater', color],
               edgecolor='black', hatch=hatch[id_res])
        ax.bar((idx + shift * width), PV[units_stack],
               bottom=EH[units_stack] + HP[units_stack] + BO[units_stack],
               label=layout.loc['PV', label], width=width, color=layout.loc['PV', color],
               edgecolor='black', hatch=hatch[id_res])
        ax.bar((idx + shift * width), HS[units_stack],
               bottom=PV[units_stack] + EH[units_stack] + HP[units_stack] + BO[units_stack],
               label=layout.loc['WaterTankSH', label], width=width, color=layout.loc['WaterTankSH', color],
               edgecolor='black', hatch=hatch[id_res])
        ax.bar((idx + shift * width), BAT[units_stack],
               bottom=HS[units_stack] + PV[units_stack] + EH[units_stack] + HP[units_stack] + BO[units_stack],
               label=layout.loc['Battery', label], width=width, color=layout.loc['Battery', color],
               edgecolor='black', hatch=hatch[id_res])

        if not opex_line:
            ax.bar((idx + 0.5 * width), df_performance[grid_stack] / era, label="Resources", width=width,
                   color=cm['salmon'],
                   edgecolor='black')
            if objectives[1] == "OPEX":
                ax.bar((idx + 0.5 * width), df_performance.Costs_grid_connection / era,
                       bottom=df_performance.Costs_op.clip(lower=0) / era,
                       label='Grid connection', width=width, color=cm['salmon_light'], edgecolor='black')
        else:
            ax.plot(idx, df_performance[grid_stack] / era, label="Resources", color=cm['salmon'],
                    linestyle=linestyle[id_res])
            ax.plot(idx, (df_performance[grid_stack] + df_performance.Costs_inv) / era, label="TOTEX", color="red",
                    linestyle=linestyle[id_res])
    ax.axhline(0, color='black', linewidth=0.8)

    ax.set_xticks(idx)
    ax.set_xticklabels(round(df_performance.Costs_inv / era, 1).values)

    if title:
        plt.title(title)
    else:
        plt.title(objectives[0] + "-" + objectives[1] + " Pareto : " + str(scenario))

    if objectives[0] == "CAPEX":
        obj_x = "CAPEX [CHF/m$^2$yr]"
    elif objectives[0] == "TOTEX":
        obj_x = "TOTEX [CHF/m$^2$yr]"

    if objectives[1] == "OPEX":
        obj_y = "Costs [CHF/m$^2$yr]"
    elif objectives[1] == "GWP":
        obj_y = "GWP [kgkgCO2/m$^2$yr]"

    plt.xlabel(obj_x)
    plt.ylabel(obj_y)

    by_label = merge_handles_labels([plt.gca()])
    plt.legend(by_label.values(), by_label.keys(), ncol=2, loc="upper left")

    if nb_pareto > 1:
        hatch_pareto = ['', r'\\\\', r'...']
        label_pareto = ['coordinated', 'uncoordinated', 'centralised'][0:nb_pareto]
        ax1 = ax.twinx()
        ax1.set_yticks([])
        circ = []
        for i in range(nb_pareto):
            circ = circ + [(mpatches.Patch(facecolor='white', edgecolor='black', hatch=hatch_pareto[i]),
                            Line2D([0], [0], color="black", linestyle=linestyle[i]))]
        ax1.legend(circ, label_pareto, loc='lower left', frameon=False, ncol=nb_pareto,
                   handler_map={tuple: HandlerTuple(ndivide=None)}, handlelength=5)

    if filename is not None:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        if export_format == 'png' or export_format == 'pdf':
            plt.tight_layout()
            plt.savefig((filename + '.' + export_format), format=export_format, dpi=300)

    return plt


def plot_gwp_KPIs(results, filename=None, export_format='png'):
    """
    TO DO
    """
    scenario = list(results.keys())[0]
    ids = list(results[scenario].keys())

    dict_df_perf = {}
    GWP_OP_list = []
    GWP_CONSTR_list = []
    GWP_TOT_list = []
    Cost_inv_list = []  # not necessary
    for id in ids:
        dict_df_perf.update({id: pd.DataFrame.from_dict(results[scenario][id]['df_Performance'])})
        GWP_OP_list.append(dict_df_perf[id].loc['Network', 'GWP_op'])
        GWP_CONSTR_list.append(dict_df_perf[id].loc['Network', 'GWP_constr'])
        GWP_TOT_list.append(dict_df_perf[id].loc['Network', 'GWP_op'] + dict_df_perf[id].loc['Network', 'GWP_constr'])
        Cost_inv_list.append(dict_df_perf[id].loc['Network', 'Costs_inv'])

    df = pd.DataFrame({'scenario': ids, 'GWP_Op': GWP_OP_list, 'GWP_Constr': GWP_CONSTR_list, 'GWP_Tot': GWP_TOT_list,
                       'Cost_Inv': Cost_inv_list})

    df.plot('scenario', y=['GWP_Op', 'GWP_Constr', 'GWP_Tot'], marker='.', linestyle='--')

    plt.xlabel('Scenario')
    plt.ylabel('GWP')
    # plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.20),
    #          fancybox=True, shadow=True, ncol=3)

    if filename is not None:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        if export_format == 'png' or export_format == 'pdf':
            plt.tight_layout()
            plt.savefig((filename + '.' + export_format), format=export_format, dpi=300)

    return plt


def plot_LCOE(results, KPI_list, era, idx=None):
    """
    TO DO
    """
    fig, ax = plt.subplots(2, figsize=(5.5, 7))
    ax2 = ax[0].twinx()

    for id_res, res in enumerate(results):
        style = ["-", "--", ":"][id_res]
        if idx is None:
            idx = np.array([res[0][i]["df_Performance"].xs("Network")["Costs_inv"] for i in res[0]]) / era
        EPFL_light_grey = '#CAC7C7'
        EPFL_red = '#FF0000'
        EPFL_leman = '#00A79F'
        EPFL_canard = '#007480'
        Salmon = '#FEA993'
        colors = {"SC": EPFL_light_grey, "PVP": EPFL_red, "LCoE1": Salmon, "SS": EPFL_canard,
                  "gwp_tot_m2": "black", "gwp_constr_m2": "black", "gwp_op_m2": "black",
                  "Import max": "black", "Export max": EPFL_red, "electricity import": EPFL_light_grey,
                  "electricity export": Salmon, "natural gas import": "black"}

        data_annual = {"Import max": [res[0][i]["df_Grid_t"].xs(("Electricity", "Network"), level=(0, 1))["Grid_supply"][0:-2].max() for i in res[0]],
                       "Export max": [res[0][i]["df_Grid_t"].xs(("Electricity", "Network"), level=(0, 1))["Grid_demand"][0:-2].max() for i in res[0]],
                       "electricity import": [res[0][i]["df_Annuals"].xs(("Electricity", "Network"), level=(0, 1))["Supply_MWh"][0] for i in res[0]],
                       "electricity export": [res[0][i]["df_Annuals"].xs(("Electricity", "Network"), level=(0, 1))["Demand_MWh"][0] for i in res[0]],
                       "natural gas import": [res[0][i]["df_Annuals"].xs(("NaturalGas", "Network"), level=(0, 1))["Supply_MWh"][0] for i in res[0]]
                       }

        for kpi in KPI_list:
            kpi_res = [res[0][i]["df_KPI"].xs("Network")[kpi] for i in res[0]]
            ax[0].plot(idx, kpi_res, marker='.', linestyle=style, color=colors[kpi], label=kpi)
        ax[0].set_ylabel('performance [kWh/kWh]', color="black")
        lcoe = np.array([res[0][i]["df_KPI"].xs("Network")["LCoE1"] for i in res[0]]) * 100
        ax2.plot(idx, lcoe, marker='.', linestyle=style, color=colors["LCoE1"], label="LCoE1")

        for key in data_annual:
            ax[1].plot(idx, data_annual[key], marker='.', linestyle=style, color=colors[key], label=key)
        ax[1].set_ylabel('energy flows [GWh]', color="black")
        ax[1].set_xlabel('capital cost [CHF/m$^2$]', color="black")

    # legend system design
    ax2.spines["right"].set_color(colors["LCoE1"])
    ax2.tick_params(axis='y', colors=colors["LCoE1"])
    ax2.set_ylabel('LCOE [cts CHF/kWh]', color=colors["LCoE1"])

    by_label = merge_handles_labels([ax[0], ax2])
    ax[0].legend(by_label.values(), by_label.keys())
    by_label = merge_handles_labels([ax[1]])
    ax[1].legend(by_label.values(), by_label.keys())

    axx = ax[1].twinx()
    custom_lines = [Line2D([0], [0], color='black', linewidth=1.5),
                    Line2D([0], [0], color='black', linewidth=1.5, linestyle='--')]
    axx.legend(custom_lines, ['coordinated', 'uncoordinated'], bbox_to_anchor=(0.85, -0.2), frameon=False, ncol=2,
               title='system design')
    axx.set_axis_off()
    plt.tight_layout()

    return plt


def plot_composite_curve(results, cluster, periods=["Yearly"], filename=None, export_format='png'):
    """
    TO DO
    """
    # process results data
    df_heat = results["df_Buildings_t"][["House_Q_heating", "House_Q_cooling", "Th_supply"]]
    df_heat_T = pd.DataFrame(np.round(df_heat['Th_supply'], 1).values, columns=["temperature"])
    df_heat_T.index = df_heat.index
    df_heat = pd.concat([df_heat, df_heat_T], axis=1)
    df_heat = df_heat.set_index("temperature", append=True)
    df_heat = df_heat.drop(columns="Th_supply")
    df_heat = df_heat.groupby(["Period", "temperature"], level=[1, 3]).sum()

    # get index typical periods
    file_ID = weather.get_cluster_file_ID(cluster)
    file_name = "index_" + file_ID + ".dat"
    thisfile = os.path.join(path_to_clustering, file_name)
    df = np.loadtxt(thisfile, skiprows=1, max_rows=8760)
    df = pd.DataFrame(df).set_index(0)

    # calculate monthly heat load profile, raw data
    t = [1, 744, 672, 744, 720, 744, 720, 744, 744, 720, 744, 720, 744]
    t = np.cumsum(t).tolist()
    monthly_profile = {}
    for i in range(len(t) - 1):
        month_time = df.loc[t[i]:t[i + 1] - 1]
        month_TP_rep = month_time.groupby(1).count() / 24
        profile = pd.concat([df_heat.xs(p) * month_TP_rep[2].xs(p) for p in month_TP_rep.index])
        profile = profile.groupby("temperature").sum()
        monthly_profile[i] = profile
    monthly_profile[12] = pd.concat([monthly_profile[i] for i in monthly_profile]).groupby("temperature").sum()

    # calculate monthly heat load profile stacked
    duration = [744, 672, 744, 720, 744, 720, 744, 744, 720, 744, 720, 744, 8760]
    month = ["January", "February", "March", "April", "May", "June", "July", "August",
             "September", "October", "November", "December", "Yearly"]
    data = pd.DataFrame()
    for i in monthly_profile:
        profile = monthly_profile[i].assign(sum=monthly_profile[i].House_Q_heating.values.cumsum())
        profile.columns = ["Heat", "Cooling", "Heat_stacked"]
        profile = profile.assign(sum=profile.Cooling.values.cumsum())
        profile.columns = ["Heat", "Cooling", "Heat_stacked", "Cooling_stacked"]
        data_month = profile.Heat_stacked / duration[i] / 1000
        data[month[i]] = data_month

    if filename is not None:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        if export_format == 'png' or export_format == 'pdf':
            for i in periods:
                fig, ax = plt.subplots(figsize=(9, 6))
                ax.plot(data[i], data[i].index, color="indianred", label="Space heating")
                plt.ylabel("Temperature [°C]", fontsize=18)
                plt.xlabel("Heat [MW]", fontsize=18)
                plt.title("Composite curve: " + i, fontsize=20)
                plt.legend(fontsize=16)
                plt.xticks(fontsize=16)
                plt.yticks(fontsize=16)
                plt.tight_layout()
                plt.savefig((filename + i + '.' + export_format), format=export_format, dpi=300)

    return data.fillna(0)


def plot_EVs(results, era, label='EN_long', color='ColorPastel'):
    fig, ax = plt.subplots(1, figsize=(5.5, 4.2))
    ax2 = ax.twinx()
    data = {}
    for id_res, res in enumerate(results):
        df_unit = dict_to_df(res, 'df_Unit')
        annuals = dict_to_df(res, 'df_Annuals')

        data["PV"] = df_unit[df_unit.index.get_level_values('Unit').str.contains('PV')]
        data["PV"] = data["PV"].groupby(level='Pareto_ID', sort=False).sum() / 1000

        data["PV_annuals"] = annuals[annuals.index.get_level_values('Hub').str.contains('PV')]
        data["PV_annuals"] = data["PV_annuals"].groupby(level='Pareto_ID', sort=False).sum()["Supply_MWh"]

        data["EVs"] = df_unit[df_unit.index.get_level_values('Unit').str.contains('EV_district')]
        data["EVs"] = data["EVs"].groupby(level='Pareto_ID', sort=False).sum() / 1000

        data["C_tot"] = [res[0][i]["df_Performance"][["Costs_op", "Costs_inv"]].xs("Network").sum() / era for i in
                         res[0]]
        data["C_tot"] = np.array(data["C_tot"]) / [data["C_tot"][0]]

        data["C_op"] = [res[0][i]["df_Performance"][["Costs_op"]].xs("Network").sum() / era for i in res[0]]
        data["C_op"] = np.array(data["C_op"]) / [data["C_op"][0]]

        data["C_cap"] = [res[0][i]["df_Performance"][["Costs_inv"]].xs("Network").sum() / era for i in res[0]]
        data["C_cap"] = np.array(data["C_cap"]) / [data["C_cap"][0]]

        E_reimport = []
        for j in res[0]:
            df_el = res[0][j]["df_Grid_t"].xs("Electricity")["Grid_demand"]
            delta_elec = df_el.drop(df_el.xs("Network", drop_level=False).index).groupby(
                ["Period", "Time"]).sum() - df_el.xs("Network")
            delta_elec = delta_elec.groupby("Period").sum().mul(res[0][1]["df_Time"].dp).sum() / 1000
            E_reimport = E_reimport + [delta_elec]
        data["E_reimport"] = [E_reimport[i] / data["PV_annuals"][i + 1] for i in range(len(E_reimport))]

        data["SC"] = np.array([res[0][i]["df_KPI"]["SC"].xs("Network") for i in res[0]])
        E_dem = np.array([res[0][i]["df_Annuals"].xs("Network", level=1)["Demand_MWh"]["Electricity"] for i in res[0]])
        E_sup = np.array([res[0][i]["df_Annuals"].xs("Network", level=1)["Supply_MWh"]["Electricity"] for i in res[0]])
        NG_sup = np.array([res[0][i]["df_Annuals"].xs("Network", level=1)["Supply_MWh"]["NaturalGas"] for i in res[0]])
        data["E_dem"] = E_dem / E_dem[0]
        data["E_sup"] = E_sup / E_sup[0]
        data["NG_sup"] = NG_sup / NG_sup[0]
        data["NG_sup"][2] = 1.12

        style = ["-", "--", ":"][id_res]
        idx = list(data["EVs"]["Units_Mult"] / data["EVs"]["Units_Mult"].max() * 100)
        ax2.plot(idx, data["SC"], linestyle=style, color=layout.loc['Electricity', color],
                 label="Self-consumption")
        ax.plot(idx, data["C_tot"], linestyle=style, color="red", label=layout.loc['TOTEX', label])
        ax.plot(idx, data["E_sup"], linestyle=style, color=layout.loc['PV_SC', color],
                label="Electricity retail")
        # ax.plot(idx, data["NG_sup"], linestyle=style, color=layout.loc['NaturalGas', color],
        #        label="Gas retail")
        ax.plot(idx, data["E_dem"], linestyle=style, color=layout.loc['Heat', color],
                label="Electricity feed-in")

    # legend system design
    ax.set_ylabel('relative variation [-]', color="black")
    ax.set_xlabel('share of electric mobility [%]', color="black")
    ax2.spines["right"].set_color(layout.loc['Electricity', color])
    ax2.tick_params(axis='y', colors=layout.loc['Electricity', color])
    ax2.set_ylabel('self-consumption [-]', color=layout.loc['Electricity', color])

    by_label = merge_handles_labels([ax, ax2])
    ax.legend(by_label.values(), by_label.keys(), bbox_to_anchor=(0.9, -0.2), frameon=False, ncol=2)

    # axx = ax.twinx()
    # custom_lines = [Line2D([0], [0], color='black', linewidth=1.5),
    #                 Line2D([0], [0], color='black', linewidth=1.5, linestyle='--')]
    # axx.legend(custom_lines, ['coordinated', 'uncoordinated'], bbox_to_anchor=(0.9, -0.18), frameon=False,
    #            ncol=2, title='system design')
    # axx.set_axis_off()
    plt.tight_layout()

    return plt


def plot_load_duration_curve(results, ids, color='ColorPastel', filename=None, export_format='pdf'):
    """
    TO DO
    """
    fig, ax = plt.subplots(figsize=(9, 6))
    axx = ax.twinx()
    linstyles = ["-", ":"]
    idx = list(range(1, 8761))
    col = 0.2
    for res in results:
        for id in ids:
            profile = dict()
            profile[id] = res[0][id]["df_Grid_t"]["Grid_demand"] - res[0][id]["df_Grid_t"]["Grid_supply"]
            profile[id] = profile[id].xs(("Electricity", "Network"), level=("Layer", "Hub"))[:-2]

            periods_profile = np.array([])
            for i in range(1, 11):
                expanded_profile = np.repeat(profile[id].xs(i, level="Period").values, int(res[0][id]["df_Time"].dp[i]))
                periods_profile = np.concatenate((periods_profile, expanded_profile))
            profile[id] = np.sort(periods_profile)
            ax.plot(idx, profile[id][::-1], linstyles[0], color=[1.0 - col, 0.1, col], label="EV " + str(id - 1) + "0%")
            col = col + 0.3
    ax.set_ylabel('transformer exchange [kW]', fontsize=19)
    ax.set_xlabel('time [hours]', fontsize=19)
    ax.legend(loc="upper right", fontsize=17)
    ax.hlines(570, -20, 12000, linewidth=1.0, linestyle="--", color=layout.loc['Electrical_grid', color])
    ax.hlines(-570, -20, 12000, linewidth=1.0, linestyle="--", color=layout.loc['Electrical_grid', color])
    ax.text(5400, 600, 'transformer capacity', color=layout.loc['Electrical_grid', color], fontsize=18)
    ax.text(3700, 380, 'grid export', color="grey", fontsize=18)
    ax.text(3700, -520, 'grid import', color="grey", fontsize=18)
    ax.set_xlim([-2, 8800])
    ax.set_ylim([-1000, 2000])
    plt.rc('xtick', labelsize=16)
    plt.rc('xtick', labelsize=16)
    rect = mpatches.Rectangle((0, 0.15), 8800, 0.37, color="grey", alpha=0.1)
    plt.gca().add_patch(rect)

    custom_lines = [Line2D([0], [0], color='black', linewidth=1.5, ),
                    Line2D([0], [0], color='black', linewidth=1.5, linestyle=':')]
    axx.legend(custom_lines, ['centralized', 'decentralized'], bbox_to_anchor=(0.86, -0.12), frameon=False, ncol=2,
               title='design strategy', fontsize=18, title_fontsize=18)
    axx.set_axis_off()

    if filename is not None:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        if export_format == 'png' or export_format == 'pdf':
            plt.tight_layout()
            plt.savefig((filename + '.' + export_format), format=export_format, dpi=300)

    return plt
