import pycurl
import os
from io import BytesIO
import subprocess
from ftplib import FTP
import signal

class Cam(object):
    """ Cam object

    Attributes
    ----------
    log : logging_object
    name : str
        name of the cam, set by the section in the config file
    ip : str
        IP of the cam in string format
    location : str
        Location of the picutres on the SD-Card
    ftp_config : config_object
        FTP configuration; such as URI, username and password


    Methods
    -------
    getFileList()
        Get the  file list of the cam via curl
    organiseMissing(files)
        Get a list of files and decide which to download
    getImage(filename)
        Get Images from cam via curl
    uploadImage(filename)
        Upload the image to remote FTP server
    isOnline()
        Check if the cam is online via ping

    """
    def __init__(self, log, name, ip, location, ftp_config):
        self.log = log
        self.name = name
        self.ip = ip
        self.getFileListPath = 'http://' + self.ip + '/command.cgi?op=100&DIR=' + location
        self.getFilePath = 'http://' + self.ip + '/' + location
        self.ftp_config = ftp_config

        #TODO: Test what happens if file is not present
        fname = './data/' + self.name + '/last.txt'
        self.last = str(0)
        try:
            with open(fname) as last_file:
                for line in last_file:
                    self.last = line.strip().split()[0]
        except FileNotFoundError:
            self.log.debug(self.name + " last.txt was not found, default value 0 is set")

        self.log.debug(self.name + " last index is " + self.last)
 
 
    def getFileList(self):
        """Get the file list of the cam via curl

        Return: list of filenames
        """

        # Assert that the cam is online, otherwise the whole function doesn't make sense
        assert self.isOnline()

        self.log.info(self.name + " get list of files")

        # Open, run and close the curl session
        crl = pycurl.Curl()
        b_obj = BytesIO()
        crl.setopt(pycurl.CONNECTTIMEOUT, 5)
        crl.setopt(pycurl.TIMEOUT, 5)
        crl.setopt(crl.URL, self.getFileListPath)
        crl.setopt(crl.WRITEDATA, b_obj)
        crl.perform()
        crl.close()
 
        # Get the file list
        get_list = b_obj.getvalue().decode('utf8').splitlines()
        del get_list[0]
 
        # Create empty list for processing later
        files = []
 
        # Get the filenames only
        for line in get_list:
            files.append(line.split(',')[1])

        self.log.debug(self.name + " " + " ".join(files))
 
        # Return the file list
        return files

    def organiseMissing(self, files):
        """Get a list of files and decide which to download
        
        Parameters
        ----------
        files: list
            List of files 
        """
        # Go trough the filenames, assuming they are sorted
        #TODO: If possible try to make it work without the assumption
        # Do this with another index (current highest) and update this index only with a bigger number
        # In the last step, update self.last with current highest index (and obviously update the value in the file
        for filename in files:
            file_index = int(''.join(list(filter(str.isdigit, filename))))

            # Check if the file needs to be downloaded
            if file_index > int(self.last):
                self.log.info("Found a file: "+ filename)
                # Download the file
                self.getImage(filename)
                self.log.info("Image " + filename + " downloaded")

                try:
                    # Upload images to corresponding FTP server
                    self.uploadImage(filename)
                    self.log.info("File " + filename + " uploaded to remote server")
                    # Remove the local file
                    os.remove('./data/' + self.name + '/pictures/' + filename)
                    self.log.info("File " + filename + " removed from local storage")
                    # Update the last index
                    self.last = file_index
                except ConnectionRefusedError:
                    self.log.error("Could not reach FTP server, exit now")
                    return 1



        # Update the last file for each cam only once
        last_file = open('./data/' + self.name + '/last.txt', "w+")
        last_file.write(str(self.last))
        last_file.close()

        return 0

 
    def getImage(self, filename):
        """Get Images from cam via curl

        Parameters
        ----------
        filename : str
            Filnema of the image to download
        """

        assert self.isOnline()

        # Initialize the curl object, set timeout to 5 seconds, otherwise curl might hang if 
        # the cam is suddenly not reachable anymore
        crl = pycurl.Curl()
        crl.setopt(pycurl.CONNECTTIMEOUT, 5)
        crl.setopt(pycurl.TIMEOUT, 5)
        self.log.info("Download Picture from " + self.getFilePath + '/' + filename)

        #TODO: Set data path in default config section
        with open('./data/'+ self.name +'/pictures/' + filename, 'wb') as f:
            signal.signal(signal.SIGALRM, self.handler)
            signal.alarm(5)
            crl.setopt(crl.URL, self.getFilePath + '/' + filename)
            crl.setopt(crl.WRITEDATA, f)
            crl.perform()
            crl.close()
            signal.alarm(0)


    def uploadImage(self, filename):
        """Upload the image to remote FTP server

        Parameters
        ----------
        filename : str
            Filename for the file which should be uploaded

        """
        # Only if the config is set to new use the additional prefix, otherwise none
        if self.ftp_config['db'] == "new":
            prefix = "_neue_db"
        else:
            prefix = ""

        # Initialize FTP settings
        ftp_path = self.ftp_config['ftp_path'] + '/' +  self.name +  prefix
        #TODO: Move to config file
        ftp = FTP("xray876.server4you.net")
        ftp.login(self.ftp_config['ftp_user'], self.ftp_config['ftp_pwd'])
        ftp.cwd(ftp_path)

        with open('data/' + self.name + '/pictures/' + filename, 'rb') as f:
            ftp.storbinary('STOR %s' % os.path.basename(filename), f)
        
        ftp.quit()


    def isOnline(self):
        """Check if the Cam is online via ping
        
        Returns
        -------
        0
            cam is online
        1
            cam is offline
        """
        command = ['ping', '-c', '1', '-w', '1', '-q', self.ip]

        return subprocess.run(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0

    def handler(self, signum, frame):
        self.log.debug("Signal handler called with signal" + signum)
        raise OSError("Could not reach camera")