import json
import os
import string
from math import cos
from datetime import datetime

import dash
import numpy as np
from dash import Dash, html, dcc, Output, Input, State, MATCH, ctx, ClientsideFunction, ALL
from dash.exceptions import PreventUpdate
from python.gui.cytoscape import style, buildEdges, buildNodes
from python.gui.flatten import flattenDevice
from python.gui.templates import Device, find_Device, now, find_Device_strict
import dash_cytoscape as cyto

# TODO Repair the Cytoscape Node Click Callbacks toggling visibility of the associated graphs
# TODO CSS BUG: the graphs don't float to the top. (check only DcDc current for example)
# TODO Startup Bug: ~~This should resolve itself once we have realtime data~~
# TODO Beautify and Test (E.G Units)

# External css and javascript for the dash app
external_stylesheets = [
    './assets/header.css',
]
external_scripts = [
    './assets/inserted.js',
]


# Main Dash App Webserver
app = Dash(__name__, external_stylesheets=external_stylesheets, external_scripts=external_scripts)


# First: Build Topology

# Grabs device names and connections from topology file
# NOTE: Device changes are only possible on restart TODO grab topology from s3

deviceNames = []
connectionsL = []
connectionsT = []
connectionsB = []
connectionsR = []
devices = []
topology = json.load(open(f"./assets/data/topology1671435263"))

def psum(l : list) -> float:
    sum = 0
    for k in l:
        if k:
            sum += k
    return float(sum)

#This builds the cytoscape nodes (deviceNames, and Edges (connections)
for i, bus in enumerate(topology["Topology"]):
    # if not bus["Name"] in deviceNames:
    for key in bus.keys():
        nodeName = bus[key].split(':',1)[0]
        if not nodeName or nodeName == "None" or nodeName == "Losses":
            continue
        if not nodeName in deviceNames:
            deviceNames.append(nodeName)
            dev = Device(nodeName)
            new = True
        elif key == "Name":
            new = False
            dev = find_Device_strict(nodeName, devices)[0]
        else:
            continue
        invisible = False
        pos = [0,0]
        buspower = []

        if len(bus[key].split(':'))>2 and bus[key].split(':')[2] == "hide":
            invisible = True

        if invisible:
            dev.connection = [bus["Name"], nodeName]


        match key:
            case "Left":
                pos = [i, 1]
                if invisible:
                    if topology["Topology"][i - 1]["Name"] != None:
                        nodeName = topology["Topology"][i - 1]["Name"]
                if not (bus["Name"], nodeName) in connectionsL:
                    connectionsL.append((bus["Name"], nodeName))
                    connectionsR.append((nodeName, bus["Name"]))
            case "Right":
                pos = [i + 2, 1]
                if invisible:
                    if len(topology["Topology"]) > i and topology["Topology"][i + 1]["Name"] != None != "None":
                        nodeName = topology["Topology"][i + 1]["Name"]
                if not (bus["Name"], nodeName) in connectionsR:
                    connectionsR.append((bus["Name"], nodeName))
                    connectionsL.append((nodeName, bus["Name"]))
            case "Name":
                pos = [i + 1, 1]
                if new:
                    buspower = [bus[key] for key in bus.keys() if not key == "Name"]
                if bus["Name"] == nodeName == "Dc":
                    connectionsR.append(("Inverter", bus["Name"]))
                    connectionsL.append((nodeName, "Inverter"))
                if bus["Name"] == nodeName == "DcDc":
                    connectionsL.append((nodeName, "Dc"))

            case "Top":
                pos = [i + 1, 0]
                if not (bus["Name"], nodeName) in connectionsT:
                    connectionsT.append((nodeName, bus["Name"]))
            case "Bottom":
                pos = [i + 1, 2]
                if not (bus["Name"], nodeName) in connectionsB:
                    connectionsB.append((bus["Name"], nodeName))

        if buspower != []:
            dev.Buspower = buspower

        if invisible:
            pos = [0, 0]
        dev.position = pos

        if new:
            devices.append(dev)




