from math import cos
from datetime import datetime
import plotly.graph_objects as go
import pandas as pd
from dash import html, dcc
import plotly.express as px

# TODO DOCUMENT MORE

def find_Device(name: str, devices):
    return [dev for dev in devices if dev.name in name]

def find_Device_strict(name: str, devices):
    return [dev for dev in devices if dev.name == name]

def now(timestamp):
    # return datetime.fromtimestamp(int(timestamp.split("/")[-1])).replace(microsecond=0)
    return datetime.now().replace(microsecond=0) #TODO CHANGE ME WHEN GETTING NEW FILES


# Main class for devices used for node graphs and plots
class Device:
    def __init__(self, name):
        self.name = name
        self.data = {}              # Todo: Maybe we can remove this altogether
        self.graphs = []
        self.built = False          # Onetime token to build the graphs
        self.hidden = True          # Careful! True => Hidden, False => Visible
        self.index = -1             # This ensures the index is smallest (As it normally is non-negative)
        self.position = [0,0]       # Position 0,0 is NOT drawn
        self.power = [None, None]
        self.Buspower = []
        self.connection = []        # This is used for invisible devices to draw the graph

    # building data from the json timestamped files to a list<dict> with the timestamp as index and all the data as dict
    def BuildData(self, data):
        values = {}
        for key in data[0].keys():
            values.update({key: []})
        for timestamp in range(0, len(data)):
            for key in data[timestamp].keys():
                values[key].append(data[timestamp][key])
        self.data = values

    # This builds the Graphs on app-start and gives back the divs containing said plots
    def BuildDiv(self, index):
        if self.power == [None,None]:
            self.UpdatePower()
        if not self.built:
            self.BuildGraphs()
            self.built = True

        self.index = index

        return html.Div(id='graphs' + self.name, children=[
            html.Div(id={"type": "graphs", "index": self.index}, children=self.graphs)])        #, style={'display': 'inlineBlock'}

    def BuildChecklist(self, index):
        return html.Div(className="round", children=[
            dcc.Checklist([{'label': [self.name.replace('_', ' ').title(), html.Br()], 'value': self.name.replace('_', ' ')}],
            persistence=False, id={'type': "topChecklist", 'index': self.index},
            style={"paddingBottom": "0px", 'display': 'inlineBlock'}),
            dcc.Checklist(options=[{"label": [graph.children.id["role"].split(self.name, 1)[1], html.Br()],
                                    "value": graph.children.id} for graph in self.graphs], value=[],
                                    persistence=False,
                                    id={'type': "checklist", 'index': self.index},
                                    style={"postition": "relative", "paddingLeft": "20px",
                                            "paddingTop": "0px"})])

    # If no graphs have been built yet builds graphs and gives back the div    '{"index":1,"type":"topChecklist"}'
    def UpdateDiv(self):
        return self.BuildDiv(self.index)

    # Builds all the plots
    def BuildGraphs(self):

        graphs = []
        values = {}
        time = []

        # Get Data from each saved time
        for i, timestamp in enumerate(self.data.keys()):
            #time.append(datetime.fromtimestamp(int(timestamp.split("/")[-1])).replace(microsecond=0))  # Todo set me back in!
            time.append(datetime.now().replace(microsecond=0)) # Todo Better for demo purposes
            for key in self.data[timestamp].keys():
                if not key in values.keys():
                    values.update({key: []})
                values[key].append(self.data[timestamp][key])

        for key in values.keys():
            global fig

            if "Ac" in key and not "Actual" in key and not 'Power' in key:
                for key2 in values[key][0][0]:
                    fig = go.Figure()
                    fig.layout.title =self.name + " Ac " + key2

                    stackgroup1 = "one"
                    stackgroup2 = "two"
                    stackgroup3 = "three"
                    if key2 == "Current":
                        stackgroup2 = "one"
                        stackgroup3 = "one"

                    fig.add_trace(go.Scatter(x=time, y=[values[key][i][0][key2] for i in range(len(time))], fill='tonexty',name="Phase 1", stackgroup=stackgroup1))
                    fig.add_trace(go.Scatter(x=time, y=[values[key][i][1][key2] for i in range(len(time))], fill='tonexty', name="Phase 2", stackgroup=stackgroup2))
                    fig.add_trace(go.Scatter(x=time, y=[values[key][i][2][key2] for i in range(len(time))], fill='tonexty', name="Phase 3", stackgroup=stackgroup3))
                    fig.update_xaxes(fixedrange=True)
                    fig.update_yaxes(fixedrange=True, rangemode="tozero")
                    fig.update_layout(uirevision=False)

                    graphs.append(html.Div(hidden=self.hidden, children=dcc.Graph(id={"type":"graph", "role": self.name + " " + "Ac " + key2.replace('/', ' '), "index":self.index*100+len(graphs)}, figure=fig,
                                                                           config={'displayModeBar': False},
                                                                           style={"width": "90%",
                                                                                  "float": "right"})))
                continue
            elif "Dc" in key and not "Actual" in key and not "Power" in key:
                for key2 in values[key][0]:
                    df = pd.DataFrame({
                        "Zeit": time,
                        "Wert": [values[key][i][key2] for i in range(len(time))],
                    })

                    fig = px.area(df, x="Zeit", y="Wert", title=self.name + " " + key2.replace('/', ' ').title(),
                                  markers=True)
                    fig.update_xaxes(fixedrange=True)
                    fig.update_yaxes(fixedrange=True, rangemode="tozero")
                    fig.update_layout(uirevision=False)
                    # finally add figure to graphs
                    graphs.append(html.Div(hidden=self.hidden,children=dcc.Graph(id={"type":"graph", "role": self.name + " " + 'Dc ' + key2.replace('/', ' '), "index":self.index*100+len(graphs)}, figure=fig,     # id=self.name + 'Dc ' + key2,
                                                          config={'displayModeBar': False},
                                                          style={"width": "90%", "float": "right",
                                                                 "display": "inline-block"})))
                continue

            elif "Alarm" in key:
                fig = go.Figure()
                fig.layout.title = self.name + "-Alarms"
                for key2 in values[key][0]:
                    fig.add_trace(go.Scatter(x=time, y=[1 if key2 in values[key][i] else 0 for i in range(len(time))], fill='tonexty', name=key2, stackgroup="one"))
                fig.update_xaxes(fixedrange=True)
                fig.update_yaxes(fixedrange=True, rangemode="tozero")
                fig.update_layout(uirevision=False)
                graphs.append(
                    html.Div(hidden=self.hidden, children=dcc.Graph(id={"type":"graph", "role": self.name + " " + "Alarms", "index":self.index*100+len(graphs)}, figure=fig,
                                                             config={'displayModeBar': False},
                                                             style={"width": "90%",
                                                                    "float": "right"})))
                continue

            elif any(["Name" in key, "Type" in key, "MainState" in key]):
                # Skip we have built this already, or it isn't useful
                continue

            # check if the data is values or flags/strings
            if type(values[key][0]) is float or type(values[key][0]) is int:

                df = pd.DataFrame({
                    "Zeit": time,
                    "Wert": values[key],
                })

                fig = px.area(df, x="Zeit", y="Wert", title=self.name + " " + key.replace('/', ' ').title(), markers=True)
                fig.update_xaxes(fixedrange=True)
                fig.update_yaxes(fixedrange=True, rangemode="tozero")
                fig.update_layout(uirevision=False)
            #finally add figure to graphs
            graphs.append(html.Div(hidden=self.hidden, children=dcc.Graph(id={"type":"graph", "role": self.name + " " + key, "index":self.index*100+len(graphs)}, figure=fig, config = {'displayModeBar': False}, style={"width":"90%", "float":"right", "display":"inline-block"})))

        self.graphs = graphs

    # This checks what kind of device this is and sets power calculation
    def UpdatePower(self):
        if not self.data:     #or self.data[list(self.data.keys())[0]]['Name'] == "ampt"
            return
        if "Frequency" in list(self.data[list(self.data.keys())[0]].keys()):
            # 3-phase AC
            for timestamp in self.data.keys():
                self.data[timestamp].update({"Ac Power": self.data[timestamp]["Ac"][0]["Voltage"] * self.data[timestamp]["Ac"][0]["Current"] * cos(self.data[timestamp]["Ac"][0]["Phi"]) + self.data[timestamp]["Ac"][1]["Voltage"] * self.data[timestamp]["Ac"][1]["Current"] * cos(self.data[timestamp]["Ac"][1]["Phi"]) + self.data[timestamp]["Ac"][2]["Voltage"] * self.data[timestamp]["Ac"][2]["Current"] * cos(self.data[timestamp]["Ac"][2]["Phi"])})
            self.power[0] = self.data[list(self.data.keys())[-1]]["Ac Power"]
        if "Dc" in list(self.data[list(self.data.keys())[0]].keys()) or "Dc48" in list(self.data[list(self.data.keys())[0]].keys()):
            # DC
            Dc = "Dc48" if "Dc48" in list(self.data[list(self.data.keys())[0]].keys()) else "Dc"
            for timestamp in self.data.keys():
                self.data[timestamp].update({"Dc Power": self.data[timestamp][Dc]["Voltage"] * self.data[timestamp][Dc]["Current"]})
            self.power[1] = self.data[list(self.data.keys())[-1]]["Dc Power"]