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

import dash_table
import plotly.express as ex
import plotly.graph_objects as go
import pandas as pd
import numpy as np


data = pd.read_csv("./data/Phone_dataset_new.csv", header=0)
details = pd.read_csv("./data/Phone_details.csv", header=0)

names = details.loc[0]

data = data.rename(columns=names)
details = details.rename(columns=names)

maxi = details.loc[1].astype(int)
details_on_card = details.loc[2].astype(int)

details_on_card = details.columns[details_on_card == 1]


fitness_columns = {
    "Memory": -1,
    "RAM": -1,
    "Camera (MP)": -1,
    "Price (Euros)": 1,
}

fitness_data = data[fitness_columns] * maxi[fitness_columns].values


external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash.Dash(
    __name__,
    external_stylesheets=[dbc.themes.LITERA],
    eager_loading=True,
    suppress_callback_exceptions=True,
)


app.layout = html.Div(
    children=[
        # .container class is fixed, .container.scalable is scalable
        dbc.Row(
            [
                dbc.Col(
                    html.H1(
                        children="What is your optimal phone?",
                        className="text-center mt-4",
                    )
                )
            ]
        ),
        dbc.Row(
            [
                dbc.Col(
                    children=[
                        # Top card with details(?)
                        dbc.Card(
                            children=[
                                dbc.CardBody(
                                    [
                                        html.H4(
                                            "Researcher's Night Event",
                                            className="card-title text-center",
                                        ),
                                        html.P(
                                            (
                                                "This app uses decision support tools to "
                                                "quickly and easily find phones which reflect "
                                                "the user's desires. Input your preferences "
                                                "below. The box on top right shows the phone "
                                                "which matches the preferences the best. "
                                                "The box on bottom right provides some "
                                                "close alternatives."
                                            ),
                                            className="card-text",
                                        ),
                                    ]
                                )
                            ],
                            className="mr-3 ml-3 mb-2 mt-2",
                        ),
                        dbc.Form(
                            [
                                dbc.FormGroup(
                                    children=[
                                        dbc.Label(
                                            "Choose desired operating system",
                                            html_for="os-choice",
                                        ),
                                        dbc.RadioItems(
                                            options=[
                                                {
                                                    "label": "Android",
                                                    "value": "Android",
                                                },
                                                {"label": "iOS", "value": "IOS"},
                                                {
                                                    "label": "No preference",
                                                    "value": "both",
                                                },
                                            ],
                                            id="os-choice",
                                            value="both",
                                            inline=True,
                                            # className="text-center mt-4",
                                        ),
                                    ],
                                    className="mr-3 ml-3 mb-2 mt-2",
                                ),
                                dbc.FormGroup(
                                    children=[
                                        dbc.Label(
                                            "Choose desired Memory capacity (GB)",
                                            html_for="memory-choice",
                                        ),
                                        dcc.Slider(
                                            id="memory-choice",
                                            min=16,
                                            max=256,
                                            step=None,
                                            included=False,
                                            value=256,
                                            marks={
                                                16: "16",
                                                32: "32",
                                                64: "64",
                                                128: "128",
                                                256: "256",
                                            },
                                            # className="text-center mt-5",
                                        ),
                                    ],
                                    className="mr-3 ml-3 mb-2 mt-2",
                                ),
                                dbc.FormGroup(
                                    children=[
                                        dbc.Label(
                                            "Choose desired RAM capacity (GB)",
                                            html_for="ram-choice",
                                        ),
                                        dcc.Slider(
                                            id="ram-choice",
                                            min=2,
                                            max=12,
                                            step=1,
                                            value=12,
                                            included=False,
                                            marks={
                                                2: "2",
                                                3: "3",
                                                4: "4",
                                                5: "5",
                                                6: "6",
                                                7: "7",
                                                8: "8",
                                                9: "9",
                                                10: "10",
                                                11: "11",
                                                12: "12",
                                            },
                                            className="text-center mt-5",
                                        ),
                                    ],
                                    className="mr-3 ml-3 mb-2 mt-2",
                                ),
                                dbc.FormGroup(
                                    children=[
                                        dbc.Label(
                                            "Choose desired camera resolution (MP)",
                                            html_for="cam-choice",
                                        ),
                                        dcc.Slider(
                                            id="cam-choice",
                                            min=0,
                                            max=130,
                                            step=1,
                                            included=False,
                                            value=70,
                                            marks={
                                                0: "0",
                                                10: "10",
                                                30: "30",
                                                50: "50",
                                                70: "70",
                                                90: "90",
                                                110: "110",
                                                130: "130",
                                            },
                                            className="text-center mt-5",
                                        ),
                                    ],
                                    className="mr-3 ml-3 mb-2 mt-2",
                                ),
                                dbc.FormGroup(
                                    children=[
                                        dbc.Label(
                                            "Choose desired budget (Euros)",
                                            html_for="cost-choice",
                                        ),
                                        dcc.Slider(
                                            id="cost-choice",
                                            min=0,
                                            max=1400,
                                            step=1,
                                            included=False,
                                            value=100,
                                            marks={
                                                0: "0",
                                                200: "200",
                                                400: "400",
                                                600: "600",
                                                800: "800",
                                                1000: "1000",
                                                1200: "1200",
                                                1400: "1400",
                                            },
                                            className="text-center mt-5",
                                        ),
                                    ],
                                    className="mr-3 ml-3 mb-2 mt-2",
                                ),
                            ],
                            style={"maxHeight": "560px", "overflow": "auto"},
                        ),
                    ],
                    width={"size": 5, "offset": 1},
                ),
                dbc.Col(
                    children=[
                        dbc.Card(
                            children=[
                                dbc.CardHeader("The best phone for you is:"),
                                dbc.CardBody(id="results"),
                            ],
                            className="mb-4",
                        ),
                        dbc.Card(
                            children=[
                                dbc.CardHeader("Other great phones:"),
                                dbc.CardBody(
                                    id="other-results",
                                    children=(
                                        [
                                            html.P(
                                                html.Span(
                                                    f"{i}. ",
                                                    id=f"other-results-list-{i}",
                                                )
                                            )
                                            for i in range(2, 6)
                                        ]
                                        + [
                                            dbc.Tooltip(
                                                id=f"other-results-tooltip-{i}",
                                                target=f"other-results-list-{i}",
                                                placement="right",
                                                style={
                                                    "maxWidth": 700,
                                                    "background-color": "white",
                                                    "color": "white",
                                                    "border-style": "solid",
                                                    "border-color": "black",
                                                },
                                            )
                                            for i in range(2, 6)
                                        ]
                                    ),
                                ),
                            ],
                            className="mt-4",
                        ),
                        html.Div(id="tooltips"),
                    ],
                    width={"size": 5, "offset": 0},
                    className="mb-2 mt-2",
                ),
            ]
        ),
        dbc.Row([html.Div(id="callback-dump")]),
    ],
)