# NOTE: Device changes are only possible on restart
#deviceNames = ['Grid','PvOnAcIn','ac_in','ac_last','ac_out','Inverter','dc_last','PvOnDc','dc_bus','DcDc','battery']
# devices = [Device(name) for name in deviceNames]

# data for the cytoscape graph found in ./cytoscape.py
nodes = buildNodes(devices)
edges = buildEdges(connectionsT + connectionsB + connectionsL + connectionsR)
elements = nodes + edges

# Second: Grab initial GraphData

# Building initial device Data TODO implement Local/S3 History
path = "./assets/data3/"
allFileNames = os.listdir(path)
fileNames = allFileNames[0:180]

# fileNames = [f"./assets/data/{i}" for i in range(1671435263, 1671435443, 2)]        #range(1669730732, 1669731092, 2)]

# timeData is a dict of dicts with the keys being the timestamps
timeData = {}
for i, file in enumerate(fileNames):
    file = json.load(open(path + file))
    timeData.update({fileNames[i]:{}})
    for device in file["Devices"]:
        timeData[fileNames[i]].update(flattenDevice(device))

# Building initial device graphs (this considerably reduces input lag, but increases start time of the program)
for time in timeData.keys():
    for key in timeData[time].keys():
        device = find_Device_strict(key.split("/")[0], devices)[0]
        if not device:
            continue #TODO THROW ERROR
        if not time in (device.data.keys()):
            device.data.update({time: {}})
        if not len(key.split("/", 1)) < 2:
            device.data[time].update({key.split("/", 1)[1]: timeData[time][key]})

debug = []

#-----------------------------------------------------------------------------------------------------------------------
# Callbacks

# This shifts the website viewpoint to the graphs of the node that has been clicked on
# And toggles visibility of graphs associated with the node-device
# using Javascript found in clientside.js
app.clientside_callback(
    ClientsideFunction(
        namespace='clientside',
        function_name='clickNode'
    ),
    Output('placeholder', 'data'),
    Input('view', 'data')
)

# Clock callback, every second updates the time TODO MAKE ME CLIENTSIDE?
# This also resets tapNodeData and tapEdgeData for correct handling of clicks on nodes/edges
@app.callback([Output('title', 'children'),
               Output('cytoscape-elements', 'tapNodeData'),
               Output('cytoscape-elements', 'tapEdgeData')],
              Input('interval-component3', 'n_intervals'))
def update_time_live(n):
    return '''
        Echtzeit Daten-Monitoring
    ''' + str(np.datetime64('now')).replace('T', ' '), {}, {}


# Updates the powergraph connections every 2 seconds, also tracks the highest power measured in 'max-power' dcc.Store
@app.callback(Output('cytoscape-elements', 'elements'),
              Output('max-power', 'data'),
              Input('interval-component-2', 'n_intervals'),
              State('max-power', 'data'))
