#!/usr/bin/env /usr/bin/python3

"""
mylib2.py
24.04.2021
- Bibliotheksfunktionen für smartHome-Programme
"""

# PACKAGES
import logging
from logging.handlers import RotatingFileHandler
import time
import mysql.connector
import datetime
import sys
import os
import requests
import json
import smtplib, ssl
from socket import gaierror
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import formatdate
from email import encoders

# GLOBALE VARIABLEN
logger = 0
(ST_UNBEKANNT, ST_OK, ST_INFO, ST_WARN, ST_ERROR, ST_ALARM) = range(6)

# Funktionen


def startlogging(prog, level):
    global logger
    lg = logging.getLogger('{}'.format(prog))
    lg.setLevel(level)
    rfh = RotatingFileHandler('/home/smartHome/log/{}.log'.format(prog),
                              maxBytes=1*1024*1024, backupCount=10)
    rfh.setLevel(level)
    lfo = logging.Formatter('%(asctime)s %(name)s %(levelname)-6s %(message)s')
    rfh.setFormatter(lfo)
    lg.addHandler(rfh)
    logger = lg
    return lg
# startlogging()

# Safe cast
def cast(to_type, val, std=None):
    try:
        return to_type(val)
    except (ValueError, TypeError):
        logger.debug('cast_error: {}'.format(val))
        return std
# cast()

## smartHome-Datenbank
class mydb:
    def __init__(self, user='home', password='tm3110H', host='127.0.0.1', database='home', options=None):
        self.db = None
        self.user = user
        self.password = password
        self.host = host
        self.database = database
        self.options = options
        self.connect()
        self.OK = True
    # mydb.__init__()

    # DB verbinden
    def connect(self):
        self.OK = False
        if self.db != None:  # schon verbunden?
            if (self.db.is_connected()):
                self.OK = True
                logger.debug('DB war verbunden host:{} database:{}'.format(
                    self.host, self.database))
                return
        for cnt in range(5):  # 5 Versuche
            try:
                if self.options != None:
                    logger.debug('DB: Parameter:{}'.format(self.options))
                    self.db = mysql.connector.connect(
                        option_files=self.options)
                    self.host = self.db.host
                    self.database = self.db.database
                else:
                    self.db = mysql.connector.connect(user=self.user,
                                                      password=self.password,
                                                      host=self.host,
                                                      database=self.database,
                                                      auth_plugin='mysql_native_password')
                self.OK = True
                break
                logger.debug('DB verbunden host:{} database:{}'.format(
                    self.host, self.database))
            except mysql.connector.Error as err:
                if (cnt == 4):
                    logger.error('DB Fehler (connect): {}'.format(err))
                time.sleep(1)
        return self.OK
    # mydb.connect()

    # DB schließen
    def close(self):
        logger.debug('mydb.close()')
        self.OK = False
        if (self.db.is_connected()):
            try:
                self.db.close()
                self.OK = True
                logger.debug('DB geschlossen')
            except self.db.Error as err:
                logger.error('DB Fehler (close): {}'.format(err))
        else:
            logger.debug('DB war geschlossen')
            self.OK = True
        return self.OK
    # mydb.close()

    # einen Wert aus DB lesen
    def read_one(self, query):
        logger.debug('mydb.read_one({})'.format(query))
        self.OK = False
        try:
            mycurs = self.db.cursor(buffered=True)
            mycurs.execute(query)
            myrec = mycurs.fetchone()
            self.OK = True
            logger.debug(myrec)
            return myrec
        except mysql.connector.Error as error:
            logger.error("DB Fehler (read_one): {}".format(error))
        finally:
            try:
                mycurs.close()
            except:
                pass
    # mydb.read_one()

    # alle Werte lesen
    def read_all(self, query):
        logger.debug('mydb.read_all({})'.format(query))
        self.OK = False
        try:
            mycurs = self.db.cursor(buffered=True)
            mycurs.execute(query)
            myrec = mycurs.fetchall()
            self.OK = True
            logger.debug(myrec)
            return myrec
        except mysql.connector.Error as error:
            logger.error("DB Fehler (read_all): {}".format(error))
        finally:
            try:
                mycurs.close()
            except:
                pass
    # mydb.read_all()

    # SQL-Abfrage, ohne Rückgabe
    def query(self, query):
        logger.debug('mydb.query({})'.format(query))
        self.OK = False
        for cnt in range(5):
            try:
                mycurs = self.db.cursor(buffered=True)
                mycurs.execute(query)
                self.db.commit()
                self.OK = True
                logger.debug('committed')
                break
            except mysql.connector.Error as error:
                if (cnt == 4):
                    logger.error("DB Fehler (query): {}".format(error))
            finally:
                if (self.db.is_connected()):
                    mycurs.close()
        return self.OK
    # mydb.query()

    def log_value(self, idgr, wert, text=''):
        cnt = 0
        q = "call log_value('{}', {}, '{}')".format(idgr, wert, text)
        while (cnt < 5):
            try:
                # self.query("call log_value('{}', {}, '{}')".format(idgr, wert, text))
                self.query(q)
            except:
                cnt += 1
                logger.debug("SQL Fehler {}".format(q))
                time.sleep(1)
                continue
    #        except:
    #            logger.exception('SQL Fehler log_value - sonst')
            else:
                break

    def get_value(self, idgr):
        q = "SELECT wert FROM wt_aktuell WHERE id_groesse = '{}';".format(idgr)
        return self.read_one(q)[0]


