import base64
import io

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output

import numpy as np
import tensorflow as tf
from PIL import Image

from constants import CLASSES

import yaml 
with open('app.yaml') as yaml_data :
    params = yaml.safe_load(yaml_data)
    
IMAGE_WIDTH = params['IMAGE_WIDTH']
IMAGE_HEIGHT = params['IMAGE_HEIGHT']
PATH_MODEL = params['PATH_MODEL']


# Load DNN model
classifier = tf.keras.models.load_model(PATH_MODEL)

def classify_image(image, model, image_box=None):
  """Classify image by model

  Parameters
  ----------
  content: image content
  model: tf/keras classifier

  Returns
  -------
  class id returned by model classifier
  """
  images_list = []
  image = image.resize((IMAGE_WIDTH, IMAGE_HEIGHT), box=image_box)
                                        # box argument clips image to (x1, y1, x2, y2)
  image = np.array(image)
  images_list.append(image)
  
  return model.predict_classes(np.array(images_list))


app = dash.Dash('Traffic Signs Recognition', external_stylesheets=[dbc.themes.BOOTSTRAP])


pre_style = {
    'whiteSpace': 'pre-wrap',
    'wordBreak': 'break-all',
    'whiteSpace': 'normal'
}


# Define application layout


navbar = dbc.NavbarSimple(
    children=[
        dbc.DropdownMenu(
            children=[
                dbc.DropdownMenuItem('Réseau de Neurones', header=True),
                dbc.DropdownMenuItem('SVM', href="#"),
            ],
            nav=True,
            in_navbar=True,
            label='Modèle',
        ),
    ],
    brand="Menu",
    brand_href="#",
    color= "#d90054",
    dark=True
)

cards = html.Div(  
    [
        dbc.Card(
            dbc.CardBody(
                [
                    html.H5("Présentation", className="card-title"),
                    html.P(
                        [
                            'Cette application à pour but de réaliser des modèles capables de classer des panneaux de signalisation allemand à partir d\'une image. L\'application fonctionne de la manière suivante : vous déposer une image à l\'emplacement indiqué et la prédiction du modèle apparait immédiatement en dessous. En haut à droite vous pouvez sélectionner le modèle que vous voulez tester.',
                        ],
                        className='card-text',
                    ),
                ]
            ),
            className='w-75 mb-3', 
            color='#f1cbd1', 
            outline='Black',  
            style={
                'margin-top': '75px',  
                'margin-left': '185px'}, 
        ),
    ]
)

app.layout = html.Div([
    html.Div([navbar]),
    
    html.Div(cards),
    dcc.Upload(
        id='bouton-chargement',
        children=html.Div([
            'Cliquer-déposer ou ',
                    html.A('sélectionner une image')
        ]),
        style={
            'width': '50%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin-top': '75px',
            'margin-left': '370px', 
        }
    ),
    html.Div(id='mon-image'),
    html.Div(id='ma-zone-resultat')
])

@app.callback(Output('mon-image', 'children'),
              [Input('bouton-chargement', 'contents')])
def update_output(contents):
    if contents is not None:
        content_type, content_string = contents.split(',')
        if 'image' in content_type:
            image = Image.open(io.BytesIO(base64.b64decode(content_string)))
            predicted_class = classify_image(image, classifier)[0]
            return html.Div([
                html.Hr(style={'margin-top': '75px'}),
                html.Img(src=contents, style={'margin-left': '750px'}),
                html.H4('Classe prédite : {}'.format(CLASSES[predicted_class]), style={'textAlign': 'center'}),
                html.Hr(),
                #html.Div('Raw Content'),
                #html.Pre(contents, style=pre_style)
            ])
        else:
            try:
                # Décodage de l'image transmise en base 64 (cas des fichiers ppm)
                # fichier base 64 --> image PIL
                image = Image.open(io.BytesIO(base64.b64decode(content_string)))
                # image PIL --> conversion PNG --> buffer mémoire 
                buffer = io.BytesIO()
                image.save(buffer, format='PNG')
                # buffer mémoire --> image base 64
                buffer.seek(0)
                img_bytes = buffer.read()
                content_string = base64.b64encode(img_bytes).decode('ascii')
                # Appel du modèle de classification
                predicted_class = classify_image(image, classifier)[0]
                # Affichage de l'image
                return html.Div([
                    html.Hr(style={'margin-top': '75px'}),
                    html.Img(src='data:image/png;base64,' + content_string, style={'margin-left': '750px'}),
                    html.H4('Classe prédite : {}'.format(CLASSES[predicted_class]), style={'textAlign': 'center'}),
                    html.Hr(),
                ])
            except:
                return html.Div([
                    html.Hr(),
                    html.Div('Uniquement des images svp : {}'.format(content_type)),
                    html.Hr(),                
                    html.Div('Raw Content'),
                    html.Pre(contents, style=pre_style)
                ])
            

# Manage interactions with callbacks
@app.callback(
    Output(component_id='ma-zone-resultat', component_property='children'),
    [Input(component_id='mon-champ-texte', component_property='value')]
)
def update_output_div(input_value):
    return html.H3('Valeur saisie ici "{}"'.format(input_value))


# Start the application
if __name__ == '__main__':
    app.run_server(debug=True)