def update_power_graph(n, maxPower):
    for i, device in enumerate(devices):
        # device.UpdatePower()
        if device.name == "Battery":
            continue
        power = device.power

        #TODO as long as device is not inverter None will be in power

        if power == [None, None] and device.Buspower == []:
            continue
        rpower = []  # Left top bottom right

        sum = psum(power)
        if sum > maxPower:
            maxPower = sum


        #TODO handle inverter two power measurements..

        # Setting nodes to either a producer or consumer depending on power out/in
        for node in ( nodes[y] for y in range(0, len(nodes)) if nodes[y]['data']['id'] == device.name):
            if sum >= 0:
                node.update({'classes': node.get('classes') + ' producer'})
            else:
                node.update({'classes': node.get('classes') + ' consumer'})

        # Updates the power values on the connections
        if device.Buspower != []:
            for remote in device.Buspower:
                remoteDevice = find_Device_strict(remote.split(":", 1)[0], devices)[0]
                if None in remoteDevice.power:
                    remotePower = remoteDevice.power[0] if remoteDevice.power[0] != None else remoteDevice.power[1]

                else: #Inverter
                    if "Ac" in device.name:
                        remotePower =  remoteDevice.power[0]
                    else:
                        remotePower = remoteDevice.power[1]

                rpower.append(remotePower)


          #or edges[y]['data']['source'] in device.Buspower)
        if None in power:
            if rpower != []:
                if rpower[0] <= 0:
                    for connection in (y for y in connectionsL if device.name == y[0]):
                        for edge in (edges[y] for y in range(0, len(edges)) if
                                     edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] == connection[1]):
                            edge.update({'style': {'width': str((rpower[0]/maxPower) * 20) + 'px'}})
                            edge['data'].update({'label': [str(int(abs(rpower[0]))) + "W"]})
                        for connection in (y for y in connectionsR if device.name == y[0]):
                            for edge in (edges[y] for y in range(0, len(edges)) if
                                         edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] ==
                                         connection[1]):
                                edge.update({'style': {'width': str(0) + 'px'}})
                                edge['data'].update({'label': []})
                else:
                    for connection in (y for y in connectionsR if device.name == y[1]):
                        for edge in (edges[y] for y in range(0, len(edges)) if
                                     edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] == connection[1]):
                            edge.update({'style': {'width': str((rpower[0]/maxPower) * 20) + 'px'}})
                            edge['data'].update({'label': [str(int(abs(rpower[0]))) + "W"]})
                    for connection in (y for y in connectionsL if device.name == y[1]):
                        for edge in (edges[y] for y in range(0, len(edges)) if
                                     edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] ==
                                     connection[1]):
                            edge.update({'style': {'width': str(0) + 'px'}})
                            edge['data'].update({'label': []})

                if rpower[3] >= 0:
                    #This should always be the case
                    for connection in (y for y in connectionsR if device.name == y[0]):
                        for edge in (edges[y] for y in range(0, len(edges)) if
                                     edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] == connection[1]):
                            edge.update({'style': {'width': str((rpower[3]/maxPower) * 20) + 'px'}})
                            edge['data'].update({'label': [str(int(rpower[3])) + "W"]})
                    for connection in (y for y in connectionsL if device.name == y[1]):
                        for edge in (edges[y] for y in range(0, len(edges)) if
                                     edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] ==
                                     connection[1]):
                            edge.update({'style': {'width': str(0) + 'px'}})
                            edge['data'].update({'label': []})
                else:
                        for connection in (y for y in connectionsL if device.name == y[1]):
                            for edge in (edges[y] for y in range(0, len(edges)) if
                                         edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] ==
                                         connection[1]):
                                edge.update({'style': {'width': str((rpower[3] / maxPower) * 20) + 'px'}})
                                edge['data'].update({'label': [str(int(abs(rpower[3]))) + "W"]})
                        for connection in (y for y in connectionsR if device.name == y[0]):
                            for edge in (edges[y] for y in range(0, len(edges)) if
                                         edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] ==
                                         connection[1]):
                                edge.update({'style': {'width': str(0) + 'px'}})
                                edge['data'].update({'label': []})

                if not rpower[2]:
                    rpower[2] = 0

                for connection in (y for y in connectionsB if device.name == y[0]):
                    for edge in (edges[y] for y in range(0, len(edges)) if
                                 edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] ==
                                 connection[1]):
                        edge.update({'style': {'width': str((abs(rpower[0] + rpower[2] - rpower[3]) / maxPower) * 20) + 'px'}})
                        edge['data'].update({'label': [str(int(abs(rpower[0] + rpower[2] - rpower[3]))) + "W"]})
                continue
            if not sum:
                continue

            if sum >= 0:
                for connection in (y for y in connectionsR + connectionsT if device.name == y[0]):
                    for edge in (edges[y] for y in range(0, len(edges)) if
                                 edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] == connection[1]):
                        edge.update({'style': {'width': str((sum/maxPower) * 20) + 'px'}})
                        edge['data'].update({'label': [str(int(sum)) + "W"]})
                    for connection in (y for y in connectionsL if device.name == y[1]):
                        for edge in (edges[y] for y in range(0, len(edges)) if
                                     edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] ==
                                     connection[1]):
                            edge.update({'style': {'width': str(0) + 'px'}})
                            edge['data'].update({'label': []})

            elif sum < 0:
                for connection in (y for y in connectionsR + connectionsT if device.name == y[0]):
                    for edge in (edges[y] for y in range(0, len(edges)) if
                                 edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] == connection[1]):
                        edge.update({'style': {'width': str(0) + 'px'}})
                        edge['data'].update({'label': []})
                for connection in (y for y in connectionsL if device.name == y[1]):
                    for edge in (edges[y] for y in range(0, len(edges)) if
                                 edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] == connection[1]):
                        edge.update({'style': {'width': str((sum/maxPower) * 20) + 'px'}})
                        edge['data'].update({'label': [str(abs(int(sum))) + "W"]})



        else:
            #This is the inverter
            if not sum:
                continue
            # if power[0] < 0:
            #     #Ac side
            # #     for connection in (y for y in connectionsL if device.name == y[0]):
            # #         for edge in (edges[y] for y in range(0, len(edges)) if
            # #                      edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] == connection[1]):
            # #             edge.update({'style': {'width': str((power[0]/maxPower) * 10) + 'px'}})
            # #             edge['data'].update({'label': [str(int(power[0])) + "W"]})
            # # else:
            # #     for connection in (y for y in connectionsL if device.name == y[0]):
            # #         for edge in (edges[y] for y in range(0, len(edges)) if
            # #                      edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] == connection[1]):
            # #             edge.update({'style': {'width': 0 + 'px'}})
            # #             edge['data'].update({'label': []})

            if power[1] > 0:
                #DC side
                for connection in (y for y in connectionsR if device.name == y[0]):
                    for edge in (edges[y] for y in range(0, len(edges)) if
                                 edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] == connection[1]):
                        edge.update({'style': {'width': str((power[1]/maxPower) * 20) + 'px'}})
                        edge['data'].update({'label': [str(int(power[1])) + "W"]})
                    for connection in (y for y in connectionsL if device.name == y[1]):
                        for edge in (edges[y] for y in range(0, len(edges)) if
                                     edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] ==
                                     connection[1]):
                            edge.update({'style': {'width': str(0) + 'px'}})
                            edge['data'].update({'label': []})
            elif power[1] < 0:
                #DC side
                for connection in (y for y in connectionsL if device.name == y[1]):
                    for edge in (edges[y] for y in range(0, len(edges)) if
                                 edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] == connection[1]):
                        edge.update({'style': {'width': str(abs(power[1]/maxPower) * 20) + 'px'}})
                        edge['data'].update({'label': [str(int(abs(power[1]))) + "W"]})
                for connection in (y for y in connectionsR if device.name == y[0]):
                    for edge in (edges[y] for y in range(0, len(edges)) if
                                 edges[y]['data']['source'] == connection[0] and edges[y]['data']['target'] == connection[1]):
                        edge.update({'style': {'width': str(0) + 'px'}})
                        edge['data'].update({'label': []})

    return elements, maxPower

