class DiffCodeAnalyzer(QObject):
    codeChanged = pyqtSignal(str)
    removedLines = pyqtSignal(QVariant, arguments=["lines"])
    addedLines = pyqtSignal(QVariant, arguments=["lines"])

    def __init__(self, parent: Optional[QObject] = None):
        super().__init__(parent)

        self._code = "\n"
        self._temp_code = "\n"

    @pyqtSlot(str)
    def saveTempCode(self, code: str) -> None:
        self._temp_code = code

    @pyqtProperty(str, notify=codeChanged)
    def code(self) -> str:
        return self._code

    @code.setter
    def code(self, v: str) -> None:
        if self._code != v:
            self._code = v
            self.codeChanged.emit(v)

    def _get_lines_diffs(self, new_code: str) -> Tuple[List[int], List[int]]:
        added_lines = []
        removed_lines = []
        hunk = []

        temp_code_diff = list(unified_diff(self._temp_code.splitlines(True), new_code.splitlines(True)))[2::]

        for line in temp_code_diff:
            hunk_info_match = re.match(r'@@ -\d+,?\d* \+\d+,?\d* @@', line)
            if hunk_info_match is not None:
                hunk_added_lines, hunk_removed_lines = self._get_line_hunk_diffs(hunk)
                added_lines.extend(hunk_added_lines)
                removed_lines.extend(hunk_removed_lines)

                hunk = []
            hunk.append(line)

        if hunk:
            hunk_added_lines, hunk_removed_lines = self._get_line_hunk_diffs(hunk)
            added_lines.extend(hunk_added_lines)
            removed_lines.extend(hunk_removed_lines)

        return added_lines, removed_lines

    @pyqtSlot(str, result=QVariant)
    def compare(self, new_code: str) -> QVariant:
        diff_lines = []
        hunk = []

        diff = list(unified_diff(self._code.splitlines(True), new_code.splitlines(True)))[2::]
        temp_code_diff = list(unified_diff(self._temp_code.splitlines(True), new_code.splitlines(True)))[2::]

        for line in diff:
            hunk_info_match = re.match(r'@@ -\d+,?\d* \+\d+,?\d* @@', line)
            if hunk_info_match is not None:
                diff_lines.extend(self._get_modified_lines(hunk))
                hunk = []
            hunk.append(line)

        if hunk:
            diff_lines.extend(self._get_modified_lines(hunk))

        added_lines, removed_lines = self._get_lines_diffs(new_code)

        if added_lines:
            self.addedLines.emit(QVariant(added_lines))
        if removed_lines:
            self.removedLines.emit(QVariant(removed_lines))

        return QVariant(diff_lines)

    def _get_modified_lines(self, hunk: List[str]) -> List[int]:
        if not hunk:
            return []
        hunk_info_match = re.match(r'@@ -(?P<line_start>\d+),?\d* \+\d+,?\d* @@', hunk[0])
        line_start = int(hunk_info_match.group('line_start'))
        raw_hunk = hunk[1::]
        # remove removed lines
        raw_hunk = filter(lambda x: x[0] is not '-', raw_hunk)
        diff_lines = [i + line_start for i, v in enumerate(raw_hunk) if v[0] is "+"]
        return diff_lines

    def _get_line_hunk_diffs(self, hunk: List[str]) -> Tuple[List[int], List[int]]:
        if not hunk:
            return [], []

        hunk_info_match = re.match(r'@@ -(?P<line_start>\d+),?\d* \+\d+,?\d* @@', hunk[0])
        line_start = int(hunk_info_match.group('line_start'))
        raw_hunk = hunk[1::]    # remove header

        line_mod_marks = ''.join([line[0] for line in raw_hunk])

        regexp = re.compile(r'-\|*\+')
        match = regexp.search(line_mod_marks)

        # keep only added or removed lines
        while match is not None:
            span_start, span_end = match.span()
            mods_count = match.group().count('|')
            line_mod_marks = ''.join(
                [line_mod_marks[0:span_start:], "|" * (mods_count + 1), line_mod_marks[span_end::]])
            match = regexp.search(line_mod_marks)

        removed_lines = [i + line_start for i, v in enumerate(line_mod_marks) if v is "-"]
        added_lines = [i + line_start for i, v in enumerate(line_mod_marks) if v is "+"]

        return added_lines, removed_lines class ExpSyntaxHighlighter(QObject):
    """
    Class which wraps SyntaxHighliter and expose only target
    """

    searchMatchedLinesChanged = pyqtSignal(QVariant, arguments=["lines"])

    def __init__(self, parent: Optional[QObject] = None):
        super().__init__(parent)

        self._syntax_highlighter = SyntaxHighlighter(self)
        self._base_font = None
        self._search_matched_lines = []

    def _setupFormat(self, color: QColor, fontSettings: QFont, colorIsForeground: bool = True) -> QTextCharFormat:
        pattern_format = QTextCharFormat()
        if color and colorIsForeground:
            pattern_format.setForeground(color)
        if color and (not colorIsForeground):
            pattern_format.setBackground(color)
        pattern_format.setFontItalic(fontSettings.italic())
        pattern_format.setFontWeight(fontSettings.bold())

        return pattern_format

    @pyqtSlot(list, QJSValue, QFont)
    def addHighlightMultiColorRule(self, patterns: List[str], color: QJSValue,
                                   fontSettings: QFont) -> None:
        """
        Adds highlight rule to syntax highlighter
        :param patterns: Regexp pattners to be matched
        :param color: Foreground color of matched text
        :param fontSettings: Determinates font weight and italic
        """

        pattern_format = list()
        self._base_font = fontSettings

        for single_color in color.toVariant():
            pattern_format.append(self._setupFormat(QColor(single_color), fontSettings))

        for single_pattern in patterns:
            self._syntax_highlighter.addHighlightRule(
                HighlightRule(pattern_format, QRegularExpression(single_pattern))
            )

    @pyqtSlot(list, QColor, QFont)
    def addHighlightSingleColorRule(self, patterns: List[str], color: QColor, fontSettings: QFont) -> None:
        """
        Adds highlight rule to syntax highlighter
        :param patterns: Regexp pattners to be matched
        :param color: Foreground color of matched text
        :param fontSettings: Determinates font weight and italic
        """

        pattern_format = self._setupFormat(color, fontSettings)
        self._base_font = fontSettings

        for single_pattern in patterns:
            self._syntax_highlighter.addHighlightRule(
                HighlightRule(pattern_format, QRegularExpression(single_pattern))
            )

    @pyqtSlot(str)
    def setSearchPattern(self, pattern: str) -> None:
        pattern_format = self._setupFormat(QColor(SEARCH_FORMAT), self._base_font, False)
        if pattern:
            self._syntax_highlighter.setSearchRule(
                HighlightRule(pattern_format, QRegularExpression(re.escape(pattern), QRegularExpression.CaseInsensitiveOption))
            )
        else:
            self._syntax_highlighter.setSearchRule(None)
        self._syntax_highlighter.rehighlight()

        self._search_matched_lines = self._syntax_highlighter.searchMatchedLines()
        self.searchMatchedLinesChanged.emit(QVariant(self._search_matched_lines))

    @pyqtProperty(QVariant, notify=searchMatchedLinesChanged)
    def searchMatchedLines(self):
        return QVariant(self._search_matched_lines)

    @pyqtProperty(QQuickItem)
    def target(self) -> None:
        return self._syntax_highlighter.target

    @target.setter
    def target(self, v: QQuickItem) -> None:
        self._syntax_highlighter.target = v class ExpAnalyzer(QObject):
    targetChanged = pyqtSignal(QQuickItem)

    def __init__(self, parent: Optional[QObject] = None) -> None:
        super().__init__(parent)

        self._target = None

    def _get_content(self):
        return self._target.property("text")

    def _get_cursor(self):
        return self._target.property("cursorPosition")

    def _get_selection(self):
        return (self._target.property("selectionStart"),
                self._target.property("selectionEnd"),
                self._target.property("selectedText"))

    @pyqtSlot(result=QVariant)
    def currentWordBorders(self) -> QVariant:
        return QVariant(self._currentWordBorders())

    def _currentWordBorders(self) -> Dict[str, int]:
        """
        :return: Current word start and end
        """

        content = self._get_content()
        cursor = self._get_cursor()
        word_start = 0
        word_end = len(content)

        if self._get_cursor() == 0:
            return {"start": -1, "end": -1}

        splitter_positions = list()
        for splitter in EXPRESSION_SPLITTERS:
            splitter_positions.extend([i for i, letter in enumerate(content) if letter == splitter])

        left_splitters_pos = list(filter(lambda x: x < cursor, splitter_positions))
        right_splitters_pos = list(filter(lambda x: x >= cursor, splitter_positions))

        if left_splitters_pos:
            word_start = min(left_splitters_pos, key=lambda x: abs(x - cursor)) + 1
        if right_splitters_pos:
            word_end = min(right_splitters_pos, key=lambda x: abs(x - cursor))

        word = content[word_start:word_end:].strip()

        if word:
            if self._get_cursor() != word_end:
                brackets_behind_cursor = re.search(
                    '\)*',
                    self._get_content()[self._get_cursor()::]
                ).group()

                word_end -= len(brackets_behind_cursor)

        return {"start": word_start, "end": word_end}

    @pyqtSlot(result=str)
    def currentWord(self) -> str:
        """
        According to cursor in text it determines current word which is edited
        :return: Current word
        """
        content = self._get_content()
        borders = self._currentWordBorders()

        if borders["start"] != -1 and borders["end"] != -1:
            word = content[borders["start"]:borders["end"]:].strip()

            return re.escape(word)
        return ""

    @pyqtProperty(QQuickItem)
    def target(self) -> QQuickItem:
        return self._target

    @target.setter
    def target(self, v: QQuickItem) -> None:
        if self._target != v:
            self._target = v
            self.targetChanged.emit(v) class CodeAnalyzer(QObject):
    _instance = None
    completerModelChanged = pyqtSignal()

    VARIABLE_RE = re.compile(r'[GLT]F@[A-Za-z0-9_\-\$&%\*]+', re.IGNORECASE)

    def __init__(self, parent: Optional[QObject] = None):
        super().__init__(parent)

        self._code = ''

    @pyqtProperty(str)
    def code(self) -> str:
        return self._code

    @code.setter
    def code(self, v: str) -> None:
        self._code = v
        self.completerModelChanged.emit()

    @pyqtProperty(QVariant, notify=completerModelChanged)
    def completerModel(self) -> QVariant:
        return QVariant(
            [
                dict(
                    identifier='.IFJcode17',
                    type=Expression.ExpressionTypes.Header
                )
            ] if '.IFJcode17' not in self.code else [] + [
                dict(
                    identifier=match,
                    type=Expression.ExpressionTypes.Types
                ) for match in sorted(("int", "bool", "float", "string"))
            ] + [
                dict(
                    identifier=func,
                    type=Expression.ExpressionTypes.Instruction
                ) for func in INSTRUCTIONS
            ] + [
                dict(
                    identifier=match,
                    type=Expression.ExpressionTypes.Variable
                ) for match in sorted(set(self.VARIABLE_RE.findall(self._code)))
            ]
        )

    @pyqtProperty(QVariant)
    def highlightRules(self) -> QVariant:
        return QVariant([dict(pattern=pattern, color=color)
                         for (pattern, color) in HIGHLIGHT_RULES]
                        )

    @pyqtProperty(QVariant)
    def expressionSplitters(self) -> QVariant:
        return QVariant(list(EXPRESSION_SPLITTERS))

    @staticmethod
    def singletonProvider(engine: QQmlEngine, script_engine: QJSEngine) -> QObject:
        if CodeAnalyzer._instance is None:
            CodeAnalyzer._instance = CodeAnalyzer()
        return CodeAnalyzer._instance class Core(QObject):
    @pyqtSlot(float, result=float)
    def scaledSize(self, ref_size: float) -> float:
        if ref_size == 0.0:
            return 0.0
        dpi = QGuiApplication.primaryScreen().logicalDotsPerInch();
        ref_dpi = 96

        return ref_size * (dpi / ref_dpi) class FileIO(QObject):
    sourceChanged = pyqtSignal(str, arguments=["source"])

    def __init__(self, parent: Optional[QObject]):
        super().__init__(parent)

        self._source = ""

    @staticmethod
    def removeFilePrefix(path: str) -> str:
        return path.replace(FILE_PREFIX[system()], "")

    @pyqtProperty(str, notify=sourceChanged)
    def source(self) -> str:
        return self._source

    @source.setter
    def source(self, v: str) -> None:
        if self._source != v:
            self._source = FileIO.removeFilePrefix(v)
            self.sourceChanged.emit(v)

    @pyqtSlot(str)
    def write(self, content: str) -> None:
        if not self._source:
            return

        with open(self._source, "wb") as f:
            f.write(bytes(content, encoding='utf-8'))

    @pyqtSlot(result=str)
    def read(self) -> str:
        if not self._source:
            return ""

        with open(self._source, "rb") as f:
            read_data = f.read()
        return str(read_data, encoding='utf-8') FILE_PREFIX = defaultdict(lambda: "file://", **{
    "Windows": "file:///",
    "Linux": "file://",
    "Darwin": "file://"
}) class FormattedTextWriter(QObject):
    targetChanged = pyqtSignal(QQuickItem)

    def __init__(self, parent: Optional[QObject]):
        super().__init__(parent)

        self._target = None
        self._document = None
        self._cursor = None
        self.targetChanged.connect(self._setupNewDocument)

    def _setupFormat(self, color: QColor) -> QTextCharFormat:
        pattern_format = QTextCharFormat()
        if color is not None:
            pattern_format.setForeground(color)
        pattern_format.setFontItalic(self._target.property("font").italic())
        pattern_format.setFontWeight(self._target.property("font").bold())

        return pattern_format

    @pyqtSlot()
    def clear(self) -> None:
        self._cursor.select(QTextCursor.Document)
        self._cursor.removeSelectedText()

    @pyqtSlot(str, QColor)
    def write(self, text: str, color: QColor) -> None:
        self._cursor.movePosition(QTextCursor.End)
        self._cursor.insertText(text, self._setupFormat(color))

    @pyqtSlot(QQuickItem)
    def _setupNewDocument(self, target: QQuickItem) -> None:
        """
        Extract set new document from target
        :param target: Item, from which document which will extracted
        """

        self._document = target.property("textDocument").textDocument()
        self._cursor = QTextCursor(self._target.property("textDocument").textDocument())

    @pyqtProperty(QQuickItem)
    def target(self) -> QQuickItem:
        return self._target

    @target.setter
    def target(self, v: QQuickItem) -> None:
        if self._target != v:
            self._target = v
            self.targetChanged.emit(v) class TreeViewModel(QStandardItemModel):
    def __init__(self, parent = None):
        super().__init__(parent)

    def _create_item(self, parent_model: Union[QStandardItem, QStandardItemModel], name: str, data: Dict) -> QStandardItem:
        item = QStandardItem(name)
        for user_role, row_data in data.items():
            item.setData(row_data, user_role)
        parent_model.appendRow(item)
        return item

    def get_item(self, path: List[str], item_name: str):
        parents = [None]

        for i, name in enumerate(path + [item_name]):
            parent_model = self if i == 0 else parents[-1]
            items = self.findItems(name, Qt.MatchExactly | Qt.MatchRecursive)
            target_item = None

            if not items:   # create new item if it does not exist
                target_item = self._create_item(parent_model, name, {Qt.UserRole: "", Qt.UserRole+1: ""})
            else:
                for item in items: # check found items with corresponding parent
                    if item.parent() == parents[-1]:
                        target_item = item
                if target_item is None:
                    target_item = self._create_item(parent_model, name, {Qt.UserRole: "", Qt.UserRole + 1: ""})

            parents.append(target_item)
        return parents[-1]

    def clear_sub_tree(self, path: List[str], item_name: str) -> None:
        index = self.get_item(path, item_name).index()
        self.removeRows(0, self.rowCount(index), index)

    def remove_sub_tree(self, path: List[str], item_name: str) -> None:
        index = self.get_item(path, item_name).index()
        parent_index = index.parent()

        self.removeRows(index.row(), 1, parent_index)


    def roleNames(self) -> Dict:
        return {
            Qt.DisplayRole: "name_col".encode(),
            Qt.UserRole: "value_col".encode(),
            Qt.UserRole + 1: "type_col".encode()
        }

    def set_item_data(self, path, name, value, value_type) -> None:
        item = self.get_item(path, name)
        item.setData(value, Qt.UserRole)
        item.setData(value_type, Qt.UserRole + 1) class DebuggerWrapper(QObject):
    breakpointsChanged = pyqtSignal(QVariant)
    currentLineChanged = pyqtSignal(int, arguments=["line"])
    programEnded = pyqtSignal()
    programEndedWithError = pyqtSignal(str, arguments=["msg"])
    callStackModelChanged = pyqtSignal(QVariant, arguments=["model"])

    def __init__(self, parent: Optional[QObject] = None):
        super().__init__(parent)

        self._debugger = Debugger()
        self._model = None
        self._io_wrapper = None
        self._program_line = -1
        self._call_stack_model = []

        self._init_debug_worker()

    def _init_debug_worker(self):
        self._debugger_worker = DebuggerWorker(self._debugger, self)
        self._debugger_worker.programEnded.connect(self.programEnded)
        self._debugger_worker.programEndedWithError.connect(self.programEndedWithError)
        self._debugger_worker.currentLineChanged.connect(self._update_program_line)
        self._debugger_worker.stateChanged.connect(self._set_model_data)

    @pyqtProperty(QVariant, notify=callStackModelChanged)
    def callStackModel(self) -> QVariant:
        return QVariant(self._call_stack_model)

    @pyqtProperty(IOWrapper)
    def ioWrapper(self) -> IOWrapper:
        return self._io_wrapper

    @ioWrapper.setter
    def ioWrapper(self, v: IOWrapper) -> None:
        self._io_wrapper = v
        if self._io_wrapper:
            self._debugger._state_kwargs = dict(
                stdout=self._io_wrapper,
                stdin=self._io_wrapper,
                stderr=self._io_wrapper
            )

    @pyqtProperty(TreeViewModel)
    def model(self) -> Union[TreeViewModel, None]:
        return self._model

    @model.setter
    def model(self, v: TreeViewModel) -> None:
        self._model = v

    @pyqtSlot(QJSValue)
    def handleRemovedLines(self, lines: QJSValue) -> None:
        lines = lines.toVariant()
        breakpoints = self._debugger.breakpoints

        for removed_line in lines:
            if removed_line in breakpoints:
                breakpoints.remove(removed_line)
            breakpoints = set(x if x < removed_line else int(x) - 1 for x in breakpoints)

        self._debugger.breakpoints = breakpoints
        self.breakpointsChanged.emit(self.breakpoints)

    @pyqtSlot(int)
    def _update_program_line(self, v: int) -> None:
        self._program_line = v
        self.currentLineChanged.emit(v)

    @pyqtSlot(QJSValue)
    def handleAddedLines(self, lines: QJSValue) -> None:
        lines = lines.toVariant()
        breakpoints = self._debugger.breakpoints

        for added_line in lines:
            breakpoints = set(x if x < added_line else int(x) + 1 for x in breakpoints)
        self._debugger.breakpoints = breakpoints
        self.breakpointsChanged.emit(self.breakpoints)

    @pyqtSlot(int)
    def toggleBreakpoint(self, line: int) -> None:
        breakpoints = self._debugger.breakpoints

        if line in breakpoints:
            self._debugger.remove_breakpoint(line)
        else:
            self._debugger.add_breakpoint(line)
        self.breakpointsChanged.emit(self.breakpoints)

    @pyqtSlot(str)
    def debug(self, code: str) -> None:
        self._debugger_worker.command_type = DebuggerWorker.CommandType.DEBUG_RUN
        self._debugger_worker.code = code
        self._debugger_worker.start()

    @pyqtSlot(str)
    def run(self, code: str) -> None:
        self._debugger_worker.command_type = DebuggerWorker.CommandType.RUN
        self._debugger_worker.code = code
        self._debugger_worker.start()

    @pyqtProperty(QVariant, notify=breakpointsChanged)
    def breakpoints(self) -> QVariant:
        return QVariant(list(self._debugger.breakpoints))

    @pyqtProperty(int, notify=currentLineChanged)
    def currentLine(self) -> int:
        return self._program_line

    @pyqtSlot()
    def runToNextBreakpoint(self):
        self._debugger_worker.command_type = DebuggerWorker.CommandType.DEBUG_NEXT_BREAKPOINT
        self._debugger_worker.start()

    @pyqtSlot()
    def runToNextLine(self):
        self._debugger_worker.command_type = DebuggerWorker.CommandType.DEBUG_NEXT_LINE
        self._debugger_worker.start()

    def save_interpreter_command(self, command, *args, **kwargs):
        try:
            if isinstance(command, Callable):
                return command(*args, **kwargs), None
        except (InvalidCodeException, BaseInterpreterError) as e:
            return None, str(e)
        return None, None

    def _set_model_data(self, state: State):
        # frames
        for frame in ("GF", "TF"):
            self._model.clear_sub_tree([], frame)
            if state.frame(frame):
                for var_name, var_value in sorted(state.frame(frame).items(), key=itemgetter(0)):
                    self._model.set_item_data([frame], str(var_name), str(var_value), type(var_value).__name__)

        self._model.clear_sub_tree([], "LF")
        if state.frame_stack:
            for var_name, var_value in sorted(state.frame("LF").items(), key=itemgetter(0)):
                self._model.set_item_data(["LF"], str(var_name), str(var_value), type(var_value).__name__)

        # data stack
        self._model.clear_sub_tree([], "Data stack")
        for i, value in enumerate(state.data_stack[::-1]):
            self._model.set_item_data(["Data stack"], "[{index}]".format(index=i), str(value), type(value).__name__)

        # frames stack9
        frame_stack_model_item_index = self._model.get_item([], "Frame stack").index()
        for i, frame in enumerate(state.frame_stack[::-1]):
            self._model.clear_sub_tree(["Frame stack"], "[{}]".format(i))
            for var_name, var_value in sorted(frame.items(), key=itemgetter(0)):
                self._model.set_item_data(["Frame stack", "[{index}]".format(index=i)], str(var_name), str(var_value),
                                          type(var_value).__name__)

        for i in range(len(state.frame_stack), self._model.rowCount(frame_stack_model_item_index)):
            self._model.remove_sub_tree(["Frame stack"], "[{}]".format(i))

        self._model.set_item_data(
            [],
            "Price", '{} ({}+{})'.format(
                state.instruction_price + state.operand_price,
                state.instruction_price,
                state.operand_price
            ),
            ''
        )

        self._call_stack_model = [
            [
                self._debugger._interpreter.program_line(x),
                (
                    self._debugger._interpreter._instructions[x].op0.label
                 if x != state.program_counter
                    else None
                ) or ''
            ]
            for x in state.call_stack + [state.program_counter]
            ][::-1]
        self.callStackModelChanged.emit(QVariant(self._call_stack_model))

    @pyqtSlot()
    def stop(self):
        self._debugger.stop()
        self._io_wrapper.unblockWaitSignal.emit()
        self._program_line = -1
        self._model.clear()
        self._call_stack_model = []
        self.callStackModelChanged.emit(QVariant(self._call_stack_model))
        self.currentLineChanged.emit(-1) class IOWrapper(QObject):
    unblockWaitSignal = pyqtSignal()
    readRequest = pyqtSignal()
    writeRequest = pyqtSignal(str, arguments=["text"])

    def __init__(self, parent: Optional[QObject]):
        super().__init__(parent)
        self._text = ""

    @pyqtSlot(str)
    def handleConsoleRead(self, text: str) -> None:
        self._text = text
        self.unblockWaitSignal.emit()

    def write(self, text: str) -> None:
        self.writeRequest.emit(text)
        sleep(0.001)

    def block_until_emit(self, unblock_signal):
        loop = QEventLoop()
        unblock_signal.connect(loop.quit)

        loop.exec_(QEventLoop.AllEvents)

    def readline(self):
        self.readRequest.emit()
        self.block_until_emit(self.unblockWaitSignal)
        text = self._text
        self._text = ""

        return text ICON_SIZES = (16, 24, 32, 48, 256)
