import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.1
import "qrc:/ui/components"
import "qrc:/ui/customviews"
import Nymea 1.0
import Nymea.AirConditioning 1.0
import QtCharts 2.3

Page {
    id: root
    property AirConditioningManager acManager: null
    property ZoneInfoWrapper zoneWrapper: null
    readonly property ZoneInfo zone: zoneWrapper.zone


    header: NymeaHeader {
        text: root.zone.name

        onBackPressed: {
            pageStack.pop()
        }
    }

    Component {
        id: lineSeriesComponent
        LineSeries { }
    }

    QtObject {
        id: d

        property date now: new Date()
        property int sampleRate: selectionTabs.currentValue.sampleRate

        readonly property int range: selectionTabs.currentValue.range
        readonly property int visibleValues: range / sampleRate

        readonly property var startTime: {
            var date = new Date(now);
            date.setTime(date.getTime() - range * 60000 + 2000);
            return date;
        }

        readonly property var endTime: {
            var date = new Date(now);
            date.setTime(date.getTime() + 2000)
            return date;
        }

        function refreshAll() {
            for (var i = 0; i < thermostatsRepeater.count; i++) {
                thermostatsRepeater.itemAt(i).logsModel.fetchLogs()
            }
            for (var i = 0; i < tempRepeater.count; i++) {
                tempRepeater.itemAt(i).logsModel.fetchLogs()
            }
            for (var i = 0; i < humidityRepeater.count; i++) {
                humidityRepeater.itemAt(i).logsModel.fetchLogs()
            }
            for (var i = 0; i < vocRepeater.count; i++) {
                vocRepeater.itemAt(i).logsModel.fetchLogs()
            }

            for (var i = 0; i < windowOpenRepeater.count; i++) {
                windowOpenRepeater.itemAt(i).logsModel.fetchLogs()
            }
            for (var i = 0; i < heatingRepeater.count; i++) {
                heatingRepeater.itemAt(i).logsModel.fetchLogs()
            }
        }
    }


    ColumnLayout {
        anchors.fill: parent

        SelectionTabs {
            id: selectionTabs
            Layout.fillWidth: true
            Layout.leftMargin: Style.smallMargins
            Layout.rightMargin: Style.smallMargins
            currentIndex: 1
            model: ListModel {
                ListElement {
                    modelData: qsTr("Hours")
                    sampleRate: NewLogsModel.SampleRate1Min
                    range: 180 // 3 Hours: 3 * 60
                }
                ListElement {
                    modelData: qsTr("Days")
                    sampleRate: NewLogsModel.SampleRate15Mins
                    range: 1440 // 1 Day: 24 * 60
                }
                ListElement {
                    modelData: qsTr("Weeks")
                    sampleRate: NewLogsModel.SampleRate1Hour
                    range: 10080 // 7 Days: 7 * 24 * 60
                }
                ListElement {
                    modelData: qsTr("Months")
                    sampleRate: NewLogsModel.SampleRate3Hours
                    range: 43200 // 30 Days: 30 * 24 * 60
                }
            }
            onTabSelected: {
                print("*** tab selected")
                d.now = new Date()
                d.refreshAll();
            }
        }

        Item {
            Layout.fillWidth: true
            Layout.fillHeight: true


            ChartView {
                id: chartView
                anchors.fill: parent

                backgroundColor: "transparent"
                margins.left: 0
                margins.right: 0
                margins.top: 0
                margins.bottom: 0

                legend.visible: false
                legend.alignment: Qt.AlignBottom
                legend.font: Style.extraSmallFont
                legend.labelColor: Style.foregroundColor

                property int busyCounter: 0

                ActivityIndicator {
                    anchors.centerIn: parent
                    visible: chartView.busyCounter > 0
                    opacity: .5
                }

    //            Label {
    //                anchors.centerIn: parent
    //                visible: !logsModel.busy && logsModel.count == 0
    //                text: qsTr("No data")
    //                font: Style.smallFont
    //                opacity: .5
    //            }

                Label {
                    x: chartView.x + chartView.plotArea.x + (chartView.plotArea.width - width) / 2
                    y: chartView.y + chartView.plotArea.y + Style.smallMargins
                    text: {
                        switch (d.sampleRate) {
                        case NewLogsModel.SampleRate1Min:
                            return d.startTime.toLocaleDateString(Qt.locale(), Locale.LongFormat)
                        case NewLogsModel.SampleRate15Mins:
                        case NewLogsModel.SampleRate1Hour:
                        case NewLogsModel.SampleRate3Hours:
                        case NewLogsModel.SampleRate1Day:
                        case NewLogsModel.SampleRate1Week:
                        case NewLogsModel.SampleRate1Month:
                        case NewLogsModel.SampleRate1Year:
                            return d.startTime.toLocaleDateString(Qt.locale(), Locale.ShortFormat) + " - " + d.endTime.toLocaleDateString(Qt.locale(), Locale.ShortFormat)
                        }
                    }
                    font: Style.smallFont
                    opacity: ((new Date().getTime() - d.now.getTime()) / d.sampleRate / 60000) > d.visibleValues ? .5 : 0
                    Behavior on opacity { NumberAnimation {} }
                }

                ValueAxis {
                    id: temperatureAxis
                    min: 0
                    max: 50
                    labelFormat: ""
                    gridLineColor: Style.tileOverlayColor
                    labelsVisible: false
                    lineVisible: false
                    titleVisible: false
                    shadesVisible: false
                }

                ValueAxis {
                    id: humidityAxis
                    min: 0
                    max: 100
                    labelFormat: ""
                    gridLineColor: Style.tileOverlayColor
                    labelsVisible: false
                    lineVisible: false
                    titleVisible: false
                    shadesVisible: false
                    visible: false
                }

                ValueAxis {
                    id: vocAxis
                    min: 0
                    max: 1000
                    //            max: vocRepeater.count > 0 ? vocRepeater.itemAt(0).logsModel.maxValue : 0
                    labelFormat: ""
                    gridLineColor: Style.tileOverlayColor
                    labelsVisible: false
                    lineVisible: false
                    titleVisible: false
                    shadesVisible: false
                    visible: false
                }

                ValueAxis {
                    id: boolAxis
                    min: 0
                    max: 1
                    labelFormat: ""
                    gridLineColor: Style.tileOverlayColor
                    labelsVisible: false
                    lineVisible: false
                    titleVisible: false
                    shadesVisible: false
                    visible: false
                }

                Item {
                    id: labelsLayout
                    x: Style.smallMargins
                    y: chartView.plotArea.y
                    height: chartView.plotArea.height
                    width: chartView.plotArea.x - x
                    Repeater {
                        model: temperatureAxis.tickCount
                        delegate: ColumnLayout {
                            y: index == temperatureAxis.tickCount - 1
                               ? parent.height - height
                               : index == 0
                                 ? 0
                                 : parent.height / (temperatureAxis.tickCount - 1) * index - height / 2
                            Label {
                                width: parent.width - Style.smallMargins
                                horizontalAlignment: Text.AlignRight
                                text: (temperatureAxis.max - (index * temperatureAxis.max / (temperatureAxis.tickCount - 1))) + "°C"
                                verticalAlignment: Text.AlignTop
                                font: Style.extraSmallFont
                                visible: tempRepeater.count > 0 || thermostatsRepeater.count > 0
                                color: app.interfaceToColor("temperaturesensor")
                            }
                            Label {
                                width: parent.width - Style.smallMargins
                                horizontalAlignment: Text.AlignRight
                                text: (humidityAxis.max - (index * humidityAxis.max / (humidityAxis.tickCount - 1))).toFixed(0) + "%"
                                verticalAlignment: Text.AlignTop
                                font: Style.extraSmallFont
                                visible: humidityRepeater.count > 0
                                color: app.interfaceToColor("humiditysensor")
                            }
                            Label {
                                width: parent.width - Style.smallMargins
                                horizontalAlignment: Text.AlignRight
                                text: (vocAxis.max - (index * vocAxis.max / (vocAxis.tickCount - 1))).toFixed(0) + "ppm"
                                verticalAlignment: Text.AlignTop
                                font: Style.extraSmallFont
                                visible: vocRepeater.count > 0
                                color: app.interfaceToColor("vocsensor")
                            }
                        }
                    }
                }

                DateTimeAxis {
                    id: dateTimeAxis
                    min: d.startTime
                    max: d.endTime
                    format: {
                        switch (selectionTabs.currentValue.sampleRate) {
                        case NewLogsModel.SampleRate1Min:
                        case NewLogsModel.SampleRate15Mins:
                            return "hh:mm"
                        case NewLogsModel.SampleRate1Hour:
                        case NewLogsModel.SampleRate3Hours:
                        case NewLogsModel.SampleRate1Day:
                            return "dd.MM."
                        }
                    }
                    tickCount: {
                        switch (selectionTabs.currentValue.sampleRate) {
                        case NewLogsModel.SampleRate1Min:
                        case NewLogsModel.SampleRate15Mins:
                            return root.width > 500 ? 13 : 7
                        case NewLogsModel.SampleRate1Hour:
                            return 7
                        case NewLogsModel.SampleRate3Hours:
                        case NewLogsModel.SampleRate1Day:
                            return root.width > 500 ? 12 : 6
                        }
                    }
                    labelsFont: Style.extraSmallFont
                    gridVisible: false
                    minorGridVisible: false
                    lineVisible: false
                    shadesVisible: false
                    labelsColor: Style.foregroundColor
                }

                Repeater {
                    id: thermostatsRepeater
                    model: zoneWrapper.thermostats
                    delegate: Item {
                        id: thermostatDelegate
                        readonly property Thing thing: zoneWrapper.thermostats.get(index)
                        property XYSeries series: null

                        readonly property NewLogsModel logsModel: NewLogsModel {
                            objectName: "temp: " + thing.name
                            engine: _engine
                            source: "state-" + thing.id + "-temperature"
                            startTime: new Date(d.startTime.getTime() - d.range * 60000)
                            endTime: new Date(d.endTime.getTime() + d.range * 60000)
                            sampleRate: d.sampleRate
                            onBusyChanged: {
                                if (busy) {
                                    chartView.busyCounter++
                                } else {
                                    chartView.busyCounter--
                                }
                            }

                            onEntriesAddedIdx: {
                                for (var i = 0; i < count; i++) {
                                    var entry = logsModel.get(i)
                                    var value = entry.values["temperature"]
                                    if (value == null) {
                                        value = 0;
                                    }
                                    series.insert(index + i, entry.timestamp, value)
                                }
                            }
                            onEntriesRemoved: {
                                series.removePoints(index, count)
                            }
                            Component.onCompleted: fetchLogs()
                        }

                        Component.onCompleted: {
                            series = chartView.createSeries(ChartView.SeriesTypeLine, thing.name, dateTimeAxis, temperatureAxis)
                            series.color = app.interfaceToColor("temperaturesensor")
                            series.width = 2

                            //                    series.opacity = Qt.binding(function() {
                            //                        return d.selectedSeries == null || d.selectedSeries == series ? 1 : 0.3
                            //                    })
                            series.borderWidth = 0;
                            series.borderColor = series.color
                        }
                        Component.onDestruction: {
                            chartView.removeSeries(series)
                        }

                    }
                }

                Repeater {
                    id: tempRepeater
                    model: zoneWrapper.indoorTempSensors
                    delegate: Item {
                        id: tempDelegate
                        readonly property Thing thing: zoneWrapper.indoorTempSensors.get(index)
                        property XYSeries series: null

                        readonly property NewLogsModel logsModel: NewLogsModel {
                            objectName: "temp: " + thing.name
                            engine: _engine
                            source: "state-" + thing.id + "-temperature"
                            startTime: new Date(d.startTime.getTime() - d.range * 60000)
                            endTime: new Date(d.endTime.getTime() + d.range * 60000)
                            sampleRate: d.sampleRate
                            onBusyChanged: {
                                if (busy) {
                                    chartView.busyCounter++
                                } else {
                                    chartView.busyCounter--
                                }
                            }

                            onEntriesAddedIdx: {
                                for (var i = 0; i < count; i++) {
                                    var entry = logsModel.get(i)
                                    var value = entry.values["temperature"]
                                    if (value == null) {
                                        value = 0;
                                    }
                                    series.insert(index + i, entry.timestamp, value)
                                }
                            }
                            onEntriesRemoved: {
                                series.removePoints(index, count)
                            }
                            Component.onCompleted: fetchLogs()

                        }

                        Component.onCompleted: {
                            series = chartView.createSeries(ChartView.SeriesTypeLine, thing.name, dateTimeAxis, temperatureAxis)
                            series.color = app.interfaceToColor("temperaturesensor")
                            series.width = 1

                            //                    series.opacity = Qt.binding(function() {
                            //                        return d.selectedSeries == null || d.selectedSeries == series ? 1 : 0.3
                            //                    })
                            series.borderWidth = 0;
                            series.borderColor = series.color
                        }
                        Component.onDestruction: {
                            chartView.removeSeries(series)
                        }

                    }
                }

                Repeater {
                    id: humidityRepeater
                    model: zoneWrapper.indoorHumiditySensors
                    delegate: Item {
                        id: humidityDelegate
                        readonly property Thing thing: zoneWrapper.indoorHumiditySensors.get(index)
                        property XYSeries series: null

                        readonly property NewLogsModel logsModel: NewLogsModel {
                            objectName: "hum: " + thing.name
                            engine: _engine
                            source: "state-" + thing.id + "-humidity"
                            startTime: new Date(d.startTime.getTime() - d.range * 60000)
                            endTime: new Date(d.endTime.getTime() + d.range * 60000)
                            sampleRate: d.sampleRate
                            onBusyChanged: {
                                if (busy) {
                                    chartView.busyCounter++
                                } else {
                                    chartView.busyCounter--
                                }
                            }

                            onEntriesAddedIdx: {
                                for (var i = 0; i < count; i++) {
                                    var entry = logsModel.get(i)
                                    var value = entry.values["humidity"]
                                    if (value == null) {
                                        value = 0;
                                    }
                                    series.insert(index + i, entry.timestamp, value)
                                }
                            }
                            onEntriesRemoved: {
                                series.removePoints(index, count)
                            }
                            Component.onCompleted: fetchLogs()
                        }

                        Component.onCompleted: {
                            series = chartView.createSeries(ChartView.SeriesTypeLine, thing.name, dateTimeAxis, humidityAxis)
                            series.color = app.interfaceToColor("humiditysensor")
                            series.width = 1
                            //                    series.opacity = Qt.binding(function() {
                            //                        return d.selectedSeries == null || d.selectedSeries == series ? 1 : 0.3
                            //                    })
                            series.borderWidth = 0;
                            series.borderColor = series.color
                        }
                        Component.onDestruction: {
                            chartView.removeSeries(series)
                        }
                    }
                }

                Repeater {
                    id: vocRepeater
                    model: zoneWrapper.indoorVocSensors
                    delegate: Item {
                        id: vocDelegate
                        readonly property Thing thing: zoneWrapper.indoorVocSensors.get(index)
                        property XYSeries series: null
                        readonly property NewLogsModel logsModel: NewLogsModel {
                            objectName: "voc: " + thing.name
                            engine: _engine
                            source: "state-" + thing.id + "-voc"
                            startTime: new Date(d.startTime.getTime() - d.range * 60000)
                            endTime: new Date(d.endTime.getTime() + d.range * 60000)
                            sampleRate: d.sampleRate
                            onBusyChanged: {
                                if (busy) {
                                    chartView.busyCounter++
                                } else {
                                    chartView.busyCounter--
                                }
                            }
                            onEntriesAddedIdx: {
                                for (var i = 0; i < count; i++) {
                                    var entry = logsModel.get(i)
                                    var value = entry.values["voc"]
                                    if (value == null) {
                                        value = 0;
                                    }
                                    series.insert(index + i, entry.timestamp, value)
                                }
                            }
                            onEntriesRemoved: {
                                series.removePoints(index, count)
                            }
                            Component.onCompleted: fetchLogs()
                        }

                        Component.onCompleted: {
                            series = chartView.createSeries(ChartView.SeriesTypeLine, thing.name, dateTimeAxis, vocAxis)
                            series.color = app.interfaceToColor("vocsensor")
                            series.width = 1
                            //                    series.opacity = Qt.binding(function() {
                            //                        return d.selectedSeries == null || d.selectedSeries == series ? 1 : 0.3
                            //                    })
                            series.borderWidth = 0;
                            series.borderColor = series.color
                        }
                        Component.onDestruction: {
                            chartView.removeSeries(series)
                        }

                    }
                }

                Repeater {
                    id: windowOpenRepeater
                    model: zoneWrapper.windowSensors
                    delegate: Item {
                        id: closableDelegate
                        readonly property Thing thing: zoneWrapper.windowSensors.get(index)
                        property AreaSeries series: null

                        LineSeries {
                            id: closableUpperSeries
                        }
                        LineSeries {
                            id: closableLowerSeries
                            XYPoint {x: dateTimeAxis.min.getTime(); y: 0}
                            XYPoint {x: dateTimeAxis.max.getTime(); y: 0}
                            function ensureValue(timestamp) {
                                if (count == 0) {
                                    append(timestamp, 0)
                                } else if (count == 1) {
                                    if (timestamp.getTime() < at(0).x) {
                                        insert(0, timestamp, 0)
                                    } else {
                                        append(timestamp, 0)
                                    }
                                } else {
                                    if (timestamp.getTime() < at(0).x) {
                                        remove(0)
                                        insert(0, timestamp, 0)
                                    } else if (timestamp.getTime() > at(1).x) {
                                        remove(1)
                                        append(timestamp, 0)
                                    }
                                }
                            }
                            function shrink() {
                                clear();
                                if (logsModel.count > 0) {
                                    ensureValue(logsModel.get(0).timestamp)
                                    ensureValue(logsModel.get(logsModel.count-1).timestamp)
                                }
                            }
                        }

                        readonly property NewLogsModel logsModel: NewLogsModel {
                            engine: _engine
                            source: "state-" + thing.id + "-closed"
                            startTime: new Date(d.startTime.getTime() - d.range * 60000)
                            endTime: new Date(d.endTime.getTime() + d.range * 60000)
                            property bool haveGeneratedLast: false
                            onBusyChanged: {
                                if (busy) {
                                    chartView.busyCounter++
                                } else {
                                    chartView.busyCounter--
                                }
                            }
                            onEntriesAddedIdx: {
                                for (var i = 0; i < count; i++) {
                                    var entry = logsModel.get(i)
                                    var value = entry.values["closed"]
                                    if (value == null) {
                                        value = false;
                                    }

                                    closableLowerSeries.ensureValue(entry.timestamp)

                                    // for booleans, we'll insert the opposite value right before the new one so the position is doubled
                                    var insertIdx = (index + i) * 2
                                    closableUpperSeries.insert(insertIdx, entry.timestamp.getTime() - 500, value)
                                    closableUpperSeries.insert(insertIdx+1, entry.timestamp, !value)
                                }

                                var last = closableUpperSeries.at(closableUpperSeries.count-1);
                                if (last.x < d.endTime) {
                                    closableUpperSeries.append(d.endTime, last.y)
                                    haveGeneratedLast = true
                                }
                            }
                            onEntriesRemoved: {
                                closableUpperSeries.removePoints(index * 2, count * 2)
                                if (haveGeneratedLast) {
                                    closableUpperSeries.removePoints(series.count - 1, 1)
                                    haveGeneratedLast = false
                                }
                            }
                            Component.onCompleted: fetchLogs()

                        }

                        Component.onCompleted: {
                            series = chartView.createSeries(ChartView.SeriesTypeArea, thing.name, dateTimeAxis, boolAxis)
                            series.lowerSeries = closableLowerSeries
                            series.upperSeries = closableUpperSeries
                            series.color = Style.green
                            series.opacity = 0.1
                            //                    series.opacity = Qt.binding(function() {
                            //                        return d.selectedSeries == null || d.selectedSeries == series ? 1 : 0.3
                            //                    })

                            series.borderWidth = 0;
                            series.borderColor = series.color
                        }
                        Component.onDestruction: {
                            chartView.removeSeries(series)
                        }
                    }
                }

                Repeater {
                    id: heatingRepeater
                    model: zoneWrapper.thermostats.count
                    delegate: Item {
                        id: heatingDelegate
                        readonly property Thing thing: zoneWrapper.thermostats.get(index)
                        property AreaSeries series: null


                        LineSeries {
                            id: heatingUpperSeries
                        }
                        LineSeries {
                            id: heatingLowerSeries
                            XYPoint {x: dateTimeAxis.min.getTime(); y: 0}
                            XYPoint {x: dateTimeAxis.max.getTime(); y: 0}
                            function ensureValue(timestamp) {
                                if (count == 0) {
                                    append(timestamp, 0)
                                } else if (count == 1) {
                                    if (timestamp.getTime() < at(0).x) {
                                        insert(0, timestamp, 0)
                                    } else {
                                        append(timestamp, 0)
                                    }
                                } else {
                                    if (timestamp.getTime() < at(0).x) {
                                        remove(0)
                                        insert(0, timestamp, 0)
                                    } else if (timestamp.getTime() > at(1).x) {
                                        remove(1)
                                        append(timestamp, 0)
                                    }
                                }
                            }
                            function shrink() {
                                clear();
                                if (logsModel.count > 0) {
                                    ensureValue(logsModel.get(0).timestamp)
                                    ensureValue(logsModel.get(logsModel.count-1).timestamp)
                                }
                            }
                        }

                        readonly property NewLogsModel logsModel: NewLogsModel {
                            objectName: "heat: " + thing.name
                            engine: _engine
                            source: "state-" + thing.id + "-heatingOn"
                            startTime: new Date(d.startTime.getTime() - d.range * 60000)
                            endTime: new Date(d.endTime.getTime() + d.range * 60000)
                            property bool haveGeneratedLast: false
                            onBusyChanged: {
                                if (busy) {
                                    chartView.busyCounter++
                                } else {
                                    chartView.busyCounter--
                                }
                            }
                            onEntriesAddedIdx: {
                                for (var i = 0; i < count; i++) {
                                    var entry = logsModel.get(i)
                                    var value = entry.values["heatingOn"]
                                    if (value == null) {
                                        value = false;
                                    }

                                    heatingLowerSeries.ensureValue(entry.timestamp)

                                    // for booleans, we'll insert the opposite value right before the new one so the position is doubled
                                    var insertIdx = (index + i) * 2
                                    heatingUpperSeries.insert(insertIdx, entry.timestamp.getTime() - 500, !value)
                                    heatingUpperSeries.insert(insertIdx+1, entry.timestamp, value)
                                }

                                var last = heatingUpperSeries.at(heatingUpperSeries.count-1);
                                if (last.x < d.endTime) {
                                    heatingUpperSeries.append(d.endTime, last.y)
                                    heatingLowerSeries.ensureValue(d.endTime)
                                    haveGeneratedLast = true
                                }
                            }
                            onEntriesRemoved: {
                                heatingUpperSeries.removePoints(index * 2, count * 2)
                                if (haveGeneratedLast) {
                                    heatingUpperSeries.removePoints(series.count - 1, 1)
                                    haveGeneratedLast = false
                                }
                                heatingLowerSeries.shrink()
                            }
                            Component.onCompleted: fetchLogs()
                        }

                        Component.onCompleted: {
                            series = chartView.createSeries(ChartView.SeriesTypeArea, thing.name, dateTimeAxis, boolAxis)
                            series.lowerSeries = heatingLowerSeries
                            series.upperSeries = heatingUpperSeries
                            series.color = Style.red
                            series.opacity = 0.1
                            series.borderWidth = 0;
                            series.borderColor = series.color
                        }
                        Component.onDestruction: {
                            chartView.removeSeries(series)
                        }
                    }
                }
            }

            MouseArea {
                id: mouseArea
                anchors.fill: parent
                anchors.leftMargin: chartView.plotArea.x
                anchors.topMargin: chartView.plotArea.y
                anchors.rightMargin: chartView.width - chartView.plotArea.width - chartView.plotArea.x
                anchors.bottomMargin: chartView.height - chartView.plotArea.height - chartView.plotArea.y

                hoverEnabled: true
                preventStealing: tooltipping || dragging

                property int startMouseX: 0
                property bool dragging: false
                property bool tooltipping: false

                property var startDatetime: null

                Timer {
                    interval: 300
                    running: mouseArea.pressed
                    onTriggered: {
                        if (!mouseArea.dragging) {
                            mouseArea.tooltipping = true
                        }
                    }
                }
                onReleased: {
                    mouseArea.tooltipping = false;

                    if (mouseArea.dragging) {
                        d.refreshAll()
                        mouseArea.dragging = false;
                    }
                }

                onPressed: {
                    startMouseX = mouseX
                    startDatetime = d.now
                }


                onDoubleClicked: {
                    if (selectionTabs.currentIndex == 0) {
                        return;
                    }

                    var idx = Math.ceil(mouseArea.mouseX * d.visibleValues / mouseArea.width)
                    var timestamp = new Date(d.startTime.getTime() + (idx * d.sampleRate * 60000))
                    selectionTabs.currentIndex--
                    d.now = new Date(Math.min(new Date().getTime(), timestamp.getTime() + (d.visibleValues / 2) * d.sampleRate * 60000))
                    powerBalanceLogs.fetchLogs()
                    logsLoader.fetchLogs()
                }

                onMouseXChanged: {
                    if (!pressed || mouseArea.tooltipping) {
                        return;
                    }
                    if (Math.abs(startMouseX - mouseX) < 10) {
                        return;
                    }
                    dragging = true

                    var dragDelta = startMouseX - mouseX
                    var totalTime = d.endTime.getTime() - d.startTime.getTime()
                    // dragDelta : timeDelta = width : totalTime
                    var timeDelta = dragDelta * totalTime / mouseArea.width
                    print("dragging", dragDelta, totalTime, mouseArea.width)
                    d.now = new Date(Math.min(new Date(), new Date(startDatetime.getTime() + timeDelta)))
                }

                onWheel: {
                    startDatetime = d.now
                    var totalTime = d.endTime.getTime() - d.startTime.getTime()
                    // pixelDelta : timeDelta = width : totalTime
                    var timeDelta = wheel.pixelDelta.x * totalTime / mouseArea.width
                    print("wheeling", wheel.pixelDelta.x, totalTime, mouseArea.width)
                    d.now = new Date(Math.min(new Date(), new Date(startDatetime.getTime() - timeDelta)))
                    wheelStopTimer.restart()
                }
                Timer {
                    id: wheelStopTimer
                    interval: 300
                    repeat: false
                    onTriggered: {
                        d.refreshAll();
                    }
                }

                Rectangle {
                    height: parent.height
                    width: 1
                    color: Style.foregroundColor
                    x: Math.min(mouseArea.width - 1, Math.max(0, mouseArea.mouseX))
                    visible: (mouseArea.containsMouse || mouseArea.tooltipping) && !mouseArea.dragging
                }

                Item {
                    id: tooltips
                    anchors.fill: parent
                    property var timestamp: new Date(((d.endTime.getTime() - d.startTime.getTime()) * mouseArea.mouseX / mouseArea.width) + d.startTime.getTime())
                    property int xOnRight: Math.max(0, mouseArea.mouseX) + Style.smallMargins
                    property int xOnLeft: Math.min(mouseArea.width, mouseArea.mouseX) - Style.smallMargins - tooltipWidth

                    property int tooltipWidth: 130
                    property int tooltipX: xOnLeft < 0 ? xOnRight : xOnLeft

                    onTimestampChanged: {
                        updateTimer.start();
                    }
                    Timer {
                        id: updateTimer
                        interval: 0
                        onTriggered: tooltips.update()
                    }

                    function update() {
                        var ordered = []
                        insert(thermostatTooltipRepeater, ordered);
                        insert(tempTooltipRepeater, ordered);
                        insert(humidityTooltipRepeater, ordered);
                        insert(vocTooltipRepeater, ordered);

                        for (var i = ordered.length - 1; i >= 0; i--) {
                            var item = ordered[i]
                            var newY = item.realY

                            if (i < ordered.length-1) {
                                var previous = ordered[i+1]
                                newY = Math.min(newY, previous.fixedY - item.height/* - Style.extraSmallMargins*/)
                            }

                            ordered[i].fixedY = newY
                        }
                    }

                    function insert(repeater, array) {
                        for (var i = 0; i < repeater.count; i++) {
                            var item = repeater.itemAt(i);
                            var insertIdx = 0;
                            while (array.length > insertIdx && item.realY > array[insertIdx].realY) {
                                insertIdx++
                            }
                            array.splice(insertIdx, 0, item)
                        }
                    }
                }


                Repeater {
                    id: thermostatTooltipRepeater
                    model: thermostatsRepeater.count

                    delegate: TooltipDelegate {
                        visible: (mouseArea.containsMouse || mouseArea.tooltipping) && !mouseArea.dragging
                        thing: thermostatsRepeater.itemAt(index).thing
                        entry: thermostatsRepeater.itemAt(index).logsModel.find(tooltips.timestamp)
                        color: app.interfaceToColor("temperaturesensor")
                        iconSource: app.interfaceToIcon("temperaturesensor")
                        valueName: "temperature"
                        axis: temperatureAxis
                        x: tooltips.tooltipX
                        width: tooltips.tooltipWidth
                        backgroundItem: chartView
                        backgroundRect: Qt.rect(mouseArea.x + x, mouseArea.y + y, width, height)
                        unit: Types.UnitDegreeCelsius
                    }
                }

                Repeater {
                    id: tempTooltipRepeater
                    model: tempRepeater.count

                    delegate: TooltipDelegate {
                        visible: (mouseArea.containsMouse || mouseArea.tooltipping) && !mouseArea.dragging
                        thing: tempRepeater.itemAt(index).thing
                        entry: tempRepeater.itemAt(index).logsModel.find(tooltips.timestamp)
                        valueName: "temperature"
                        color: app.interfaceToColor("temperaturesensor")
                        iconSource: app.interfaceToIcon("temperaturesensor")
                        axis: temperatureAxis
                        x: tooltips.tooltipX
                        width: tooltips.tooltipWidth
                        backgroundItem: chartView
                        backgroundRect: Qt.rect(mouseArea.x + x, mouseArea.y + y, width, height)
                        unit: Types.UnitDegreeCelsius
                    }
                }

                Repeater {
                    id: humidityTooltipRepeater
                    model: humidityRepeater.count

                    delegate: TooltipDelegate {
                        visible: (mouseArea.containsMouse || mouseArea.tooltipping) && !mouseArea.dragging
                        thing: humidityRepeater.itemAt(index).thing
                        entry: humidityRepeater.itemAt(index).logsModel.find(tooltips.timestamp)
                        color: app.interfaceToColor("humiditysensor")
                        iconSource: app.interfaceToIcon("humiditysensor")
                        valueName: "humidity"
                        axis: humidityAxis
                        x: tooltips.tooltipX
                        width: tooltips.tooltipWidth
                        backgroundItem: chartView
                        backgroundRect: Qt.rect(mouseArea.x + x, mouseArea.y + y, width, height)
                        unit: Types.UnitPercentage
                    }
                }

                Repeater {
                    id: vocTooltipRepeater
                    model: vocRepeater.count

                    delegate: TooltipDelegate {
                        visible: (mouseArea.containsMouse || mouseArea.tooltipping) && !mouseArea.dragging
                        thing: vocRepeater.itemAt(index).thing
                        entry: vocRepeater.itemAt(index).logsModel.find(tooltips.timestamp)
                        valueName: "voc"
                        color: app.interfaceToColor("vocsensor")
                        iconSource: app.interfaceToIcon("vocsensor")
                        axis: vocAxis
                        x: tooltips.tooltipX
                        width: tooltips.tooltipWidth
                        backgroundItem: chartView
                        backgroundRect: Qt.rect(mouseArea.x + x, mouseArea.y + y, width, height)
                        unit: Types.UnitPartsPerMillion
                    }
                }
            }
        }

        GridLayout {
            id: legend
            Layout.fillWidth: true
            Layout.leftMargin: chartView.plotArea.x
            Layout.rightMargin: Style.smallMargins
            Layout.bottomMargin: Style.smallMargins
            columns: Math.min(Math.max(1, width / 100),
                              thermostatsRepeater.count
                              + tempRepeater.count
                              + humidityRepeater.count
                              + vocRepeater.count
                              + (windowOpenRepeater.count > 0 ? 1 : 0)
                              + (heatingRepeater.count > 0 ? 1 : 0))

            Repeater {
                model: thermostatsRepeater.count
                delegate: LegendDelegate {
                    Layout.fillWidth: true
                    text: thermostatsRepeater.itemAt(index).thing.name
                    iconName: app.interfaceToIcon("thermostat")
                    color: app.interfaceToColor("temperaturesensor")
                }
            }
            Repeater {
                model: tempRepeater.count
                delegate: LegendDelegate {
                    Layout.fillWidth: true
                    text: tempRepeater.itemAt(index).thing.name
                    iconName: app.interfaceToIcon("temperaturesensor")
                    color: app.interfaceToColor("temperaturesensor")
                }
            }
            Repeater {
                model: humidityRepeater.count
                delegate: LegendDelegate {
                    Layout.fillWidth: true
                    text: humidityRepeater.itemAt(index).thing.name
                    iconName: app.interfaceToIcon("humiditysensor")
                    color: app.interfaceToColor("humiditysensor")
                }
            }
            Repeater {
                model: vocRepeater.count
                delegate: LegendDelegate {
                    Layout.fillWidth: true
                    text: vocRepeater.itemAt(index).thing.name
                    iconName: app.interfaceToIcon("vocsensor")
                    color: app.interfaceToColor("vocsensor")
                }
            }
            LegendDelegate {
                Layout.fillWidth: true
                color: Qt.rgba(Style.green.r, Style.green.g, Style.green.b, 0.2)
                text: qsTr("Window open")
            }
            LegendDelegate {
                Layout.fillWidth: true
                color: Qt.rgba(Style.red.r, Style.red.g, Style.red.b, 0.2)
                text: qsTr("Heating on")
            }
        }
    }

}

