"""
This file contains all the important stuff for the actual 'code' editor
"""

from PyQt5.QtWidgets import (
    QWidget,
    QVBoxLayout,
    QHBoxLayout,
    QCompleter,
    QPlainTextEdit,
    QLabel,
    QTextEdit,
    QToolTip,
    QAction,
    QApplication,
    QMenu,
    QInputDialog,
)
from PyQt5.QtGui import (
    QFont,
    QTextCursor,
    QTextOption,
    QTextBlock,
    QColor,
    QTextCharFormat,
    QCursor,
    QTextFormat,
    QPainter,
    QTextLayout,
)
from PyQt5.QtCore import Qt, QPoint, QSize, QProcess, QRect, pyqtSignal, QRegExp, QEvent
from Hydra.utils.config import config_reader, LOCATION
from Hydra.utils.completer_utility import tokenize
from Hydra.utils.find_utility import find_all
from Hydra.utils.completer_utility import wordList
from Hydra.widgets.Pythonhighlighter import PyHighlighter
from Hydra.widgets.Pythonhighlighter import PyHighlighter
from Hydra.widgets.OpenFile import OpenFile
from Hydra.widgets.SaveFile import SaveFile
from Hydra.widgets.Editor import Editor, Completer
from Hydra.widgets.numberBar import NumberBar
from Hydra.widgets.foldArea import FoldArea
from Hydra.widgets.Label import StatusLabel
from PyQt5.QtTest import QTest
import os

configs = [config_reader(0), config_reader(1), config_reader(2)]

with open(LOCATION + "default.json") as choice:
    choiceIndex = int(choice.read())

editor = configs[choiceIndex]["editor"]