import signal
import sys
import ifj2017
from os import path
from platform import system
from PyQt5.QtCore import (QSize, QtFatalMsg, QtCriticalMsg, QtWarningMsg, QtInfoMsg,
                          qInstallMessageHandler, QtDebugMsg)
from PyQt5.QtCore import QTimer
from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QIcon
from PyQt5.QtQml import QQmlApplicationEngine, qmlRegisterSingletonType, qmlRegisterType
from PyQt5.QtWidgets import QApplication
from ifj2017.ide.code.diff_code_analyzer import DiffCodeAnalyzer
from ifj2017.ide.code.expression import ExpSyntaxHighlighter, ExpAnalyzer
from ifj2017.ide.code_analyzer import CodeAnalyzer
from ifj2017.ide.core.core import Core
from ifj2017.ide.core.file_io import FileIO, FILE_PREFIX
from ifj2017.ide.core.formatted_text_writer import FormattedTextWriter
from ifj2017.ide.core.tree_view_model import TreeViewModel
from ifj2017.ide.debugger_wrapper import DebuggerWrapper
from ifj2017.ide.io_wrapper import IOWrapper
from ifj2017.ide.settings import ICON_SIZES
    from termcolor import colored
# coding=utf-8



try:
except ImportError:
    def colored(mode, *args, **kwargs):
        return mode