# Prozess
class prozess:
    def __init__(self, name, intervall=60, own=True, db=None):
        self.name = name
        self.db = db
        self.intervall = intervall
        self.pid = 0
        self.tstart = time.time()

        if own:
            self.pid = os.getpid()
        elif db != None:
            self.pid = self.db.readwtakt(self.name)
            self.intervall = int(self.db.readtxakt(self.name))
    # prozess.__init__()

    # Prozess starten, ggf. DB verbinden
    def start(self, connectdb=False):
        self.tstart = time.time()
        if (self.db != None) and connectdb:
            self.db.connect()
    # prozess.start()

    # Prozess pausieren (Intervall um Prozesszeit korrigieren)
    def sleep(self, intervall=0, closedb=False):
        if intervall > 0.0:
            self.intervall = intervall
        tproz = int(time.time() - self.tstart)
        tsleep = max(1, self.intervall - tproz)
        logger.debug('=== WARTEN === {}s'.format(tsleep))
        if self.db != None:
            self.db.log_value(self.name, int(self.pid), str(self.intervall))
            if closedb:
                self.db.close()
        time.sleep(tsleep)
    # prozess.sleep()
## class prozess

def convert24(str1):
    # print(str1)
    if str1[0] == '?':
        return str1
    if str1[0] == '*':
        return ' '
    # Stunde einstellig
    if str1[1:2] == ':':
        str1 = "0"+str1
    # Checking if last two elements of time
    # is AM and first two elements are 12
    if str1[-2:] == "AM" and str1[:2] == "12":
        return "00" + str1[2:-2]

    # remove the AM
    elif str1[-2:] == "AM":
        return str1[:-2]

    # Checking if last two elements of time
    # is PM and first two elements are 12
    elif str1[-2:] == "PM" and str1[:2] == "12":
        return str1[:-2]
    else:
        # add 12 to hours and remove PM
        return str(int(str1[:2]) + 12) + str1[2:5]

def convertHHMM(hhmm, sa, su):
    if len(hhmm) < 2:
        return -1
    if hhmm[0].upper() == 'S':
        if hhmm[1].upper() == 'A': # Sonnenaufgang
            tmin = sa
        elif hhmm[1].upper() == 'U':
            tmin = su
        else:
            return -1
        if len(hhmm) < 3:
            return tmin
        if hhmm[2] == '-':
            if hhmm[3:].isdecimal():
                return tmin - int(hhmm[3:])
            else:
                return -1
        elif hhmm[2] == '+':
            if hhmm[3:].isdecimal():
                return tmin + int(hhmm[3:])
            else:
                return -1
        else:
            return tmin
    else: # Uhrzeit
        #logger.debug('hhmm: {}'.format(hhmm))
        hm = hhmm.split(':')
        if hm[0].isdecimal():
            tmin = int(hm[0]) * 60
        else:
            return -1
        if len(hm) == 1: # hh
            return tmin
        if hm[1].isdecimal():
            return tmin + int(hm[1])
        else:
            return -1        

## Mail
class Mail():
    def __init__(self, sender, receiver):
        self.smtp_port = 587
        self.smtp_server = "smtp.ionos.de"
        self.login = "torsten@muehmel-net.de"
        self.passwd = "tm31102"
        self.sender = sender
        self.receiver = receiver

    def send(self, subject, message):
        timestr = time.strftime('(%d.%m.%Y)')
        msg = MIMEMultipart()
        msg['From'] = self.sender
        msg['To'] = self.receiver
        msg['Date'] = formatdate(localtime = True)
        msg['Subject'] = subject
        msg.attach(MIMEText(message, 'plain'))

        context = ssl.create_default_context()
        smtp = smtplib.SMTP(self.smtp_server, self.smtp_port)
        smtp.ehlo()
        smtp.starttls(context=context)
        smtp.login(self.login,self.passwd)
        smtp.sendmail(self.sender, self.receiver, msg.as_string())
        smtp.quit()
## class Mail
