#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# vim: expandtab sw=4 ts=4 sts=4:
#
# Beehive-Monitoring, process SMS Requests
#
# Author: Joerg Lehmann, nbit Informatik GmbH
#
"""Beehive Monitoring - SMS Processing"""

from __future__ import print_function
import gammu
import os
import sys
import time
import yaml
import smtplib
import re
import glob
import shutil
import random
import string
import subprocess
from os.path import basename
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate

# Root Path
APP_ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))

# Set Default Encoding to UTF-8
reload(sys)  # Reload does the trick!
sys.setdefaultencoding('UTF8')

# State Machine fuer Gammu
sm = gammu.StateMachine()

# Alarmunterdrueckung mit "manipulation"
mute_alarm_until = None

# Read Configuration from YAML-File
with open("%s/bin/beielimon-config.yaml" % APP_ROOT, 'r') as stream:
    try:
        config_data = yaml.load(stream)
    except yaml.YAMLError as exc:
        print(exc)

def send_sms(phonenumbers , text):
    for phonenumber in phonenumbers:
        message = {
        'Text': text,
        'SMSC': {'Location': 1},
        'Number': phonenumber
        }

        #GELD SPAREN sm.SendSMS(message)
        print("Send SMS to %s, Text: %s" % (phonenumber, text))
        sm.SendSMS(message)

def send_mail(send_from, send_to, subject, text, files=None):
    sm.Terminate()
    os.system('/usr/bin/sudo %s/root-bin/connect_to_internet' % (APP_ROOT))
    msg = MIMEMultipart()
    msg['From'] = send_from
    msg['To'] = send_to
    msg['Date'] = formatdate(localtime=True)
    msg['Subject'] = subject

    msg.attach(MIMEText(text,"plain","utf-8"))

    for f in files or []:
        with open(f, "rb") as fil:
            part = MIMEApplication(
                fil.read(),
                Name=basename(f)
            )
        # After the file is closed
        part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
        msg.attach(part)

    if config_data['mailserver_tls'] == 'ssl':
        smtp = smtplib.SMTP_SSL(config_data['mailserver'],config_data['mailserver_port'],timeout=120)
    else:
        smtp = smtplib.SMTP(config_data['mailserver'],config_data['mailserver_port'],timeout=120)
    smtp.set_debuglevel(1)
    smtp.ehlo()
    if config_data['mailserver_tls'] == 'starttls':
        smtp.starttls()
    if config_data['mailuser'] != '':
        smtp.login(config_data['mailuser'], config_data['mailpwd'])
    smtp.sendmail(send_from, send_to, msg.as_string())
    smtp.close()
    os.system('/usr/bin/sudo %s/root-bin/disconnect_from_internet' % (APP_ROOT))
    # Wir reinitialisieren das Modem
    sm.Init()

def send_help(phonenumber,command):
    # SMS Maximale Groesse: 140 Zeichen
    if command == '':
        sms_message = """Mögliche Befehle:

help, info, manipulation, balance, reboot, shutdown, hotspot

Nähere Hilfe: help <befehl>, z.B. help info"""

    elif command == 'info':
        sms_message = """info - letzte Messwerte (SMS)
info 2017 - Messwerte 2017 (EMail)
info 201711 - Messwerte Nov. 2017
info 20171102 - Messwerte 2. Nov. 2017"""

    elif command == 'help':
        sms_message = "Zeigt die möglichen Befehle an"
    
    elif command == 'manipulation':
        sms_message = "Manipulation - Schwarmalarm wird für %d Minuten abgeschaltet" % (config_data['manipulation_duration_minutes'])
    
    elif command == 'balance':
        sms_message = "Anforderung Info zum Prepaid-Guthaben"

    elif command == 'reboot':
        sms_message = "Neustart des BeieliPi's"

    elif command == 'shutdown':
        sms_message = """Herunterfahren des BeieliPi's

ACHTUNG: ZUM STARTEN MUSS STROM AUS- UND WIEDER EINGESTECKT WERDEN!
"""

    elif command == 'hotspot':
        sms_message = """hotspot on - Hotspot Funktion einschalten
hotspot off - Hotspot ausschalten

Hotspot erlaubt ein Auslesen per Smartphone/Table (WLAN)"""

    send_sms([phonenumber],sms_message)


def GetLastValues(scale_uuid):
    files = glob.glob("%s/data/weight-%s-????????.log" % (APP_ROOT,scale_uuid))
    if len(files) > 0:
        with open(sorted(files)[-1], "r") as f:
            for line in f: pass
            result =  line
        

        # Beispiel: 2017-11-07 08:23,0
        m = re.match("(\d{4})-(\d{2})-(\d{2}) (\d\d:\d\d),(\d+)", result)
        if m:
            return "%sg (%s.%s.%s %s)" % (m.group(5),m.group(3),m.group(2),m.group(1),m.group(4))
        else:
            return "Fehler beim Parsen: %s" % (result)

    else:
       return "keine Messwerte"

