"""
Author: Cooper
Date: 10/04/2019

Desc: FTP client for grabbing relevant files.  At the moment this is very Abu Dhabi specific in the sense that it uses
the versioning created for them.  This versioning scheme can easily be changed to suit whatever though.

The FTP server that is used in Abu Dhabi does not support the latest set of FTP commands.

Note
----
Context-specific implementation. May need further work if things like connectivity failures are to be dealt
with cleanly by classes using this one.

"""
import sys
import os
import ftplib
from ftplib import FTP
import time
import hashlib
import re

class FakeFile:
    def read(self, size=0):
        return ''

class FTPClient(object):
    def __init__(self, config, conf_dir):
        self.tmpdir = r"/tmp"

        self.config = config
        self.conf_dir = conf_dir
        self.host = config.get("FTP_server", "192.168.0.1")
        self.user = config.get("FTP_user", "HANOVER")
        self.pw = config.get("FTP_pw", "HANOVER")
        self.directory = config.get("FTP_dir", "/toveh/DTHANOVER")
        self.regex_pattern = config.get("FTP_filename_regex", "^data_v-[0-9]*-[0-9]*-[0-9]*[.]zip|0E2")
        self.holdoff = config.get("FTP_holdoff", 60)

        self.current_version = "0-0-0"
        self.update_version = ""
        self.payload_file_name = ""
        self.update_available = False
        self.update_complete = None

        self.getDataVersion()
        print(self.regex_pattern)

    """
    ###################################################################################################################
    Version functions
    """

    def generatemd5(self, path):
        ericfile = open(path, "rb")

        md5hash = hashlib.md5(ericfile.read()).hexdigest()

        print("\tMD5: %s" % md5hash)
        return md5hash

    def getDataVersion(self):
        path = os.path.join(self.conf_dir, "data_version.txt")
        print(path)

        try:
            self.current_version = open(path, "r").read()
        except IOError:
            print("FTP: No version file!")
            self.current_version = "0-0-0"

        print("\t" + self.current_version)

    def saveDataVersion(self):
        path = os.path.join(self.conf_dir, "data_version.txt")

        print("FTP: Writing file version")
        try:
            outfile = open(path, "w").write(self.current_version)
            print("FTP version saved: %s" % self.current_version)
        except IOError:
            print("FTP: Version file write error.")

    def compareVersion(self, version):
        """
        Checks that update is possible but probably should only change the flag when the file has been retrieved.

        This returns true if the version is newer than existing
        """

        if version == None:
            return 0

        newVersion = version[7:-4].split("-")
        oldVersion = self.current_version.split("-")

        #if the file follows convention then
        for index in range(0, 3):
            try:
                oldval = int(oldVersion[index])
                newval = int(newVersion[index])
                # print(oldval, newval)

                if oldval > newval:
                    return 0
                elif oldval == newval:
                    continue
                else:
                    return 1

            except ValueError:
                return 0

        print("FTP Update available: ", self.update_available)
        return 0

    """
    ###################################################################################################################
    FTP Functions
    """

    def connectFTP(self):
        """
        Connects to an FTP server
        """
        print("FTP Connecting @ %s" % self.host)
        try:
            self.ftp = FTP(self.host)
        except OSError as e:
            print(e)
            return 0

        try:
            self.ftp.login(self.user, self.pw)
        except ftplib.all_errors as e:
            print(e)
            print("\t...Connection failure")
            return 0
        else:
            print("\t...Connected")
            return 1

    def checkForFiles(self, directory):
        #There should really only be one of each type of file in here but who knows what may happen
        tmp = []
        print("FTP Getting list of files")
        files = self.ftp.mlsd(directory)

        try:
            for file in files:
                # print(file[0])
                x = re.search(self.regex_pattern, file, re.IGNORECASE)
                if x:
                    tmp.append(file[0])
        except ftplib.all_errors as e:
            print(e)

        self.ftp.quit()

        if len(tmp) > 0:
            tmp.sort(reverse=True)
            return tmp[0]
        else:
            return None

    def checkForFilesLegacy(self, directory):
        """
        The FTP server on the INIT OBC is ancient and thus does not support mlsd calls, this is used instead
        """
        ftpdir = []
        tmp = []
        print("FTP Getting list of files")
        try:
            self.ftp.cwd(directory)
            self.ftp.dir(ftpdir.append)
            self.ftp.quit()
        except ftplib.all_errors as e:
            print(e)

        else:
            for line in ftpdir:
                filename = line.split(" ")[-1]
                x = re.search(self.regex_pattern, filename, re.IGNORECASE)
                if x:
                    tmp.append(filename)

            if len(tmp) > 0:
                tmp.sort(reverse=True)
                return tmp[0]
            else:
                return None


    def pullFile(self, file):
        """
        Pulls a given fil from the FTP server
        """
        if self.connectFTP() == 0:
            return 0

        try:
            self.ftp.cwd(self.directory)
            print("FTP Retrieving %s" % file)
        except ftplib.all_errors as e:
            print(e)
            return 0

        binfile = open(os.path.join(self.tmpdir, file), "wb")

        try:
            self.ftp.retrbinary("RETR %s" % file, binfile.write)
            binfile.close()
            print("\t...Got!")
        except ftplib.all_errors as e:
            print(e)
            return 0

        self.ftp.quit()

        return 1

    """
    ###################################################################################################################
    Main
    """

    def run(self):
        #Threadable call to monitor the FTP server
        #Check flags to remove the flag file and update the version
        oldTime = time.time()
        try:
            ftp_holdoff = int(self.config["FTP_holdoff"])
        except ValueError:
            print("FTP: Invalid timeout in config, defaulting to 5mins")
            ftp_holdoff = 300

        while 1:
            #print("FTP Status a: %s c:%s" % (self.update_available, self.update_complete))
            if self.update_available:
                #I suppose there isnt much point in checking for the FTP if theres an update available but should check here
                #If update has been complete

                if self.update_complete == 0:
                    print("FTP Update reported complete")
                    self.update_available = False
                    self.update_complete = None
                    self.current_version = self.payload_file_name[7:-4]
                    self.saveDataVersion()
                elif self.update_complete == 1:
                    print("FTP Update failed, resetting state")
                    self.update_available = False
                    self.update_complete = None
                else:
                    print("FTP Update waiting processing")

                time.sleep(1)
                continue

            else:
                if time.time() - oldTime > ftp_holdoff:
                    oldTime = time.time()
                    if self.connectFTP():
                        latestFile = self.checkForFilesLegacy(self.directory)
                        if latestFile == None:
                            print("FTP No files")
                            continue
                        else:
                            print("FTP Discovered: " + latestFile)
                            if self.compareVersion(latestFile):
                                if self.pullFile(latestFile):
                                    self.update_available = True
                                    self.payload_file_name = latestFile
                                    print("FTP: Update obtained")

            #print("FTP Waiting for next cycle.")
            time.sleep(1)

if __name__ == "__main__":
    import _thread

    config = {
        "FTP_server": "192.168.8.109",
        "FTP_user": "HANOVER",
        "FTP_pw": "HANOVER",
        "FTP_dir": "/toveh/DTHanover",
        "FTP_filename_regex": "^data_v-[0-9]*-[0-9]*-[0-9]*[.]0E2",
        "FTP_holdoff": 10
    }

    ftp = FTPClient(config, "/tmp")
    _thread.start_new_thread(ftp.run, ())

    while 1:
        if ftp.update_available:
            time.sleep(2)
            ftp.update_complete = 0
        else:
            time.sleep(2)