@app.callback(
    [
        Output("results", "children"),
        *[Output(f"other-results-list-{i}", "children") for i in range(2, 6)],
        *[Output(f"other-results-tooltip-{i}", "children") for i in range(2, 6)],
    ],
    [
        Input(f"{attr}-choice", "value")
        for attr in ["os", "memory", "ram", "cam", "cost"]
    ],
)
def results(*choices):
    if choices[0] == "both":
        choice_data = data
    elif choices[0] == "IOS":
        choice_data = data[[True if "IOS" in st else False for st in data["OS"]]]
    if choices[0] == "Android":
        choice_data = data[[True if "Android" in st else False for st in data["OS"]]]
    relevant_data = choice_data[
        ["Memory", "RAM", "Camera (MP)", "Price (Euros)",]
    ].reset_index(drop=True)
    card_data = choice_data[details_on_card].reset_index(drop=True)
    maxi = np.asarray([-1, -1, -1, 1])
    relevant_data = relevant_data * maxi
    ideal = relevant_data.min().values
    nadir = relevant_data.max().values
    aspirations = choices[1:] * maxi
    distance = (aspirations - relevant_data) / (ideal - nadir)
    distance = distance.max(axis=1)
    distance_order = np.argsort(distance)
    best = table_from_data(card_data.loc[distance_order.values[0]], choices[1:])
    total_number = len(distance_order)
    if total_number >= 4:
        others, tooltips = other_options(card_data.loc[distance_order.values[1:5]])
    else:
        others, tooltips = other_options(
            card_data.loc[distance_order.values[1:total_number]]
        )
        others = others + [f"{i}. -" for i in range(len(others) + 2, 6)]
        tooltips = tooltips + [None for i in range(len(tooltips) + 2, 6)]
    return (best, *others, *tooltips)