def ProcessSend_SMS_Queue():
    files = glob.glob("%s/send_sms_queue/*yml" % (APP_ROOT))
    for f in files:
        with open("%s" % f, 'r') as stream:
            try:
                sms_data = yaml.load(stream)
            except yaml.YAMLError as exc:
                print(exc)
        os.remove(f)
        if is_muted():
            print("Alarmierung ist abgeschaltet bis %s" % (time.strftime("%d.%m.%Y %H:%M", mute_alarm_until)))
        else:
            send_sms(sms_data['phonenumbers'],sms_data['text'])
        
def CreateAttachements(infotime):
    mypid = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(7))
    mycsvdir = "%s/tmp/%s/csv" % (APP_ROOT,mypid)
    if not os.path.exists(mycsvdir):
        os.makedirs(mycsvdir)
    myzipdir = "%s/tmp/%s/zip" % (APP_ROOT,mypid)
    if not os.path.exists(myzipdir):
        os.makedirs(myzipdir)
    res = []
    my_pattern = '%s%s' % (infotime,'?' * (8 - len(infotime)))
    files = glob.glob("%s/data/weight-*-%s.log" % (APP_ROOT,my_pattern))
    for s in config_data['scales']:
        filename = "%s/%s-%s.csv" % (mycsvdir,s['alias'].replace(' ','_'),infotime)
        with_data = False
        with open(filename, 'a') as file:
            for ifile in sorted(files):
                if (s['scale_uuid'] in ifile) and (infotime in ifile):
                    with_data = True
                    with open(ifile, 'r') as ifile:
                        for line in ifile:
                            m = re.match("(\d{4})-(\d{2})-(\d{2}) (\d\d:\d\d),(\d+)", line)
                            if m:
                                file.write("%s.%s.%s %s,%s\n" % (m.group(3),m.group(2),m.group(1),m.group(4),m.group(5)))
                            else:
                                file.write("Fehler beim Parsen: %s" % (line))
        if with_data:
           res.append(filename)   

    files = glob.glob("%s/data/swarmalarm-*-%s.log" % (APP_ROOT,my_pattern))
    for s in config_data['scales']:
        filename = "%s/Schwarmalarm-%s-%s.csv" % (mycsvdir,s['alias'].replace(' ','_'),infotime)
        with_data = False
        with open(filename, 'a') as file:
            for ifile in sorted(files):
                if (s['scale_uuid'] in ifile) and (infotime in ifile):
                    with_data = True
                    with open(ifile, 'r') as ifile:
                        for line in ifile:
                            m = re.match("(\d{4})-(\d{2})-(\d{2}) (\d\d:\d\d),(\d+)", line)
                            if m:
                                file.write("%s.%s.%s %s\n" % (m.group(3),m.group(2),m.group(1),m.group(4)))
                            else:
                                file.write("Fehler beim Parsen: %s" % (line))
        if with_data:
           res.append(filename)   

    zipfile = "%s/%s" % (myzipdir,infotime)
    zipfile = "%s/%s" % (myzipdir,infotime)
    shutil.make_archive(zipfile, 'zip', mycsvdir)
    return [ "%s.zip" % (zipfile) ]       

def send_report(infotime):
    # Send EMail Report
    my_text = my_text = "Diese Meldung ist die Antwort auf die SMS-Anfrage \"info %s\"\n\n" % (infotime)
    my_text += "Letzte Messwerte:\n"
    for s in config_data['scales']:
        my_text += "%s: %s\n" % (s['alias'],GetLastValues(s['scale_uuid']))
    my_text += "\n\nIm ZIP-File sind die Messwerte der angefragten Periode enthalten. Die Messdaten sind im CSV-Format abgespeichert und können z.B. mit Excel analysiert werden.\n\n"
    attachements = CreateAttachements(infotime)

    send_mail(config_data['mailfrom'],config_data['mailto'],'Messwerte BeieliPi',my_text,attachements)

def send_info_sms(phonenumber):
    my_text = "Letzte Messwerte:\n"
    for s in config_data['scales']:
        my_text += "%s: %s\n" % (s['alias'],GetLastValues(s['scale_uuid']))

    if mute_alarm_until:
        my_text += "\nAlarmierung ist abgeschaltet bis %s" % (time.strftime("%d.%m.%Y %H:%M", mute_alarm_until))

    send_sms([phonenumber],my_text)

def send_info(phonenumber, message_uc):
    m = re.match(".*(INFO)\s+(\d{8})", message_uc)
    if m:
        send_report(m.group(2))
    else:
        m = re.match(".*(INFO)\s+(\d{6})", message_uc)
        if m:
            send_report(m.group(2))
        else:
            m = re.match(".*(INFO)\s+(\d{4})", message_uc)
            if m:
                send_report(m.group(2))
            else:
                send_info_sms(phonenumber)

