import base64
import datetime
import io
import argparse


import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.dependencies import Input, Output, State

import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots

import pandas as pd


parser = argparse.ArgumentParser(description='Launch dash app to display tubes.')
parser.add_argument('datacsv', metavar='data.csv', type=str, 
                    help='a csv file with bounds on signals')
args = parser.parse_args()

# initial load
init_df = pd.read_csv(args.datacsv,index_col=False)
init_df_json=init_df.to_json(date_format='iso', orient='split')
init_types=init_df['type'].unique()
init_typesel_options=[{'label': t, 'value': t} for t in init_types]
init_signals = init_df['varid'].unique()
init_signals_option = [{'label': i, 'value': i} for i in init_signals]

# TODO: extraire les suffixes de contexte booleens pour construire, pour chaque signal, la liste des signaux de partitionnement


app = dash.Dash(__name__)

app.layout = html.Div([
    dcc.Upload(
        id='upload-data',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        # Allow multiple files to be uploaded
        multiple=True
    ),
    dcc.Dropdown(
        id="signal_sel",
        options=[],
        multi=True,
        value = []
    ),
    dcc.Checklist(
        id="sameplotcheck",
        options=[
            {'label': 'Plot on same graph', 'value': 'samegraph'},
        ],
        value=[]
    ),
    dcc.Checklist(
        id="typesel",
        options=[], 
        value=[] 
    ),
    dcc.Graph(id="graph"),
    #html.Button("Switch Min/Max", id='btn', n_clicks=0),
    html.Div(id='selected-data'),    
    html.Div(id='output-data-upload'),    
    # Hidden div inside the app that stores the intermediate value
    #html.Div(children='Dash: A web application framework for Python.', style={
    #    'textAlign': 'center',
    #    'color': colors['text']
    #}),
    html.Div(id='global_df', style={'display': 'none'},
             children=init_df_json
             )
])

# # @app.callback(
# #     Output("graph", "figure"), 
# #     [Input("btn", "n_clicks")])
# # def display_graph(n_clicks):
# #     if n_clicks % 2 == 0:
# #         x, y = 'timestep', ' __lego_anti_windup_real_10_min'
# #     else:
# #         x, y = 'timestep', ' __lego_anti_windup_real_10_max'

# #     fig = px.line(df, x=x, y=y)    
# #     return fig


def onesigfig(fig, signal_df, name,row,col):
    color='rgba(0,250,0,0.4)'
    x, y_min, y_max = 'timestep', 'min', 'max'
    
    fig.add_traces(
        go.Scatter(
            x = signal_df[x],
            y = signal_df[y_min],
            line = dict(color='rgba(0,0,0,0)',shape='hv'),
            name=None),
        rows=row,cols=col)
    fig.add_traces(
        go.Scatter(
            x = signal_df[x],
            y = signal_df[y_max],
            fill='tonexty',
            line = dict(shape='hv'),
            name=name,
            fillcolor =color),
        rows=row,cols=col)
    fig.add_traces(
        go.Scatter(
            x=signal_df[x],
            y = signal_df[y_max],
            line = dict(color = 'black', width=1,shape='hv'),
            name=name+'_max'),
        rows=row,cols=col)
    fig.add_traces(
        go.Scatter(
            x=signal_df[x],
            y = signal_df[y_min],
            mode='lines',
            line = dict(color = 'black', width=1,shape='hv'),
            name=name+'_min'),
        rows=row,
        cols=col)
#    fig.update_yaxes(type='linear')
    return fig
    # fig.add_traces(go.Scatter(x = signal_df[x], y = signal_df[y_min],
    #                       line = dict(color='rgba(0,0,0,0)')))
    # fig.add_traces(go.Scatter(x = signal_df[x], y = signal_df[y_max],
    #                           fill='tonexty',
    #                           fillcolor =color))
    # fig.add_traces(go.Scatter(x=signal_df[x], y = signal_df[y_max],
    #                   line = dict(color = 'black', width=1)))
    # fig.add_traces(go.Scatter(x=signal_df[x], y = signal_df[y_min],
    #                   line = dict(color = 'black', width=1)))
    
    #fig = px.line(signal_df, x=x, y=[y_min,y_max],line_shape='hv')
    #, fill='tonexty')
    #    fig.add_scatter(df, x=x, y=y_max, mode='lines')