# Handles the visibility checkmarks through wildcards
@app.callback([Output({'type': 'graphs', 'index': MATCH}, "children"),
               Output({'type': 'checklist', 'index': MATCH}, "value")],
              [Input({'type': 'checklist', 'index': MATCH}, "value"),
               Input({'type': 'topChecklist', 'index': MATCH}, "value")],
              State({'type': 'graphs', 'index': MATCH}, "children")
                , prevent_initiall_call=True)
def graph_visibility_checklist(value, top, graphs):
    if not graphs or not ctx.triggered_id:
        raise PreventUpdate

    #Setting all invisible and turning selected back on
    for graph in graphs:
        graph["props"]["hidden"] = True


    if top != [] and ctx.triggered_id.type == 'topChecklist':
        id = []
        for graph in graphs:
            id.append(graph["props"]["children"]["props"]["id"])
            graph["props"]["hidden"] = False

        for i in id:
            for dev in find_Device(i, devices):
                dev.hidden = False

        return graphs, id

    elif ctx.triggered_id.type == 'topChecklist':
        return graphs, []

    if not value:
        return graphs, dash.no_update

    for graph in (graph for graph in graphs if (any(graph["props"]["children"]["props"]["id"]["role"] in i["role"] for i in value) or any(i["role"] in graph["props"]["children"]["props"]["id"]["role"] for i in value))):
        graph["props"]["hidden"] = False
    for i in value:
        for dev in find_Device(i["role"], devices):
            dev.hidden = False

    return graphs, dash.no_update

