#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""Main program."""
__author__ = "Sven Sager"
__copyright__ = "Copyright (C) 2018 Sven Sager"
__license__ = "GPLv3"
__version__ = "0.1.1"

import logging
import signal
from influxdb import InfluxDBClient
from os.path import join
from paho.mqtt.client import Client
from ssl import CERT_NONE
from threading import Event

logger = logging.getLogger()

# !!! Nur zum TESTEN, auf INFO belassen, sonst WARNING !!!
logging.basicConfig(level=logging.INFO)
# logging.basicConfig(level=logging.WARNING)


class LittleMqttToInflux():

    """Main program class."""

    def __init__(self):
        """Init LittleMqttToInflux class."""
        # System variables
        self._evt_exit = Event()
        self._idb = None
        self._mqtt_rc = -1
        # --------------------------------------------------

        # TODO: Diese Werte an eigenen Server anpassen!

        # Basetopic muss hier OHNE '/revpi01' angegeben werden! Es handelt
        # sich um die unterste MQTT-Ebene, dies wird unser Datanbankname.
        self.basetopic = "datensammlung"

        # MQTT Einstellungen
        self.mqtthost = "127.0.0.1"
        self.mqttuser = "systemreader"
        self.mqttpassword = "test"
        self.mqttport = 1883
        self.mqtttls = False

        # Influx Einstellungen
        self.influxdatabase = self.basetopic
        self.influxserver = "127.0.0.1"
        self.influxport = 8086

        # --------------------------------------------------
        # Catch signales
        signal.signal(signal.SIGINT, self._sigexit)
        signal.signal(signal.SIGTERM, self._sigexit)

    def _sigexit(self, signum, sigframe):
        """Signal handler to clean and exit program."""
        self.stop()

    def mqtt_connect(self, client, userdata, flags, rc):
        """Wird ausgeloest bei connect/reconnect zum Broker."""
        # Verbindungs rc merken
        self._mqtt_rc = rc
        if rc > 0:
            return

        # Basetopic subscriben
        client.subscribe(join(self.basetopic, "#"))

    def mqtt_message(self, client, userdata, msg):
        """Wird immer ausgeloest wenn eine MQTT-Nachricht kommt."""

        # Topics aufsplitten. Dabei entsteht eine Liste, deren Einträge wir
        # wie folgt interpretieren:

        # Topic: datensammlung/revpi01/io/temp_heater
        # [ datenbankname , measurement,  text   ,   sensorname ]
        # ["datensammlung",  "revpi01" ,  "io"   , "temp_heater"]
        topics = msg.topic.split("/")

        # Wenn es nicht genau 4 Einträge gibt, verwerfen wir den Datensatz
        if len(topics) != 4:
            return

        # Die Daten stehen als bytes() in msg.payload. Diese versuchen wir
        # IMMER in float() zu wandeln.
        try:
            value = float(msg.payload)
        except Exception:
            # Datensatz verwerfen
            return

        # Directory für das Schreiben in InfluxDB erstellen
        dataset = {
            "measurement": topics[1],
            "tags": {
                "sensorname": topics[3],
            },
            "fields": {
                "value": value
            }
        }

        # Directory in InfluxDB schreiben
        try:
            self._idb.write_points([dataset])
            logger.info("geschrieben: {0} {1}".format(topics[3], value))
        except Exception:
            logger.error("Fehler beim Schreiben in InfluxDB")

    def start(self):
        """main program with loop."""

        # InfluxDB einrichten
        self._idb = InfluxDBClient(
            database=self.influxdatabase,
            host=self.influxserver,
            port=self.influxport
        )

        # Datenbank anlegen, sollte sie nicht existieren
        try:
            self._idb.create_database(self.basetopic)
        except Exception:
            logger.error("Kann Datenbank nicht erzeugen")
            raise RuntimeError("Kann Datenbank nicht erzeugen")

        # MQTT aktivieren
        mqtt = Client()
        if self.mqttuser != "":
            mqtt.username_pw_set(self.mqttuser, self.mqttpassword)
        if self.mqtttls:
            mqtt.tls_set(cert_reqs=CERT_NONE)
            mqtt.tls_insecure_set(True)

        mqtt.on_connect = self.mqtt_connect
        mqtt.on_message = self.mqtt_message

        # mainloop
        while not self._evt_exit.is_set():

            # Erstes MQTT Verbinden versuchen
            if self._mqtt_rc == -1:
                try:
                    mqtt.connect(self.mqtthost, self.mqttport, 60)
                except Exception:
                    logger.error("mqtt connect error")
                else:
                    mqtt.loop_start()

            # Wait (never use sleep)
            self._evt_exit.wait(1)

        # MQTT trennen
        mqtt.loop_stop()

    def stop(self):
        """Stop mainloop."""
        self._evt_exit.set()


if __name__ == "__main__":
    root = LittleMqttToInflux()
    root.start()
