# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""This module contains the close quotes editor extension."""

import re

# Third party imports
from qtpy.QtGui import QTextCursor

# Local imports
from spyder.plugins.editor.api.editorextension import EditorExtension


def unmatched_quotes_in_line(text):
    """Return whether a string has open quotes.

    This simply counts whether the number of quote characters of either
    type in the string is odd.

    Take from the IPython project (in IPython/core/completer.py in v0.13)
    Spyder team: Add some changes to deal with escaped quotes

    - Copyright (C) 2008-2011 IPython Development Team
    - Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
    - Copyright (C) 2001 Python Software Foundation, www.python.org

    Distributed under the terms of the BSD License.
    """
    # Remove escaped quotes from counting, but only if they are not to the
    # right of a backslash because in that case the backslash is the char that
    # is escaped.
    text = re.sub(r"(?<!\\)\\'", '', text)
    text = re.sub(r'(?<!\\)\\"', '', text)

    # We check " first, then ', so complex cases with nested quotes will
    # get the " to take precedence.
    if text.count('"') % 2:
        return '"'
    elif text.count("'") % 2:
        return "'"
    else:
        return ''


class CloseQuotesExtension(EditorExtension):
    """Editor Extension for insert closing quotes automatically."""

    def on_state_changed(self, state):
        """Connect/disconnect sig_key_pressed signal."""
        if state:
            self.editor.sig_key_pressed.connect(self._on_key_pressed)
        else:
            self.editor.sig_key_pressed.disconnect(self._on_key_pressed)

    def _on_key_pressed(self, event):
        if event.isAccepted():
            return

        # It is necessary to use here the text of the event and not the key
        # to avoid issues with international keyboards.
        # See spyder-ide/spyder#9814
        char = event.text()
        if char in ('"', '\'') and self.enabled:
            self.editor.completion_widget.hide()
            self._autoinsert_quotes(char)
            event.accept()

    def _autoinsert_quotes(self, char):
        """Control how to automatically insert quotes in various situations."""
        line_text = self.editor.get_text('sol', 'eol')
        line_to_cursor = self.editor.get_text('sol', 'cursor')
        cursor = self.editor.textCursor()
        last_three = self.editor.get_text('sol', 'cursor')[-3:]
        last_two = self.editor.get_text('sol', 'cursor')[-2:]
        trailing_text = self.editor.get_text('cursor', 'eol').strip()

        if self.editor.has_selected_text():
            text = self.editor.get_selected_text()
            self.editor.insert_text("{0}{1}{0}".format(char, text))
            # keep text selected, for inserting multiple quotes
            cursor.movePosition(QTextCursor.Left, QTextCursor.MoveAnchor, 1)
            cursor.movePosition(
                QTextCursor.Left,
                QTextCursor.KeepAnchor,
                # Don't count 2 cursor moves for \r\n. Fixes: #25743
                len(text.replace(self.editor.get_line_separator(), "\n"))
            )
            self.editor.setTextCursor(cursor)
        elif self.editor.in_comment():
            self.editor.insert_text(char)
        elif (len(trailing_text) > 0 and
                not unmatched_quotes_in_line(line_to_cursor) == char and
                not trailing_text[0] in (',', ':', ';', ')', ']', '}')):
            self.editor.insert_text(char)
        elif (unmatched_quotes_in_line(line_text) and
                (not last_three == 3*char)):
            self.editor.insert_text(char)
        # Move to the right if we are before a quote
        elif self.editor.next_char() == char:
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor, 1)
            cursor.clearSelection()
            self.editor.setTextCursor(cursor)
        # Automatic insertion of triple double quotes (for docstrings)
        elif last_three == 3*char:
            self.editor.insert_text(3*char)
            cursor = self.editor.textCursor()
            cursor.movePosition(QTextCursor.PreviousCharacter,
                                QTextCursor.KeepAnchor, 3)
            cursor.clearSelection()
            self.editor.setTextCursor(cursor)
        # If last two chars are quotes, just insert one more because most
        # probably the user wants to write a docstring
        elif last_two == 2*char:
            self.editor.insert_text(char)
            self.editor.delayed_popup_docstring()
        # Automatic insertion of quotes
        else:
            self.editor.insert_text(2*char)
            cursor = self.editor.textCursor()
            cursor.movePosition(QTextCursor.PreviousCharacter)
            self.editor.setTextCursor(cursor)
