"""
solthermpi - Soltherm Raspberry Pi monitoring package, version {version}

Usage:
  python -m solthermpi (writedata|senddata|update|sendcronlog|cleanup|rebootif)

Arguments:
  writedata        Read temperature measurements from sensor and write them
                   to a logfile
  senddata         send temperature data to FTP server, move uploaded files to
                   log archive directory
  update           check FTP server for machine specific config files and
                   new versions of this package. Install if any.
  sendcronlog      Send log files with cron output to FTP server. Delete
                   uploaded log files.
  cleanup          Clean up log archive directory (delete everything older than
                   100 days)
  rebootif         Check FTP server for rebootNeeded file and reboot if found.
  reboot           Reboot now
  resetusb         Check FTP server for resetUSB file and cut the usb power for
                   five seconds if found.
  display-serialno Display Raspberry Pi serial number
"""

from datetime import datetime, timedelta, date
import subprocess
import os
import sys
import re
from tempfile import TemporaryDirectory
import pip
import time
from time import strftime
import shlex
import glob

from .config import config
from .config import CONFIGFILE, init_config
from .utils import (get_serial_raspberry, ftp_get_file, ftp_list_files,
                    ftp_put_file, ftp_delete_file)
from .crontab import update_crontab
from . import __version__


__doc__ = __doc__.format(version=__version__)


def main():
    if len(sys.argv) != 2:
        print(__doc__)
        return

    option = sys.argv[1]

    if option == "writedata":
        writedata()
    elif option == "senddata":
        senddata()
    elif option == "update":
        update_config()
        update_software()
    elif option == "sendcronlog":
        send_cronlog()
    elif option == "cleanup":
        cleanup()
    elif option == "rebootif":
        rebootIf()
    elif option == "reboot":
        reboot()
    elif option == "resetusb":
        resetUsb()
    elif option == "display-serialno":
        print(get_serial_raspberry())
    else:
        print(__doc__)
        return