def start_manipulation():
    global mute_alarm_until
    mute_alarm_until = time.localtime(time.time() + (config_data['manipulation_duration_minutes'] * 60))
    print("Alarmierung ist abgeschaltet bis %s" % (time.strftime("%d.%m.%Y %H:%M", mute_alarm_until)))

def is_muted():
    global mute_alarm_until
    res = False
    if mute_alarm_until:
        if time.localtime() < mute_alarm_until:
            res = True
        else:
            res = False
            mute_alarm_until = None

    return res

def balance(phonenumber):
    sm.Terminate()
    sms_message = subprocess.check_output(["/usr/bin/sudo","/home/beieli/root-bin/get_balance.sh", config_data['balance_ussd']])
    sm.Init()
    send_sms([phonenumber],sms_message)

def reboot():
    os.system('/usr/bin/sudo /sbin/init 6')

def shutdown():
    os.system('/usr/bin/sudo /sbin/init 0')

def hotspot_on():
    os.system('/usr/bin/sudo %s/root-bin/hotspot_on' % (APP_ROOT))

def hotspot_off():
    os.system('/usr/bin/sudo %s/root-bin/hotspot_off' % (APP_ROOT))

def command_not_understood(phonenumber, message):
    send_sms([phonenumber],'Befehl nicht verstanden: %s\n\nMögliche Befehle: help, info, manipulation, balance, reboot, shutdown, hotspot' % (message[:50]))


def process_received_sms(phonenumber,message):
    # message in Grossbuchstaben (damit Gross-/Kleinschreibung keine Rolle spielt)
    message_uc = message.upper()

    # Bestimmung, ob es eine gueltige Telefonnummer ist
    valid_number = False
    for s in config_data['scales']:
        if phonenumber in s['sms_alert_phonenumbers']:
            valid_number = True
    
    if not valid_number:
        print("Da versucht ein unberechtigter, etwas abzufragen... (Nummer: %s)" % (phonenumber))
        return

    if 'HELP INFO' in message_uc:
        send_help(phonenumber,'info')
    elif 'HELP MANIPULATION' in message_uc:
        send_help(phonenumber,'manipulation')
    elif 'HELP HELP' in message_uc:
        send_help(phonenumber,'help')
    elif 'HELP BALANCE' in message_uc:
        send_help(phonenumber,'balance')
    elif 'HELP REBOOT' in message_uc:
        send_help(phonenumber,'reboot')
    elif 'HELP SHUTDOWN' in message_uc:
        send_help(phonenumber,'shutdown')
    elif 'HELP HOTSPOT' in message_uc:
        send_help(phonenumber,'hotspot')
    elif 'HELP' in message_uc:
        send_help(phonenumber,'')
    elif 'INFO' in message_uc:
        send_info(phonenumber, message_uc)
    elif 'MANIPULATION' in message_uc:
        start_manipulation()
    elif 'BALANCE' in message_uc:
        balance(phonenumber)
    elif 'REBOOT' in message_uc:
        reboot()
    elif 'SHUTDOWN' in message_uc:
        shutdown()
    elif 'HOTSPOT OFF' in message_uc:
        hotspot_off()
    elif 'HOTSPOT ON' in message_uc:
        hotspot_on()
    else:
        command_not_understood(phonenumber, message)
    

def main():
    sm.ReadConfig()
    sm.Init()
    counter = 9999


    while True:
        counter += 1
        if counter > 1000:
            # Wir synchronisren die Zeit mit dem Internet
            sm.Terminate()
            os.system('/usr/bin/sudo %s/root-bin/sync_time_with_internet' % (APP_ROOT))
            sm.Init()
            counter = 0
        status = sm.GetSMSStatus()
        #print(status)
        remain = status['SIMUsed'] + status['PhoneUsed'] + status['TemplatesUsed']

        if remain:
            firstsms = sm.GetNextSMS(Start = True, Folder = 0)
            #print("FIRSTSMS: %s" % (firstsms))
            for x in range(len(firstsms)):
                #sm.DeleteSMS(firstsms[x]['Folder'], firstsms[x]['Location'])
                sm.DeleteSMS(0, firstsms[x]['Location'])
                #print("sm.DeleteSMS: %s %s" % (firstsms[x]['Folder'], firstsms[x]['Location']))
                number = firstsms[x]['Number']
                message = firstsms[x]['Text']
                #print(number,message)
                print("process_received_sms %s %s" % (number,message))
                process_received_sms(number,message)
                remain = remain - len(firstsms)
        
        sys.stdout.flush()
        ProcessSend_SMS_Queue()
        time.sleep(15) 
        
        

if __name__ == "__main__":
    main()