# The small number next to the graph triggers are the latest Value or an average thereof (AC)
@app.callback(Output({'type': 'checklist', 'index': ALL}, "options"),
              Input('interval-component3', 'n_intervals'),
              [State({'type': 'graphs', 'index': ALL}, "children"),
               State({'type': 'checklist', 'index': ALL}, "options")]
)
def lable_previews(n, graphs, options):
    for x in graphs:
        if x != []:
            for g in x:
                for l in options:
                     if l!=[]:
                         for a in l:
                             if g["props"]["children"]["props"]["id"]["role"].split(" ",1)[1].replace(" ", "") in a["label"][0].replace(" ", ""):
                                val = 0
                                if g["props"]["children"]["props"]["figure"]["data"] != [] and len(g["props"]["children"]["props"]["figure"]["data"][0]["y"]) != 0:
                                    if len(g["props"]["children"]["props"]["figure"]["data"]) == 3:
                                        val = round((g["props"]["children"]["props"]["figure"]["data"][0]["y"][-1]
                                                    + g["props"]["children"]["props"]["figure"]["data"][1]["y"][-1]
                                                    + g["props"]["children"]["props"]["figure"]["data"][2]["y"][-1])/3, 1)
                                    else:
                                        if g["props"]["children"]["props"]["figure"]["data"][0]["y"][-1] != []:
                                            val = round(g["props"]["children"]["props"]["figure"]["data"][0]["y"][-1], 1)


                                a["label"][0] = (a["label"][0].rstrip(string.digits + string.punctuation + string.whitespace) + "   " + str(val))
    return options

# the second output triggers the viewport callback nodeViewShift TODO MAKE ME CLIENTSIDE?
@app.callback(Output('view', 'data'),
              [Input('cytoscape-elements', 'tapNodeData'),
              Input('cytoscape-elements', 'tapEdgeData')],
              State('view', 'data'),)
def cytoscape_visibility_clicks(nodeData, edgeData, view):
    if nodeData and nodeData != {}:
        return '{"index":%s,"type":"topChecklist"}' % (find_Device_strict(nodeData["id"], devices)[0].index)

    elif edgeData and edgeData != {}:
        return '{"index":%s,"type":"topChecklist"}' % (find_Device_strict(edgeData["source"], devices)[0].index)
    return dash.no_update

# Updates the individual graphs every second
@app.callback(Output('OurGraphs', 'children'),
              Input('interval-component', 'n_intervals'),
               State('OurGraphs', 'children'),
              prevent_initiall_call=True)