def extract_selection(df):
    children = html.Div([
        html.Hr(),  # horizontal line
        dash_table.DataTable(
            data=df.to_dict('records'),
            columns=[{'name': i, 'id': i} for i in df.columns]
        ),
        html.Hr(),  # horizontal line
    ])
    return children
        
        
# Changing dropdown menu or same/split view updates the figure
@app.callback(
    Output("graph", "figure"),
    [Input("signal_sel", "value"),
     Input("sameplotcheck", "value"),
     Input("global_df", 'children')
     ])
def display_graph_signal(selection,checkval,jsonified):
    if 'samegraph' in checkval:
        plot_on_same_graph = True
    else:
        plot_on_same_graph = False
    color='rgba(0,250,0,0.4)'
    x, y_min, y_max = 'timestep', 'min', 'max'
    if selection is None or selection == [] or jsonified == None:
        return {}
        #selection=signals
    else:
        df=pd.read_json(jsonified, orient='split')
        if plot_on_same_graph:
            nb_rows=1
            fig = make_subplots(rows=nb_rows,
                                cols=1,
                                )
        else:
            nb_rows=len(selection)
            fig = make_subplots(rows=nb_rows,
                                cols=1,
                                subplot_titles=selection)
        for idx, sig in enumerate(selection):
            signal_df = df.query("varid=='" + sig + "'")
            if plot_on_same_graph:
                row_id=1
            else:
                row_id=idx+1
            onesigfig(fig,signal_df,sig,row_id,1)
        fig.update_layout(height=300 * nb_rows)
        return fig

# Changing global_df or choice of types updates dropdown menu
@app.callback(Output("signal_sel", "options"),
              Output("signal_sel", "value"),
              Output('selected-data', 'children'),
              Input("global_df", 'children'),
              Input('typesel', 'value')
              )
def update_available_signals(jsonified, typesel):
    if jsonified == None:
        return [],[]
    else:
        df=pd.read_json(jsonified, orient='split')
        signals_data=df.query('type in ' +str(typesel))
        signals=signals_data['varid'].unique()
        signals_options=[{'label': i, 'value': i} for i in signals]
        data=extract_selection(signals_data)
        return signals_options, signals, data


def parse_contents(contents, filename, date):
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            #print(decoded)
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')),
                index_col=False
            )
            #types=df['type'].unique()
            #signals = df['varid'].unique()
        
    except Exception as e:
        print(e)
        return None, [], [], html.Div([
            'There was an error processing this file.'
        ])

    return df, html.Div([
        html.H5(filename),
        html.H6(datetime.datetime.fromtimestamp(date)),

        dash_table.DataTable(
            data=df.to_dict('records'),
            columns=[{'name': i, 'id': i} for i in df.columns]
        ),

        html.Hr(),  # horizontal line

        # For debugging, display the raw contents provided by the web browser
        html.Div('Raw Content'),
        html.Pre(contents[0:200] + '...', style={
            'whiteSpace': 'pre-wrap',
            'wordBreak': 'break-all'
        })
    ])

# Loading new csv, updates types and global_df
@app.callback(Output("typesel", 'options'),
              Output("typesel", 'value'),
              Output("global_df", 'children'),
              Output('output-data-upload', 'children'),
              Input('upload-data', 'contents'),
              State('upload-data', 'filename'),
              State('upload-data', 'last_modified')
              )
def update_df_and_outputlog(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
        #args= zip(list_of_contents, list_of_names, list_of_dates)
        #c,n,d= args[0]
        df, children = parse_contents(list_of_contents[0], list_of_names[0], list_of_dates[0])
        # for c, n, d in
        
        df_json=df.to_json(date_format='iso', orient='split')
        types=df['type'].unique()
        typesel_options = [{'label': t, 'value': t} for t in types]
        return typesel_options, types, df_json, children
    else:
        return init_typesel_options, init_types, init_df_json, None
        
app.run_server(debug=True)