"""@app.callback(Output("tooltips", "children"), [Input("callback-dump", "children")])
def tooltips(tooldict):
    num = len(tooldict["ids"])
    content = []
    for i in range(num):
        content.append(dbc.Tooltip(tooldict["tables"][i], target=tooldict["ids"][i]))
    return content"""


def table_from_data(data, choices):
    # print(choices)
    to_compare = ["Memory", "RAM", "Camera (MP)", "Price (Euros)"]
    # print(data[to_compare].values)
    diff = (data[to_compare].values - choices) * [1, 1, 1, -1]
    colors = [None, None, None] + ["green" if x >= 0 else "red" for x in diff]
    # print(np.sign(diff))
    return dbc.Table(
        [
            html.Tbody(
                [
                    html.Tr(
                        [
                            html.Th(col),
                            html.Td([str(data[col]),],),
                            html.Td([html.Span(" ▉", style={"color": c,},)],),
                        ]
                    )
                    for (col, c) in zip(data.index, colors)
                ]
            )
        ]
    )


def table_from_data_horizontal(data):
    header = [html.Thead(html.Tr([html.Th(col) for col in data.index]))]
    body = [html.Tbody([html.Tr([html.Td(data[col]) for col in data.index])])]
    return dbc.Table(header + body)


def other_options(data):
    contents = []
    tables = []
    ids = []
    i = 2
    for index, row in data.iterrows():
        contents.append(f"{i}. {row['Model']}")
        tables.append(table_from_data_horizontal(row))
        i = i + 1
    return contents, tables


if __name__ == "__main__":
    app.run_server(debug=False)

