# Copyright 2019 bitconnect
# Author: Tobias Perschon
# License: GNU GPLv2, see LICENSE.txt
import glob
import os
import shutil
import re
import pygame
import time
from .usb_drive_mounter import USBDriveMounter


class USBDriveReaderCopy(object):

    def __init__(self, config, screen):
        """Create an instance of a file reader that uses the USB drive mounter
        service to keep track of attached USB drives and automatically mount
        them for reading videos.
        """
        self._config = config
        self._screen = screen
        self._load_config(config)
        self._pygame_init(config)
        self._mounter = USBDriveMounter(root=self._mount_path,
                                        readonly=self._readonly)
        self._mounter.start_monitor()

        if not os.path.exists(self._target_path):
            os.makedirs(self._target_path)
        #subprocess.call(['mkdir', self._target_path])

    def _pygame_init(self, config):
        self._bgcolor = (52,52,52)
        self._fgcolor = (149,193,26)
        self._bordercolor = (255,255,255)
        self._fontcolor = (255,255,255)
        self._font = pygame.font.Font(None, 40)

        #positions and sizes:
        self.screenwidth = pygame.display.Info().current_w
        self.screenheight = pygame.display.Info().current_h
        self.pwidth=0.8*self.screenwidth
        self.pheight=0.05*self.screenheight
        self.borderthickness = 2

        #create rects:
        self.borderrect   =  pygame.Rect((self.screenwidth / 2) - (self.pwidth / 2),
                                         (self.screenheight / 2) - (self.pheight / 2),
                                         self.pwidth,
                                         self.pheight)


    def _load_config(self, config):
        self._mount_path = config.get('usb_drive', 'mount_path')
        self._readonly = config.getboolean('usb_drive', 'readonly')
        self._target_path = config.get('directory', 'path')
        self._copy_mode = config.get('copymode', 'mode')
        self._copyloader = config.getboolean('copymode', 'copyloader')
        self._password = config.get('copymode', 'password')

        #needs to be changed to a more generic approach to support other players
        self._extensions = '|'.join(config.get(self._config.get('video_looper', 'video_player'), 'extensions') \
                                 .translate(str.maketrans('','', ' \t\r\n.')) \
                                 .split(','))

    def copy_files(self, paths):
        self.clear_screen()

        copy_mode = self._copy_mode
        copy_mode_info = "(from config)"
        for path in paths:
            if not os.path.exists(path) or not os.path.isdir(path):
                continue

            #check password
            if not self._password == "":
                if not self.check_file_exists('{0}/{1}'.format(path.rstrip('/'), self._password)):
                    continue

            #override copymode?
            if self.check_file_exists('{0}/{1}'.format(path.rstrip('/'), 'replace')):
                copy_mode = "replace"
                copy_mode_info = "(overridden)"
            if self.check_file_exists('{0}/{1}'.format(path.rstrip('/'), 'add')):
                copy_mode = "add"
                copy_mode_info = "(overridden)"
            if self.check_file_exists('{0}/{1}'.format(path.rstrip('/'), 'replace')) \
                    and self.check_file_exists('{0}/{1}'.format(path.rstrip('/'), 'add')):
                copy_mode = self._copy_mode
                copy_mode_info = "(from config)"

            #inform about copymode
            self.draw_info_text("Mode: " + copy_mode + " " + copy_mode_info)

            if copy_mode == "replace":
                # iterate over target path for deleting:
                for x in os.listdir(self._target_path):
                    if x[0] is not '.' and re.search('\.{0}$'.format(self._extensions), x, flags=re.IGNORECASE):
                        os.remove('{0}/{1}'.format(self._target_path.rstrip('/'), x))

            # iterate over source path for copying:
            for x in os.listdir(path):
                if x[0] is not '.' and re.search('\.{0}$'.format(self._extensions), x, flags=re.IGNORECASE):
                    #copy file
                    self.copy_with_progress('{0}/{1}'.format(path.rstrip('/'), x), '{0}/{1}'.format(self._target_path.rstrip('/'), x))

            #copy loader image
            if self._copyloader:
                loader_file_path = '{0}/{1}'.format(path.rstrip('/'), 'loader.png')
                if os.path.exists(loader_file_path):
                    self.clear_screen()
                    self.draw_info_text("Copying splashscreen file...")
                    time.sleep(2)
                    self.copy_with_progress(loader_file_path,'/home/pi/loader.png')
                    
    def draw_copy_progress(self, copied, total):
        perc = 100 * copied / total
        assert (isinstance(perc, float))
        assert (0. <= perc <= 100.)

        progressrect =  pygame.Rect((self.screenwidth / 2) - (self.pwidth / 2) + self.borderthickness,
                                                                (self.screenheight / 2) - (self.pheight / 2) + self.borderthickness,
                                                                (self.pwidth-(2*self.borderthickness))*(perc/100),
                                                                self.pheight - (2*self.borderthickness))


        #border
        pygame.draw.rect(self._screen, self._bordercolor, self.borderrect, self.borderthickness)
        #progress
        pygame.draw.rect(self._screen, self._fgcolor, progressrect)
        #progress_text
        self.draw_progress_text(str(int(round(perc)))+"%")

        pygame.display.update(self.borderrect)

    def draw_info_text(self, message):
        label1 = self._font.render(message, True, self._fontcolor, self._bgcolor)
        l1w, l1h = label1.get_size()
        self._screen.blit(label1, (self.screenwidth / 2 - l1w / 2, self.screenheight / 2 - l1h - self.pheight/2 - 3*self.borderthickness))
        pygame.display.update()

    def draw_progress_text(self, progress):
        label1 = self._font.render(progress, True, self._bgcolor, self._fgcolor)
        l1w, l1h = label1.get_size()
        self._screen.blit(label1, (self.screenwidth / 2 - l1w / 2, self.screenheight / 2 - l1h / 2 + self.borderthickness))

    def clear_screen(self, full=True):
        if full:
            self._screen.fill(self._bgcolor)
            pygame.display.update()
        else:
            self._screen.fill(self._bgcolor,self.borderrect)
            pygame.display.update(self.borderrect)

    #checks for file without and with any extension
    def check_file_exists(self,file):
        return (glob.glob(file + ".*") + glob.glob(file)) != []

    def copyfile(self, src, dst, *, follow_symlinks=True):
        """Copy data from src to dst.

        If follow_symlinks is not set and src is a symbolic link, a new
        symlink will be created instead of copying the file it points to.

        """
        if shutil._samefile(src, dst):
            raise shutil.SameFileError("{!r} and {!r} are the same file".format(src, dst))

        for fn in [src, dst]:
            try:
                st = os.stat(fn)
            except OSError:
                # File most likely does not exist
                pass
            else:
                # XXX What about other special files? (sockets, devices...)
                if shutil.stat.S_ISFIFO(st.st_mode):
                    raise shutil.SpecialFileError("`%s` is a named pipe" % fn)

        if not follow_symlinks and os.path.islink(src):
            os.symlink(os.readlink(src), dst)
        else:
            size = os.stat(src).st_size
            with open(src, 'rb') as fsrc:
                with open(dst, 'wb') as fdst:
                    self.copyfileobj(fsrc, fdst, callback=self.draw_copy_progress, total=size)
        return dst

    def copyfileobj(self, fsrc, fdst, callback, total, length=16 * 1024):
        copied = 0
        while True:
            buf = fsrc.read(length)
            if not buf:
                break
            fdst.write(buf)
            copied += len(buf)
            callback(copied, total=total)

    def copy_with_progress(self, src, dst, *, follow_symlinks=True):
        if os.path.isdir(dst):
            dst = os.path.join(dst, os.path.basename(src))

        # clear screen before copying
        self.clear_screen(False)

        self.copyfile(src, dst, follow_symlinks=follow_symlinks)
        # shutil.copymode(src, dst)
        return dst

    def search_paths(self):
        """Return a list of paths to search for files. Will return a list of all
        mounted USB drives.
        """
        if(self._mounter.has_nodes()):
            self._mounter.mount_all()
            self.copy_files(glob.glob(self._mount_path + '*'))

        return [self._target_path]

    def is_changed(self):
        """Return true if the file search paths have changed, like when a new
        USB drive is inserted.
        """
        if self._mounter.poll_changes() and self._mounter.has_nodes():
            return True
        else:
            return False

    def idle_message(self):
        """Return a message to display when idle and no files are found."""
        return 'Insert USB drive with compatible movies. Copy Mode: files will be copied to RPi.'


def create_file_reader(config, screen):
    """Create new file reader based on mounting USB drives."""
    return USBDriveReaderCopy(config, screen)