import logging

from AnyQt import _api
from confapp import conf

from pyforms_gui.controls.control_base import ControlBase
import pyforms_gui.utils.tools as tools

from AnyQt import QtCore, uic
from AnyQt.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QSpinBox
from AnyQt.QtGui  import QFontMetrics, QColor, QIcon, QFont

if _api.USED_API == _api.QT_API_PYQT5:
    from PyQt5.Qsci import QsciScintilla, QsciLexerPython, QsciAPIs
# elif _api.USED_API == _api.QT_API_PYQT4:
#     from PyQt4.Qsci import QsciScintilla, QsciLexerPython, QsciAPIs

logger = logging.getLogger(__name__)


class ControlCodeEditor(ControlBase):
    """
    Control that offers a code editor with pretty-print and line numbers and a save button
    """

    ARROW_MARKER_NUM = 8

    def __init__(self, *args, **kwargs):
        """

        :param label:
        :param default:
        :param helptext:
        """
        self._read_only = kwargs.get('readonly', False)
        self._changed_func = None
        super(ControlCodeEditor, self).__init__(*args, **kwargs)

        self.discard_event = kwargs.get('discard_event', self.discard_event)

    def init_form(self):
        """

        """

        control_path = tools.getFileInSameDirectory(__file__, "code_editor.ui")
        self._form = uic.loadUi(control_path)

        self._code_editor = self._form.code_editor
        self._save_button = self._form.save_button
        self._discard_button = self._form.discard_button

        self._save_button.clicked[bool].connect(self.on_save_changes)
        self._discard_button.clicked[bool].connect(self.on_discard_changes)

        if self._read_only:
            self._code_editor.setReadOnly(True)
            self._save_button.setVisible(False)
            self._discard_button.setVisible(False)

        self.form.font_size.addItem('9')
        self.form.font_size.addItem('10')
        self.form.font_size.addItem('11')
        self.form.font_size.addItem('12')
        self.form.font_size.addItem('14')
        self.form.font_size.addItem('18')
        self.form.font_size.addItem('24')

        # Set the default font size
        index = self.form.font_size.findText(conf.PYFORMS_CONTROL_CODE_EDITOR_DEFAULT_FONT_SIZE)
        self.form.font_size.setCurrentIndex(index)

        self.form.font_size.currentIndexChanged.connect(self.__font_size_index_changed)

        self.form.save_button.setIcon(QIcon(conf.PYFORMS_ICON_CODEEDITOR_SAVE))
        self.form.discard_button.setIcon(QIcon(conf.PYFORMS_ICON_CODEEDITOR_DISCARD))

        self.lexer = QsciLexerPython

        self._code_editor.keyPressEvent = self._key_pressed
        self._changed_func = None

        self.value = self._value
        super(ControlCodeEditor, self).init_form()

    def __font_size_index_changed(self, index):
        item = self.form.font_size.currentText()
        if len(item) >= 1:
            self._load_code_editor_settings()

    def _load_code_editor_settings(self):
        """
        Load settings on the code editor like, font style, margins, scroll, etc.
        Based on the example from http://eli.thegreenplace.net/2011/04/01/sample-using-qscintilla-with-pyqt/
        """

        item = self.form.font_size.currentText()
        size = int(item)

        # Set the default font
        font = QFont()
        font.setFamily('Courier New')
        font.setFixedPitch(True)
        font.setPointSize(size)
        self._code_editor.setFont(font)
        self._code_editor.setMarginsFont(font)

        # Margin 0 is used for line numbers
        fontmetrics = QFontMetrics(font)
        self._code_editor.setMarginsFont(font)
        self._code_editor.setMarginWidth(0, fontmetrics.width("00000") + 6)
        self._code_editor.setMarginLineNumbers(0, True)
        self._code_editor.setMarginsBackgroundColor(QColor("#cccccc"))

        # Clickable margin 1 for showing markers
        self._code_editor.setMarginSensitivity(1, True)
        self._code_editor.marginClicked.connect(self.on_margin_clicked)
        self._code_editor.markerDefine(QsciScintilla.RightArrow, self.ARROW_MARKER_NUM)
        self._code_editor.setMarkerBackgroundColor(QColor("#ee1111"), self.ARROW_MARKER_NUM)

        # Detect changes to text
        self._code_editor.modificationChanged.connect(self.on_modification_changed)

        # Brace matching: enable for a brace immediately before or after the current position
        self._code_editor.setBraceMatching(QsciScintilla.SloppyBraceMatch)

        # Current line visible with special background color
        self._code_editor.setCaretLineVisible(True)
        self._code_editor.setCaretLineBackgroundColor(QColor("#ffe4e4"))

        # Set Python lexer
        # Set style for Python comments (style number 1) to a fixed-width Courier.
        lexer = self.lexer()
        lexer.setDefaultFont(font)
        self._code_editor.setLexer(lexer)
        self._code_editor.setIndentationWidth(4)
        # self._code_editor.SendScintilla(QsciScintilla.SCI_STYLESETFONT, 1, 'Courier')
        self._code_editor.SendScintilla(QsciScintilla.SCI_STYLESETFONT, 1)

        # Don't want to see the horizontal scrollbar at all
        # Use raw message to Scintilla here (all messages are documented here: http://www.scintilla.org/ScintillaDoc.html)
        self._code_editor.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0)

        self._lexer_obj = lexer
        self.qsci_api = QsciAPIs(self._lexer_obj)
        ## Add autocompletion strings
        self.qsci_api.add("aLongString")
        self.qsci_api.add("aLongerString")
        self.qsci_api.add("aDifferentString")
        self.qsci_api.add("sOmethingElse")
        ## Compile the api for use in the lexer
        self.qsci_api.prepare()
        self._code_editor.setAutoCompletionThreshold(1)
        self._code_editor.setAutoCompletionSource(QsciScintilla.AcsAll)

    # not too small
    # self._code_editor.setMinimumSize(600, 450)

    ###################################################################
    ############ Events ###############################################
    ###################################################################

    def on_margin_clicked(self, nmargin, nline, modifiers):  # pylint: disable=unused-argument
        """
        On margin clicked, toggle marker for the line the margin was clicked on
        :param nmargin:
        :type nmargin:
        :param nline:
        :type nline:
        :param modifiers:
        :type modifiers:
        """
        if self._code_editor.markersAtLine(nline) != 0:
            self._code_editor.markerDelete(nline, self.ARROW_MARKER_NUM)
        else:
            self._code_editor.markerAdd(nline, self.ARROW_MARKER_NUM)

    def on_modification_changed(self):
        """
        On modification change, re-enable save button
        """
        if self._changed_func:
            self._save_button.setEnabled(True)
            self._discard_button.setEnabled(True)

    def on_save_changes(self):
        """
        On button save clicked, save changes made on the code editor to file
        """
        if self.changed_event():
            self._code_editor.setModified(False)
            self._save_button.setEnabled(False)
            self._discard_button.setEnabled(False)

    def on_discard_changes(self):

        if self.discard_event():
            self._code_editor.setModified(False)
            self._save_button.setEnabled(False)
            self._discard_button.setEnabled(False)

    def discard_event(self):

        return True


    def _key_pressed(self, event):
        """
        Handle KeyPressed event
        We only care about CTRL-S in order to save changes
        :param event: key event
        """
        QsciScintilla.keyPressEvent(self._code_editor, event)
        if event.key() in [QtCore.Qt.Key_S, QtCore.Qt.Key_Save]:
            modifiers = QApplication.keyboardModifiers()
            if modifiers == QtCore.Qt.ControlModifier and self.is_modified:
                logger.debug("Saving...")
                self.on_save_changes()

        self.key_pressed_event(event)

    def key_pressed_event(self, event):
        """
        Override KeyPressed event as you like
        :param event: key event
        """
        pass

    @property
    def is_modified(self):
        return self._code_editor.isModified()

    ###################################################################
    ############ Properties ###########################################
    ###################################################################

    @property
    def lexer(self):
        return self._lexer

    @lexer.setter
    def lexer(self, value):
        self._lexer = value
        self._load_code_editor_settings()

    @property
    def value(self):
        return self._code_editor.text()

    @value.setter
    def value(self, value):
        if value is not None:
            self._code_editor.setText(str(value))
            self._code_editor.setModified(False)
            self._save_button.setEnabled(False)
            self._discard_button.setEnabled(False)

    @property
    def changed_event(self):
        return self._changed_func if self._changed_func else (lambda: 0)

    @changed_event.setter
    def changed_event(self, value):
        self._changed_func = value