[((15, 7, 15, 60), 'pandas.read_csv', 'pd.read_csv', (), '', True, 'import pandas as pd\n'), ((16, 10, 16, 59), 'pandas.read_csv', 'pd.read_csv', (), '', True, 'import pandas as pd\n'), ((41, 6, 46, 1), 'dash.Dash', 'dash.Dash', (), '', False, 'import dash\n'), ((316, 11, 316, 38), 'numpy.asarray', 'np.asarray', ({(316, 22, 316, 37): '[-1, -1, -1, 1]'}, {}), '([-1, -1, -1, 1])', True, 'import numpy as np\n'), ((323, 21, 323, 41), 'numpy.argsort', 'np.argsort', ({(323, 32, 323, 40): 'distance'}, {}), '(distance)', True, 'import numpy as np\n'), ((374, 11, 374, 35), 'dash_bootstrap_components.Table', 'dbc.Table', ({(374, 21, 374, 34): '(header + body)'}, {}), '(header + body)', True, 'import dash_bootstrap_components as dbc\n'), ((296, 8, 296, 37), 'dash.dependencies.Output', 'Output', ({(296, 15, 296, 24): '"""results"""', (296, 26, 296, 36): '"""children"""'}, {}), "('results', 'children')", False, 'from dash.dependencies import Input, Output, State\n'), ((301, 8, 301, 40), 'dash.dependencies.Input', 'Input', ({(301, 14, 301, 30): 'f"""{attr}-choice"""', (301, 32, 301, 39): '"""value"""'}, {}), "(f'{attr}-choice', 'value')", False, 'from dash.dependencies import Input, Output, State\n'), ((297, 10, 297, 55), 'dash.dependencies.Output', 'Output', ({(297, 17, 297, 42): 'f"""other-results-list-{i}"""', (297, 44, 297, 54): '"""children"""'}, {}), "(f'other-results-list-{i}', 'children')", False, 'from dash.dependencies import Input, Output, State\n'), ((298, 10, 298, 58), 'dash.dependencies.Output', 'Output', ({(298, 17, 298, 45): 'f"""other-results-tooltip-{i}"""', (298, 47, 298, 57): '"""children"""'}, {}), "(f'other-results-tooltip-{i}', 'children')", False, 'from dash.dependencies import Input, Output, State\n'), ((289, 17, 289, 45), 'dash_html_components.Div', 'html.Div', (), '', True, 'import dash_html_components as html\n'), ((372, 34, 372, 46), 'dash_html_components.Th', 'html.Th', ({(372, 42, 372, 45): 'col'}, {}), '(col)', True, 'import dash_html_components as html\n'), ((55, 20, 58, 21), 'dash_html_components.H1', 'html.H1', (), '', True, 'import dash_html_components as html\n'), ((373, 33, 373, 51), 'dash_html_components.Td', 'html.Td', ({(373, 41, 373, 50): 'data[col]'}, {}), '(data[col])', True, 'import dash_html_components as html\n'), ((359, 28, 359, 40), 'dash_html_components.Th', 'html.Th', ({(359, 36, 359, 39): 'col'}, {}), '(col)', True, 'import dash_html_components as html\n'), ((282, 24, 282, 47), 'dash_html_components.Div', 'html.Div', (), '', True, 'import dash_html_components as html\n'), ((361, 37, 361, 76), 'dash_html_components.Span', 'html.Span', (), '', True, 'import dash_html_components as html\n'), ((242, 32, 242, 76), 'dash_bootstrap_components.CardHeader', 'dbc.CardHeader', ({(242, 47, 242, 75): '"""The best phone for you is:"""'}, {}), "('The best phone for you is:')", True, 'import dash_bootstrap_components as dbc\n'), ((243, 32, 243, 58), 'dash_bootstrap_components.CardBody', 'dbc.CardBody', (), '', True, 'import dash_bootstrap_components as dbc\n'), ((249, 32, 249, 69), 'dash_bootstrap_components.CardHeader', 'dbc.CardHeader', ({(249, 47, 249, 68): '"""Other great phones:"""'}, {}), "('Other great phones:')", True, 'import dash_bootstrap_components as dbc\n'), ((71, 40, 74, 41), 'dash_html_components.H4', 'html.H4', (), '', True, 'import dash_html_components as html\n'), ((75, 40, 86, 41), 'dash_html_components.P', 'html.P', (), '', True, 'import dash_html_components as html\n'), ((96, 40, 99, 41), 'dash_bootstrap_components.Label', 'dbc.Label', (), '', True, 'import dash_bootstrap_components as dbc\n'), ((100, 40, 116, 41), 'dash_bootstrap_components.RadioItems', 'dbc.RadioItems', (), '', True, 'import dash_bootstrap_components as dbc\n'), ((122, 40, 125, 41), 'dash_bootstrap_components.Label', 'dbc.Label', (), '', True, 'import dash_bootstrap_components as dbc\n'), ((126, 40, 141, 41), 'dash_core_components.Slider', 'dcc.Slider', (), '', True, 'import dash_core_components as dcc\n'), ((147, 40, 150, 41), 'dash_bootstrap_components.Label', 'dbc.Label', (), '', True, 'import dash_bootstrap_components as dbc\n'), ((151, 40, 172, 41), 'dash_core_components.Slider', 'dcc.Slider', (), '', True, 'import dash_core_components as dcc\n'), ((178, 40, 181, 41), 'dash_bootstrap_components.Label', 'dbc.Label', (), '', True, 'import dash_bootstrap_components as dbc\n'), ((182, 40, 200, 41), 'dash_core_components.Slider', 'dcc.Slider', (), '', True, 'import dash_core_components as dcc\n'), ((206, 40, 209, 41), 'dash_bootstrap_components.Label', 'dbc.Label', (), '', True, 'import dash_bootstrap_components as dbc\n'), ((210, 40, 228, 41), 'dash_core_components.Slider', 'dcc.Slider', (), '', True, 'import dash_core_components as dcc\n'), ((263, 44, 274, 45), 'dash_bootstrap_components.Tooltip', 'dbc.Tooltip', (), '', True, 'import dash_bootstrap_components as dbc\n'), ((255, 48, 258, 49), 'dash_html_components.Span', 'html.Span', (), '', True, 'import dash_html_components as html\n')]