# See more at https://coldfix.de/2016/11/08/pyqt-boilerplate/#keyboardinterrupt-ctrl-c
def setup_interrupt_handling():
    signal.signal(signal.SIGINT, _interrupt_handler)
    safe_timer(50, lambda: None)

def _interrupt_handler(signum, frame):
    QApplication.quit()


def safe_timer(timeout, func, *args, **kwargs):
    def timer_event():
        try:
            func(*args, **kwargs)
        finally:
            QTimer.singleShot(timeout, timer_event)
    QTimer.singleShot(timeout, timer_event)

def qt_message_handler(mode, context, message):
    modes = {
        QtInfoMsg: "Info",
        QtWarningMsg: "Warning",
        QtCriticalMsg: "Critical",
        QtFatalMsg: "Fatal",
        QtDebugMsg: "Debug"
    }

    modes_colors = {
        QtInfoMsg: "blue",
        QtWarningMsg: "yellow",
        QtCriticalMsg: "red",
        QtFatalMsg: "red",
        QtDebugMsg: "green"
    }

    mode = colored(modes[mode], modes_colors[mode])

    if context.file is None:
        print('{mode}: {msg}'.format(mode=mode, msg=message))
    else:
        print('{mode}: {msg}\t\t\t\tline: {line}, function: {func}, file: {file}'.format(
            mode=mode, line=context.line, func=context.function, file=context.file, msg=message))


