from FoxDotEditor.tkimport import *
from FoxDotEditor.Format import *
from FoxDotEditor.AppFunctions import *
from renardo_lib.Settings import AUTO_COMPLETE_BRACKETS

whitespace = [" ", "\t", "\n", "\r", "\f", "\v"]


class BracketHandler:
    counter = 0

    def __init__(self, master):
        self.root = master
        self.text = master.text
        self.active = AUTO_COMPLETE_BRACKETS
        self.inbrackets = False
        self.separators = py_separators
        self.style = bracket_formatting
        self.left_brackets = dict(zip(left_b, right_b))
        self.right_brackets = dict(zip(right_b, left_b))
        self.right_brackets_only = list("]})")
        self.queue = []
        if self.active:
            for char in list(self.left_brackets.keys()) + list(self.right_brackets.keys()):
                # this binds bracket presses to the handle method
                self.text.bind(char, self.handle)
        self.visible_range = (0, 0)

    def handle(self, event=None, insert=INSERT):
        self.visible_range = self.text.get_visible_range()
        ret = None
        line, column = index(self.text.index(insert))
        # 1. Type a left bracket
        next_char = self.text.get(index(line, column))
        if event.char in self.left_brackets:
            # A. Type a left bracket and an area is selected
            try:
                a = self.text.index(SEL_FIRST)
                b = self.text.index(SEL_LAST)
                self.text.insert(b, self.left_brackets[event.char])
                self.text.insert(a, event.char)
                # unselect
                self.text.tag_remove(SEL, "1.0", END)
                ret = "break"
            except Exception:
                pass
            # B. If next character is a whitespace or a right bracket add a
            # closing bracket as well.
            if next_char in whitespace + self.right_brackets_only:
                # If the next char is quote mark, just move the cursor
                if event.char in ("'", '"'):
                    if event.char == next_char:
                        self.text.mark_set(insert, index(line, column + 1))
                        ret = "break"
                    else:
                        # Only add a closing quote mark if there are already
                        # an even no. of quotes in the line
                        text = self.text.get(index(line, 0),
                                             index(line, "end"))
                        n = text.count(event.char)
                        if n % 2 == 0:
                            self.text.insert(self.text.index(insert),
                                             event.char + self.left_brackets[event.char])
                            self.text.mark_set(insert, index(line, column + 1))
                            ret = "break"
                else:
                    self.text.insert(self.text.index(insert),
                                     event.char + self.left_brackets[event.char])
                    self.text.mark_set(insert, index(line, column + 1))
                    ret = "break"
            # Update line colours
            self.root.colour_line(line)
        # 2. Type right bracket
        elif event.char in self.right_brackets:
            # Go back and find an open bracket -> assume the first open bracket
            # is related to *this* one
            self.text.insert("{}.{}".format(line, column),
                             event.char)  # Add bracket
            coords = self.find_starting_bracket(line, column, event.char)
            new_line, new_col = line, column + 1
            # Assume we are adding a new bracket
            adding_bracket = True
            # Get index of the end of the buffer
            end_line, end_col = index(self.text.index("{}.end".format(self.visible_range[1])))
            while (new_line, new_col) != (end_line, end_col):
                # If we find a closing bracket,  find it's pair
                next_char = self.text.get(index(new_line, new_col))
                if next_char == event.char:
                    coords_ = self.find_starting_bracket(new_line,
                                                         new_col,
                                                         event.char,
                                                         offset=0)
                    # If there is a closing brackets
                    if coords_ is None:
                        adding_bracket = False
                        break
                    else:
                        adding_bracket = True
                if index(new_line, new_col) == self.text.index(index(new_line,
                                                                     "end")):
                    new_line += 1
                    new_col = 0
                else:
                    new_col += 1

            if not adding_bracket:
                loc = index(line, column+1)
                if self.text.get(loc) == event.char:
                    self.text.delete(loc)
            ret = "break"
            # Update line colours
            self.root.colour_line(line)
            # Highlight brackets (remove old highlighting first)
            self.text.tag_delete("tag_open_brackets")

            if coords is not None:
                row, col = coords
                self.text.tag_config("tag_open_brackets", **self.style)
                self.text.tag_add("tag_open_brackets", "{}.{}".format(row, col),
                                  "{}.{}".format(row, col+1))
                self.text.tag_add("tag_open_brackets", "{}.{}".format(line, column),
                                  "{}.{}".format(line, column+1))
        if ret is None:
            self.text.insert(INSERT, event.char)
            self.root.colour_line(line)
        # Store the text
        self.root.text_as_string = self.root.get_all()
        # Add separator to undo stack
        self.text.edit_separator()
        return "break"

    def delete(self, insert=INSERT):
        """
        Deletes enclosed set of brackets if no contents and returns True,
        returns False all other times
        """
        if self.active:
            line, column = index(self.text.index(insert))
            next_char = self.text.get(index(line, column))
            prev_char = self.text.get(index(line, column-1))
            if prev_char in self.left_brackets:
                if next_char == self.left_brackets[prev_char] and column > 0:
                    self.text.delete(index(line, column-1), index(line, column+1))
                    return True
        return False

    def find_starting_bracket(self, line, column, bracket_style, offset=0):
        """
        Finds the opening bracket to the closing bracket at line, column
        co-ords. Returns None if not found. Only goes as far as the first line
        visible on screen
        """
        line_length = column - 1
        used_br = offset
        start_row, end_row = self.visible_range  # returns tuple
        for row in range(line, start_row - 1, -1):
            for col in range(line_length, -1, -1):
                # If the char is a left bracket and not used, break
                if self.text.get("{}.{}".format(row, col)) == self.right_brackets[bracket_style]:
                    if used_br == 0:
                        return row, col
                    else:
                        used_br -= 1
                elif self.text.get("{}.{}".format(row, col)) == bracket_style:
                    used_br += 1
            line_length = int(self.text.index("{}.end".format(row-1)).split(".")[1])
        else:
            return None