def writedata():
    """
    Read temperature sensors and write measurements to log file.
    """
    # suche alle Temperatursensoren
    # this creates a file .digitemprc in current directory
    cmd_init = ['digitemp_DS9097U', '-i', '-s', '/dev/ttyUSB0']
    try:
        out = subprocess.check_output(cmd_init, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
        raise Exception("command '{}' returned with non-zero exit status: {}"
                        .format(" ".join(cmd_init), e.output))
    else:
        print(out)

    logdir = config.get("GENERAL", "logdir")

    # schreibe die Sensordaten in Datei in log-Ordner
    # TODO UTC or local time?
    today = datetime.utcnow().strftime("%Y%m%d")
    myserial = get_serial_raspberry()
    filename = '{serial}_{date}'.format(serial=myserial, date=today)
    logfile = os.path.join(logdir, filename)
    cmd = ['digitemp_DS9097U', '-a',
           '-l', logfile, '-o', '%Y-%m-%d %H:%M:%S; %R; %.2C']
    try:
        output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
        raise Exception("command '{}' returned with non-zero exit status: {}"
                        .format(" ".join(cmd), e.output))
    print(output)


def senddata():
    """
    Upload logfiles to FTP server.
    """
    ftp_dir = config.get("GENERAL", "ftp_logfiles_dir")
    logdir = config.get("GENERAL", "logdir")
    logdir_archive = config.get("GENERAL", "logdir_archive")
    os.makedirs(logdir_archive, exist_ok=True)

    regex = re.compile(".+_([0-9]{8})")
    logfiles = [f for f in os.listdir(logdir) if regex.match(f)]

    for logfile in logfiles:
        filename_server = os.path.join(ftp_dir, logfile)
        ftp_put_file(file_local=os.path.join(logdir, logfile),
                     file_remote=filename_server)
        print("uploaded {} to {}".format(logfile, filename_server))

        # move files of previous days to archive folder (today's file is still
        # being written)
        date_str = re.match(regex, logfile).groups()[0]
        t = datetime.strptime(date_str, "%Y%m%d")
        if t.date() < date.today():
            os.rename(src=os.path.join(logdir, logfile),
                      dst=os.path.join(logdir_archive, logfile))


def update_config():
    """
    download serial no. specific config file from ftp server and copy it
    to $HOME. Also update crontab.
    """
    serialno = get_serial_raspberry()
    ftp_dir = config.get("GENERAL", "ftp_logfiles_dir")
    configfile_remote = os.path.join(ftp_dir, "config", serialno, CONFIGFILE)
    configfile_local = os.path.join(os.path.expanduser("~"), CONFIGFILE)
    try:
        ftp_get_file(configfile_remote, configfile_local)
    except FileNotFoundError:
        print("FTP server contains no config file for serial no. '{}'. "
              "Nothing to do.".format(serialno))
        return
    else:
        print("Downloaded new config file {}".format(configfile_local))

    # update crontab with new config options (only if we're on a Raspberry Pi)
    newconfig = init_config()  # reload config file
    update_crontab(newconfig)


def update_software():
    """
    check if there are any solthermpi-VERSION.tar.gz files on FTP server and
    install latest version.
    """
    serialno = get_serial_raspberry()
    ftp_dir = config.get("GENERAL", "ftp_configfiles_dir")
    ftp_dir = os.path.join(ftp_dir, serialno)
    allfiles = ftp_list_files(ftp_dir, regex="solthermpi-[0-9\.]+\.tar\.gz")
    allfiles.sort()
    if not allfiles:
        print("No package updates available for this device.")
        return
    latest_version = allfiles[-1]
    # download file
    file_remote = os.path.join(ftp_dir, latest_version)
    with TemporaryDirectory(suffix='', prefix='tmp') as tempdir:
        file_local = os.path.join(tempdir, latest_version)
        ftp_get_file(file_remote, file_local)
        print("Installing package {} ...".format(file_local))
        exitcode = 0
        try:
            exitcode = pip.main(["install", file_local])
        # apparently, pip explicitely exits if an error occurs during the
        # installation process (http://jelly.codes/articles/python-pip-module/)
        except SystemExit:
            pass
        else:
            if exitcode == 0:
                print("Solthermpi update successful")
                # delete tar.gz file on ftp server after successful
                # installation
                ftp_delete_file(file_remote)

                # send file with installed version number
                versionfilename = 'solthermpi_version.txt'
                versionfile = os.path.join(tempdir, versionfilename)
                with open(versionfile, 'w') as fo:
                    fo.write(latest_version)

                versionfile_remote = os.path.join(ftp_dir, versionfilename)
                ftp_put_file(versionfile, versionfile_remote)
                return
        print("Could not install package {}.".format(file_local))


def cleanup():
    """
    """
    # TODO delete files in archive folder
    raise NotImplementedError()


def rebootIf():
    """
    Check for file "rebootNeeded" on the ftp server and reboot if so.
    """
    serialno = get_serial_raspberry()
    ftp_dir = config.get("GENERAL", "ftp_configfiles_dir")
    ftp_dir = os.path.join(ftp_dir, serialno)
    allfiles = ftp_list_files(ftp_dir, regex="^rebootNeeded$")
    if not allfiles:
        print("No restart needed.")
        return
    print("Found rebootNeeded file. Will reboot now.")
    ftp_delete_file(ftp_dir+'/'+allfiles[0])
    reboot()


def reboot():
    """ reboot now """
    os.system("sudo /sbin/reboot")


def checkUsbResetRequested():
    """
    Check if USB needs to be resetted
    - Check for file "resetUSB" on the ftp server
    """
    serialno = get_serial_raspberry()
    ftp_dir = config.get("GENERAL", "ftp_configfiles_dir")
    ftp_dir = os.path.join(ftp_dir, serialno)

    # Is there a "resetUSB" file on the ftp server?
    allfiles = ftp_list_files(ftp_dir, regex="^resetUSB$")
    if allfiles:
        print("USB power cut requested thru resetUSB file.")
        ftp_delete_file(ftp_dir+'/'+allfiles[0])
        return True

    print("Found no resetUSB file.")
    return False


def hasDataForLastHour():
    """
    Check if we have logged data in the last hour
    """
    serialno = get_serial_raspberry()
    threshold = datetime.utcnow() - timedelta(hours=1)
    log_dir = config.get("GENERAL", "logdir")
    search_pattern = "{}/{}_*".format(log_dir, serialno)
    log_files = glob.glob(search_pattern)
    if not len(log_files):
        print("{} No log file found.". format(strftime('%c')))
        return False

    log_files.sort()
    logfile = log_files[-1]
    logpath = os.path.join(log_dir, logfile)
    f = open(logpath, 'r')
    lines = f.read().splitlines()
    last_line = lines[-1]
    datepart = last_line.partition(';')[0]
    try:
        datetime_object = datetime.strptime(datepart, '%Y-%m-%d %H:%M:%S')
    except ValueError:
        print("Last line does not begin with a date.")
        return False

    ret = datetime_object > threshold
    if ret:
        print("{} We have data from the last hour.".format(strftime('%c')))
    else:
        print("{} No data for the last hour.".format(strftime('%c')))

    return ret


def resetUsb():
    """
    Check if USB needs to be resetted and do so if yes.
    """
    if checkUsbResetRequested():
        doResetUsb()
    elif not hasDataForLastHour():
        doResetUsb()
    else:
        print("{} No USB reset needed.".format(strftime('%c')))


def doResetUsb():
    """
    Cut power for the USB device, wait 5 seconds and power it on again.
    """
    print("{} Will cut USB power now.".format(strftime('%c')))
    serialno = get_serial_raspberry()
    ftp_dir = config.get("GENERAL", "ftp_configfiles_dir")
    ftp_dir = os.path.join(ftp_dir, serialno)

    off_args = shlex.split("sudo tee /sys/bus/usb/drivers/usb/unbind")
    on_args = shlex.split("sudo tee /sys/bus/usb/drivers/usb/bind")
    usb_device = b'1-1'
    p1 = subprocess.Popen(off_args, stdin=subprocess.PIPE,
                          stdout=subprocess.PIPE)
    p2 = subprocess.Popen(on_args, stdin=subprocess.PIPE,
                          stdout=subprocess.PIPE)
    p1.communicate(usb_device)
    print("USB power off command returned " + p1.returncode.__str__())
    print("Sleep for five seconds")
    time.sleep(5)
    p2.communicate(usb_device)
    print("USB power on command returned " + p2.returncode.__str__())
    print("{} Done".format(strftime('%c')))


def send_cronlog():
    """
    Upload logs from cron jobs to ftp server. Delete uploaded log files.
    """
    serialno = get_serial_raspberry()
    ftp_dir = config.get("GENERAL", "ftp_cronlog_dir")

    # delete cron logs older than 3 days
    threshold = datetime.utcnow() - timedelta(days=3)

    cron_logpath = config.get("GENERAL", "cron_logpath")
    regex = "[0-9]{8}\.log"
    for f in os.listdir(cron_logpath):
        if not re.match(regex, f):
            continue

        file_remote = os.path.join(ftp_dir, "{}_{}".format(serialno, f))
        file_local = os.path.join(cron_logpath, f)
        ftp_put_file(file_local=file_local,
                     file_remote=file_remote)
        print("Uploaded {}.".format(f))

        t = datetime.strptime(f, "%Y%m%d.log")
        if t < threshold:
            os.remove(file_local)
            print("removed {}".format(file_local))


main()