def update_graphs(n, graphs):

    # TODO GRAB NEW DATA FROM DISK OR S3
    timeData = {}
    #Local copy of allFileNames because we loop over them at the moment
    number = [item for item in allFileNames if item not in fileNames][n % len(allFileNames)-1]

    fileNames.pop(0)
    fileNames.append(str(number))

    filename = path + str(number)
    file = json.load(open(filename))

    timeData.update({filename: {}})
    for device in file["Devices"]:
        timeData[filename].update(flattenDevice(device))

    for device in devices:
        if not filename in (device.data.keys()):
            device.data.update({filename: {}})
            for key in (key for key in timeData[filename].keys() if key.split("/",1)[0] == device.name):
                device.data[filename].update({key.split("/", 1)[1]: timeData[filename][key]})

    for k, device in enumerate(graphs):
        for graph in device["props"]["children"][0]["props"]["children"]:
            id = graph["props"]["children"]["props"]["id"]
            fig = graph["props"]["children"]["props"]["figure"]
            if "Power" in id["role"].split(" ", 1)[1]:
                if "Ac" in id["role"].split(" ", 1)[1]:
                    # 3-phase AC
                    fig["data"][0]['y'][0] =  (devices[k].data[list(devices[k].data.keys())[-1]]["Ac"][0]["Voltage"] *
                                              devices[k].data[list(devices[k].data.keys())[-1]]["Ac"][0]["Current"] *
                                              cos(devices[k].data[list(devices[k].data.keys())[-1]]["Ac"][0]["Phi"])+
                                              devices[k].data[list(devices[k].data.keys())[-1]]["Ac"][1]["Voltage"] *
                                              devices[k].data[list(devices[k].data.keys())[-1]]["Ac"][1]["Current"] *
                                              cos(devices[k].data[list(devices[k].data.keys())[-1]]["Ac"][1]["Phi"])+
                                              devices[k].data[list(devices[k].data.keys())[-1]]["Ac"][2]["Voltage"] *
                                              devices[k].data[list(devices[k].data.keys())[-1]]["Ac"][2]["Current"] *
                                              cos(devices[k].data[list(devices[k].data.keys())[-1]]["Ac"][2]["Phi"]))

                    devices[k].power[0] = fig["data"][0]['y'][0]
                if "Dc" in id["role"].split(" ", 1)[1] and not "Dc48" in devices[k].data[list(devices[k].data.keys())[-1]]:
                    # DC
                    fig["data"][0]['y'][0] = (devices[k].data[list(devices[k].data.keys())[-1]]["Dc"]["Voltage"] *
                                              devices[k].data[list(devices[k].data.keys())[-1]]["Dc"]["Current"])
                    devices[k].power[1] = fig["data"][0]['y'][0]
                if "Dc48" in devices[k].data[list(devices[k].data.keys())[-1]]:
                    fig["data"][0]['y'][0] = (devices[k].data[list(devices[k].data.keys())[-1]]["Dc48"]["Voltage"] *
                                              devices[k].data[list(devices[k].data.keys())[-1]]["Dc48"]["Current"])
                    devices[k].power[1] = fig["data"][0]['y'][0]

                fig["data"][0]['x'][0] = now(filename)
                fig["data"][0]['y'] = fig["data"][0]['y'][-1:] + fig["data"][0]['y'][:-1]
                fig["data"][0]['x'] = fig["data"][0]['x'][-1:] + fig["data"][0]['x'][:-1]
                continue

            if "Dc" in id["role"].split(" ",1)[1] or "Ac" in id["role"].split(" ",1)[1] or "Alarm" in id["role"] and "Actual" not in id["role"] :
                for i, g in enumerate(fig["data"]):
                    if g["x"] == [] or g["y"] == []:
                        continue
                    g["x"].pop(0)
                    g["y"].pop(0)
                    g["x"].append(now(filename))

                    if "Ac" in id["role"].split(" ",1)[1] and "Actual" not in id["role"] and "Power" not in id["role"]:
                        g["y"].append(devices[k].data[list(devices[k].data.keys())[-1]][
                                     id["role"].split(" ", 1)[1].split(' ')[0]][i][
                                     id["role"].split(" ", 1)[1].split(' ')[1]])
                    elif "Dc48" in devices[k].data[list(devices[k].data.keys())[-1]] and "Actual" not in id["role"] and "Power" not in id["role"]:
                        g["y"].append(devices[k].data[list(devices[k].data.keys())[-1]]["Dc48"][
                                     id["role"].split(" ", 1)[1].split(' ')[1]])
                    elif "Dc" in id["role"].split(" ",1)[1] and "Actual" not in id["role"] and "Power" not in id["role"]:
                        g["y"].append(devices[k].data[list(devices[k].data.keys())[-1]][
                                     id["role"].split(" ", 1)[1].split(' ')[0]][
                                     id["role"].split(" ", 1)[1].split(' ')[1]])
                    elif "Alarm" in id["role"].split(" ",1)[1]:
                        g["y"].append(1 if g["name"] in devices[k].data[list(devices[k].data.keys())[-1]][
                                          id["role"].split(" ", 1)[1].split(' ')[0]] else 0)

                continue
            elif "Actual" in id["role"] or fig["data"] ==[]:
                continue

            fig["data"][0]['y'][0] = devices[k].data[list(devices[k].data.keys())[-1]][id["role"].split(" ", 1)[1]]
            fig["data"][0]['x'][0] = now(filename)
            fig["data"][0]['y'] = fig["data"][0]['y'][-1:] + fig["data"][0]['y'][:-1]
            fig["data"][0]['x'] = fig["data"][0]['x'][-1:] + fig["data"][0]['x'][:-1]

    return graphs

