from Qt import QtCore, QtWidgets
import logging
import os


log = logging.getLogger("wizart_desktop")


class LogView(QtWidgets.QWidget):

    received_msg = QtCore.Signal(str)
    log_msg_update = QtCore.Signal(str, int)

    def __init__(self, parent=None):
        super(LogView, self).__init__(parent)

        #self.log_file = QtCore.QFile()
        #self.log_stream = QtCore.QTextStream()

        self.view = QtWidgets.QPlainTextEdit(self)
        self.view.setLineWrapMode(self.view.WidgetWidth)
        self.view.setReadOnly(True)
        self.view.setMaximumBlockCount(1000000)

        layout = QtWidgets.QVBoxLayout(self)
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.view)
        self.setLayout(layout)

        self.received_msg.connect(self.__received_msg)

    def scroll_to_bottom(self):
        QtCore.QTimer.singleShot(0, self.__scroll_to_bottom)

    def __scroll_to_bottom(self):
        bar = self.view.verticalScrollBar()
        bar.setValue(bar.maximum())

    def parse_progress(self, line):
        line = unicode(line)
        if line:
            comp = line.split("|", 1)
            if len(comp) == 2:
                msg = comp[1]
                progress = -1
                if "PROGRESS" in msg:
                    lines = msg.split(';;')
                    import re
                    progress = re.findall(r'(\d+)%,', lines[0])
                    if progress:
                        progress = int(progress[0])
                    msg = "\n".join(lines)

                self.log_msg_update.emit(msg, progress)

    def write(self, msg):
        self.received_msg.emit(msg)

    def __received_msg(self, msg):
        cursor = self.view.textCursor()
        cursor.movePosition(cursor.End)
        self.parse_progress(msg)
        cursor.insertText(msg)
        self.scroll_to_bottom()


class LogWatcher(QtCore.QObject):
    '''
    LogWatcher reads changes in the log file.
    LogWatcher remembers the time ['mtime'] of the last file modification
    and checks the current value once per second.
    If the file has been modified since the last read,
    then LogWatcher reads the added lines.
    Each time it reads, LogWatcher remembers the position ['pos'] of the End-Of-File,
    so that next time it can start reading from this position.
    '''
    # The timer cannot be started from another process
    # Signal must be used
    file_added = QtCore.Signal()

    def __init__(self, parent=None):
        super(LogWatcher, self).__init__(parent)
        self.__files = {}
        self.__timer = QtCore.QTimer(self)
        self.__timer.setInterval(1000)
        self.__timer.timeout.connect(self.check_files)
        self.file_added.connect(self.start)

    def add_file(self, prefix, path):
        if not prefix in self.__files:
            self.__files[prefix] = TempLogFile(path)
        self.file_added.emit()

    def remove_file(self, prefix):
        del self.__files[prefix]

    def start(self):
        if not self.__timer.isActive():
            self.__timer.start()

    def stop(self):
        self.__timer.stop()

    def check_files(self):
        if not self.__files:
            self.stop()
            return
        for prefix, temp_file in self.__files.items():
            if temp_file.is_exists():
                if temp_file.is_modified():
                    self.log_new_lines(prefix, temp_file.get_new_lines())
            else:
                self.remove_file(prefix)

    def log_new_lines(self, prefix, lines):
        for line in lines:
            line = line.rstrip()
            if line:
                log.info(prefix + line)

class TempLogFile(object):
    """
    Object for working with temporary log files
    """
    def __init__(self, path):
        self.path = path
        self.last_position = 0
        self.file_size = 0

    def is_exists(self):
        return os.path.exists(self.path)

    def is_modified(self):
        file_size = os.path.getsize(self.path)
        if file_size != self.file_size:
            self.file_size = file_size
            return True
        return False

    def get_new_lines(self):
        fd = os.open(self.path, os.O_RDONLY)
        with os.fdopen(fd, 'r') as temp_file:
            temp_file.seek(self.last_position)
            lines = temp_file.readlines()
            self.last_position = temp_file.tell()
        return lines