class Content(QWidget):

    readyToShow = pyqtSignal(bool)

    def __init__(
        self, text, fileName, baseName, parent, isReadOnly=False, searchCommand=None
    ):
        super().__init__()

        self.lazy = None
        if os.path.isfile(fileName):
            self.size_of_file = os.path.getsize(fileName)
            self.lazy = True  # If file exists then we can lazy load it

        else:
            self.size_of_file = 0
            self.lazy = False

        self.editor = Editor(self, isReadOnly)
        self.numberbar = NumberBar(self.editor)
        self.foldArea = FoldArea(self.editor)

        self.stack = []
        self.text = text
        self.parent = parent
        self.wordlist = wordList
        self.fileName = fileName
        self.baseName = baseName
        self.setting_up = 0
        self.temporary = 0

        self.searchCommand = searchCommand
        self.font = QFont()
        self.font.setFamily(editor["editorFont"])
        self.font.setPointSize(editor["editorFontSize"])
        self.tabSize = editor["TabWidth"]

        self.highlighter = PyHighlighter(self.editor)
        self.currentBlockCount = None
        self.saved = True
        self.editor.setPlainText(str(text))
        self.main_layout = QVBoxLayout(self)
        self.hbox = QHBoxLayout()

        self.status_bar_layout = QHBoxLayout()
        self.statusBarFont = QFont(editor["statusBarFont"], editor["statusBarFontSize"])
        self.columnLabel = StatusLabel(text="", font=self.statusBarFont)
        self.rowLabel = StatusLabel(text="", font=self.statusBarFont)
        self.totalLineLabel = StatusLabel(text="", font=self.statusBarFont)

        self.open_file = OpenFile()
        self.save_file = SaveFile(self)

        self.open_file.add_args(self.fileName, self.lazy)
        # Create a widget for the line numbers
        self.hbox.addWidget(self.numberbar)
        self.hbox.addWidget(self.foldArea)
        self.hbox.addWidget(self.editor)

        self.hbox.setSpacing(0)

        self.status_bar_layout.addWidget(self.columnLabel)
        self.status_bar_layout.addSpacing(10)
        self.status_bar_layout.addWidget(self.rowLabel)
        self.status_bar_layout.addSpacing(10)
        self.status_bar_layout.addWidget(self.totalLineLabel)
        self.status_bar_layout.addStretch()

        self.main_layout.addLayout(self.hbox)
        self.main_layout.addLayout(self.status_bar_layout)
        self.open_file.dataSignal.connect(
            self.insertText
        )  # This causes a bug if the cursor isn't in a position
        self.open_file.readDone.connect(self.readCompleted)
        # self.save_file.updateOffset.connect(lambda amount: self.change_offset(amount))

        self.open_file.readDone.connect(self.opening)
        # self.open_file.readAmountSignal.connect(self.prepare_to_save) lazy load [not working]
        # self.open_file.EndOfFileSignal.connect(self.EOF) lazy load [not working]

        self.IO_status = QLabel("Opening...")
        self.IO_status.setFont(self.font)

        self.save_file.readDone.connect(self.saving)

        self.line = None
        self.column = None
        # self.opening = True
        self.startLine = None
        self.endLine = None

        self.end = [False, 0]
        self.start_from = 0

        self.completer = Completer(self.wordlist)
        self.setCompleter(self.completer)

        if self.baseName.endswith(".py"):
            self.highlighter = PyHighlighter(self.editor.document(), self.editor)
        elif self.baseName.endswith(".doc"):
            self.highlighter = PyHighlighter(self.editor.document(), self.editor)

        self.editor.cursorPositionChanged.connect(self.change_col)
        # self.editor.textChanged.connect()

    def assignLines(self, array: list) -> None:

        self.startLine = array[0]
        self.endLine = array[1]

    def change_read_amount(self, amount: int) -> None:

        self.open_file.change_read_amount(amount)

    def change_offset(self, amount: int) -> None:

        self.open_file.change_offset(amount)

    def opening(self, state: bool) -> None:

        if state:
            self.readyToShow.emit(True)

    def saving(self, state: bool) -> None:

        if state:  # Saving has finished

            pass  # TODO: indicate that we have finished

        else:

            pass  # TODO: Indicate that we haven't finished

    def start_saving(self):

        self.save_file.add_args(self)
        self.save_file.start()

    def readCompleted(self, state: bool) -> None:

        if state:

            self.initialize(True)

    def initialize(self, justOpened=False):
        """
        After content is loaded into the file, this function will handle jump to definition when opening a file
        """
        QTest.qWait(5)  # Without this, it won't work

        if self.searchCommand:

            self.searchFor(self.searchCommand)

    def searchFor(self, searchCommand: str):

        regex = QRegExp(searchCommand)

        index = find_all(self.editor.toPlainText(), searchCommand)
        a = list(index)

        textCursor = self.editor.textCursor()
        try:
            textCursor.setPosition(a[0])
            textCursor.movePosition(
                textCursor.Right, textCursor.KeepAnchor, regex.matchedLength()
            )
            self.editor.setTextCursor(textCursor)

        except Exception as E:
            pass

    def jumpToDef(self, tagList: list):

        tagInfo = tagList[0]
        fileName = tagList[1]
        searchCommand = tagList[2]

        if fileName.split("/")[-1] == self.baseName:
            self.searchFor(searchCommand)

        self.parent.cleanOpen(fileName, False, searchCommand)

    def EOF(self, data: list) -> None:
        """

        :param data[0]: bool
        :param data[1]: int
        :return: None
        """

        if data[0]:

            self.end = [*data]  # unpack the data

        else:  # this should never happen
            self.end = False

    def insertText(self, text: str) -> None:

        cursor = QTextCursor(self.editor.document())

        cursor.movePosition(QTextCursor.End)

        self.editor.setTextCursor(cursor)

        self.editor.insertPlainText(text)

    def prepare_to_save(self, array):

        currently_read_bytes = array[0]
        maximum_bytes = array[1]
        """If the user hasn't scrolled to the bottom
        (lazy loading isn't completed) and saves the file. no data gets lost"""
        self.start_from = currently_read_bytes

    def decide(self, array):  # Right now this function is not used anywhere

        """
        Decides if we lazy load text to the user or not
        :param array:
        :return: None
        """

        if not self.lazy:
            return
        else:
            text_size = len(self.editor.toPlainText().encode("utf-8"))
            if self.size_of_file != text_size:
                min_value = array[0]
                max_value = array[1]

                if min_value == max_value:
                    self.open_file.our_start()

    def start_opening(self):

        self.open_file.start()

    def change_col(self):
        textCursor = self.editor.textCursor()

        line = textCursor.blockNumber() + 1
        column = textCursor.positionInBlock()

        """self.status_bar.setText(
            "Line: "
            + str(line)
            + " Column: "
            + str(column)
            + "           Total: "
            + str(self.editor.totalLines() - 1)
            + " lines"
            + "     Size: "
            + str(self.size_of_file / 1000)
            + " KiB"
        )"""

        self.rowLabel.setText("Ln: {}".format(line))
        self.columnLabel.setText("Col: {}".format(column))
        self.totalLineLabel.setText("Total: {}".format(self.editor.totalLines()))

        self.editor.highlightCurrentLine()

    def get_size(self, input):
        return round(len(input.encode("utf-8")) / 1000)

    def leaveEvent(self, event: QEvent) -> None:

        self.editor.returnCursorToNormal()
        super().leaveEvent(event)

    def code_info(self, data):
        counter = 1
        keywords = {}
        text_array = data.splitlines()

        for w in text_array:
            word = w.strip()
            if word.startswith("class ") or word.startswith("def "):
                keywords[counter] = word.strip()

            counter += 1
        return keywords

    def tokenize_file(self):

        for i in tokenize(self.fileName):
            for j in i:
                if j not in self.wordlist:
                    self.wordlist.append(j)

    def setCompleter(self, completer):

        self.completer.setWidget(self)

        completer.setCompletionMode(QCompleter.PopupCompletion)
        completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer = completer

        self.completer.insertText.connect(self.insertCompletion)

    def insertCompletion(self, completion):
        textCursor = self.editor.textCursor()

        extra = len(completion) - len(self.completer.completionPrefix())

        textCursor.movePosition(QTextCursor.Left)

        textCursor.movePosition(QTextCursor.EndOfWord)
        textCursor.insertText(completion[-extra:])

        if completion.endswith("()"):
            cursorPos = textCursor.position()
            textCursor.setPosition(cursorPos - 1)

        self.editor.setTextCursor(textCursor)

    def textUnderCursor(self):
        textCursor = self.editor.textCursor()
        textCursor.select(QTextCursor.WordUnderCursor)

        return textCursor.selectedText()

    def focusInEvent(self, event):
        if self.completer:
            self.completer.setWidget(self)
            QPlainTextEdit.focusInEvent(self, event)

    def checkIfCanComplete(self):

        pass

    def keyPressEvent(self, event):

        if (
            self.completer
            and self.completer.popup()
            and self.completer.popup().isVisible()
        ):
            if event.key() in (
                Qt.Key_Enter,
                Qt.Key_Return,
                Qt.Key_Escape,
                Qt.Key_Tab,
                Qt.Key_Backtab,
            ):
                event.ignore()
                return

        isShortcut = event.modifiers() == Qt.ControlModifier and event.key() == Qt.Key_B

        if not self.completer or not isShortcut:
            QPlainTextEdit.keyPressEvent(self.editor, event)

        completionPrefix = self.textUnderCursor()

        if not isShortcut:
            if self.completer.popup():
                self.completer.popup().hide()
            return

        self.completer.setCompletionPrefix(completionPrefix)

        popup = self.completer.popup()

        popup.setFont(self.font)
        popup.setCurrentIndex(self.completer.completionModel().index(0, 0))

        cr = self.editor.cursorRect()
        cr.translate(QPoint(10, 10))
        cr.setWidth(
            self.completer.popup().sizeHintForColumn(0)
            + self.completer.popup().verticalScrollBar().sizeHint().width()
        )
        self.completer.complete(cr)

    def getTextCursor(self):
        textCursor = self.editor.textCursor()
        textCursorPos = textCursor.position()

        return textCursor, textCursorPos

    def changeSaved(self):
        self.modified = self.editor.is_modified()

        try:
            if self.modified:
                self.parent.setWindowTitle(
                    "Hydra ~ " + str(self.baseName) + " [UNSAVED]"
                )

            else:
                pass

        except NameError as E:
            print(E, " on line 124 in the file Content.py")