[((26, 13, 26, 51), 'tensorflow.keras.models.load_model', 'tf.keras.models.load_model', ({(26, 40, 26, 50): 'PATH_MODEL'}, {}), '(PATH_MODEL)', True, 'import tensorflow as tf\n'), ((49, 6, 49, 89), 'dash.Dash', 'dash.Dash', (), '', False, 'import dash\n'), ((18, 13, 18, 38), 'yaml.safe_load', 'yaml.safe_load', ({(18, 28, 18, 37): 'yaml_data'}, {}), '(yaml_data)', False, 'import yaml\n'), ((43, 10, 43, 25), 'numpy.array', 'np.array', ({(43, 19, 43, 24): 'image'}, {}), '(image)', True, 'import numpy as np\n'), ((130, 14, 130, 45), 'dash.dependencies.Output', 'Output', ({(130, 21, 130, 32): '"""mon-image"""', (130, 34, 130, 44): '"""children"""'}, {}), "('mon-image', 'children')", False, 'from dash.dependencies import Input, Output\n'), ((179, 4, 179, 74), 'dash.dependencies.Output', 'Output', (), '', False, 'from dash.dependencies import Input, Output\n'), ((46, 31, 46, 52), 'numpy.array', 'np.array', ({(46, 40, 46, 51): 'images_list'}, {}), '(images_list)', True, 'import numpy as np\n'), ((105, 4, 105, 22), 'dash_html_components.Div', 'html.Div', ({(105, 13, 105, 21): '[navbar]'}, {}), '([navbar])', True, 'import dash_html_components as html\n'), ((107, 4, 107, 19), 'dash_html_components.Div', 'html.Div', ({(107, 13, 107, 18): 'cards'}, {}), '(cards)', True, 'import dash_html_components as html\n'), ((126, 4, 126, 28), 'dash_html_components.Div', 'html.Div', (), '', True, 'import dash_html_components as html\n'), ((127, 4, 127, 35), 'dash_html_components.Div', 'html.Div', (), '', True, 'import dash_html_components as html\n'), ((131, 15, 131, 53), 'dash.dependencies.Input', 'Input', ({(131, 21, 131, 40): '"""bouton-chargement"""', (131, 42, 131, 52): '"""contents"""'}, {}), "('bouton-chargement', 'contents')", False, 'from dash.dependencies import Input, Output\n'), ((180, 5, 180, 70), 'dash.dependencies.Input', 'Input', (), '', False, 'from dash.dependencies import Input, Output\n'), ((152, 25, 152, 37), 'io.BytesIO', 'io.BytesIO', ({}, {}), '()', False, 'import io\n'), ((85, 20, 85, 68), 'dash_html_components.H5', 'html.H5', (), '', True, 'import dash_html_components as html\n'), ((86, 20, 91, 21), 'dash_html_components.P', 'html.P', (), '', True, 'import dash_html_components as html\n'), ((136, 42, 136, 74), 'base64.b64decode', 'base64.b64decode', ({(136, 59, 136, 73): 'content_string'}, {}), '(content_string)', False, 'import base64\n'), ((139, 16, 139, 53), 'dash_html_components.Hr', 'html.Hr', (), '', True, 'import dash_html_components as html\n'), ((140, 16, 140, 70), 'dash_html_components.Img', 'html.Img', (), '', True, 'import dash_html_components as html\n'), ((142, 16, 142, 25), 'dash_html_components.Hr', 'html.Hr', ({}, {}), '()', True, 'import dash_html_components as html\n'), ((66, 16, 66, 72), 'dash_bootstrap_components.DropdownMenuItem', 'dbc.DropdownMenuItem', (), '', True, 'import dash_bootstrap_components as dbc\n'), ((67, 16, 67, 53), 'dash_bootstrap_components.DropdownMenuItem', 'dbc.DropdownMenuItem', (), '', True, 'import dash_bootstrap_components as dbc\n'), ((112, 20, 112, 53), 'dash_html_components.A', 'html.A', ({(112, 27, 112, 52): '"""sélectionner une image"""'}, {}), "('sélectionner une image')", True, 'import dash_html_components as html\n'), ((150, 46, 150, 78), 'base64.b64decode', 'base64.b64decode', ({(150, 63, 150, 77): 'content_string'}, {}), '(content_string)', False, 'import base64\n'), ((157, 33, 157, 60), 'base64.b64encode', 'base64.b64encode', ({(157, 50, 157, 59): 'img_bytes'}, {}), '(img_bytes)', False, 'import base64\n'), ((162, 20, 162, 57), 'dash_html_components.Hr', 'html.Hr', (), '', True, 'import dash_html_components as html\n'), ((163, 20, 163, 107), 'dash_html_components.Img', 'html.Img', (), '', True, 'import dash_html_components as html\n'), ((165, 20, 165, 29), 'dash_html_components.Hr', 'html.Hr', ({}, {}), '()', True, 'import dash_html_components as html\n'), ((169, 20, 169, 29), 'dash_html_components.Hr', 'html.Hr', ({}, {}), '()', True, 'import dash_html_components as html\n'), ((171, 20, 171, 29), 'dash_html_components.Hr', 'html.Hr', ({}, {}), '()', True, 'import dash_html_components as html\n'), ((172, 20, 172, 43), 'dash_html_components.Div', 'html.Div', ({(172, 29, 172, 42): '"""Raw Content"""'}, {}), "('Raw Content')", True, 'import dash_html_components as html\n'), ((173, 20, 173, 55), 'dash_html_components.Pre', 'html.Pre', (), '', True, 'import dash_html_components as html\n')]