qInstallMessageHandler(qt_message_handler)

app = QApplication(sys.argv)

base_url = QUrl("{}{}/".format(FILE_PREFIX[system()], str(path.abspath(path.dirname(__file__))).replace('\\', '/')))

icon = QIcon()
for size in ICON_SIZES:
    path = FileIO.removeFilePrefix(base_url.resolved(QUrl("assets/icons/{}x{}.png".format(size, size))).url())
    icon.addFile(path, QSize(size, size))
app.setWindowIcon(icon)

qmlRegisterSingletonType(base_url.resolved(QUrl("assets/styles/UIStyles.qml")), "StyleSettings", 1, 0, "StyleSettings")
qmlRegisterSingletonType(CodeAnalyzer, "CodeAnalyzer", 1, 0, "CodeAnalyzer", CodeAnalyzer.singletonProvider)
qmlRegisterType(ExpSyntaxHighlighter, "ExpSyntaxHighlighter", 1, 0, "ExpSyntaxHighlighter")
qmlRegisterType(DiffCodeAnalyzer, "DiffCodeAnalyzer", 1, 0, "DiffCodeAnalyzer")
qmlRegisterType(ExpAnalyzer, "ExpAnalyzer", 1, 0, "ExpAnalyzer")
qmlRegisterType(TreeViewModel, "TreeViewModel", 1, 0, "TreeViewModel")
qmlRegisterType(DebuggerWrapper, "Debugger", 1, 0, "Debugger")
qmlRegisterType(IOWrapper, "IOWrapper", 1, 0, "IOWrapper")

qmlRegisterType(FileIO, "FileIO", 1, 0, "FileIO")