# Main HTML Layout of the app ------------------------------------------------------------------------------------------
app.layout = html.Div(children=[
    # Static Header with logo and Clock
    html.Header(className='bp-page-header', children=[
        html.Img(src="./assets/innovenergy_Logo_onOrange.png", className="logo"),
        html.H1(children='Salimax Gui Demo'),
        html.H4(className="clock", id="title", children=['''
        Echtzeit Daten-Monitoring
        ''' + str(datetime.now().replace(microsecond=0))])
    ]),

    #Big powergraph "overview"
    html.Div(id="powerGraph", className='powerGraph', children=
        cyto.Cytoscape(
            id='cytoscape-elements',
            layout={'name': 'preset'},
            style={'position': 'relative', 'width': '100%', 'height': '600px', 'margin': 'auto'},
            elements=elements,
            stylesheet=style,
            userZoomingEnabled=False,
            userPanningEnabled=False,
        )
    ),
    #html.Div(id="Divider", className="divider"),
    # html.Div(id="graph_buttons", children=[device.BuildButton() for device in devices]),
    # Don't be fooled, these are all the plots! They are built up in the callbacks
    html.Div(id="OurGraphs",className="graphs",children=[device.BuildDiv(i) for i,device in enumerate(devices)]),
    html.Div(id="Checklist",className="sidenav",children=[device.BuildChecklist(i) for i,device in enumerate(devices)]),

    # Every Second triggers updates to clock and plots
    dcc.Interval(
        id='interval-component3',
        interval=1 * 1000,  # in milliseconds
        n_intervals=0
    ),

    # Every two seconds updates plots
    dcc.Interval(
        id='interval-component',
        interval=1 * 1000,  # in milliseconds
        n_intervals=0
    ),

    # Every 5 seconds triggers PowerGraph Update
    dcc.Interval(
        id='interval-component-2',
        interval=1 * 1100,  # in milliseconds
        n_intervals=0
    ),

    # fake Cookie for the highest seen power used for graphing
    dcc.Store(
        id='max-power',
        data=1000
    ),

    # fake cookie used to transfer viewport shift id data to clientside javascript
    dcc.Store(
        id='view',
        data=0
    ),

    # Placeholder for Output callbacks which have no return value
    dcc.Store(id='placeholder', data=0)
])


# Main App Start on specified ip (here local)
if __name__ == '__main__':
    app.run_server(host= 'localhost', debug=False, port=8080)