import sublime
import sublime_plugin
import os

_st_version = int(sublime.version())
if _st_version < 3000:
    from modules import conflict_re
    from modules import drawing_flags as draw
    from modules import git_mixin
    from modules import icons
    from modules import messages as msgs
    from modules import settings
else:
    from .modules import conflict_re
    from .modules import drawing_flags as draw
    from .modules import git_mixin
    from .modules import icons
    from .modules import messages as msgs
    from .modules import settings


def plugin_loaded():
    settings.load()


def find_conflict(view, begin=0):
    conflict_region = view.find(conflict_re.NO_NAMING_GROUPS_PATTERN, begin)

    if not conflict_region:
        conflict_region = view.find(conflict_re.NO_NAMING_GROUPS_PATTERN, 0)
        if not conflict_region:
            sublime.status_message(msgs.get('no_conflict_found'))
            return None

    return conflict_region


def highlight_conflict_group(view, group):
    scope = group + '_gutter'
    if settings.get(scope):
        conflict_regions = view.find_all(conflict_re.CONFLICT_GROUP_REGEX[group])

        if not conflict_regions:
            return

        # Remove the first and last line since they just contain the separators
        highlight_regions = []
        for region in conflict_regions:
            region = view.split_by_newlines(region)[1:-1]
            # Ignore empty subregions
            if not region:
                continue

            for subregion in region:
                highlight_regions.append(subregion)

        view.erase_regions("GitConflictRegion_" + group)
        view.add_regions(
            "GitConflictRegions_" + group,
            highlight_regions,
            "warning",
            icons.get(group),
            draw.hidden()
        )


def highlight_conflicts(view):
    conflict_regions = view.find_all(conflict_re.NO_NAMING_GROUPS_PATTERN)

    view.erase_regions("GitConflictRegions")
    view.add_regions(
        "GitConflictRegions",
        conflict_regions,
        settings.get('matching_scope'),
        "",
        draw.visible()
    )

    highlight_conflict_group(view, 'ours')
    highlight_conflict_group(view, 'ancestor')
    highlight_conflict_group(view, 'theirs')


def extract(view, region, keep):
    conflict_text = view.substr(region)
    match = conflict_re.CONFLICT_REGEX.search(conflict_text)

    # If we didn't matched the group return None
    if not match.group(keep):
        sublime.status_message(msgs.get('no_such_group'))
        return None

    return conflict_re.CONFLICT_REGEX.sub(r'\g<' + keep + '>', conflict_text)


class FindNextConflict(sublime_plugin.TextCommand):
    def run(self, edit):
        # Reload settings
        settings.load()

        current_selection = self.view.sel()

        # Use the end of the current selection for the search, or use 0 if nothing is selected
        begin = 0
        if len(current_selection) > 0:
            begin = self.view.sel()[-1].end()

        conflict_region = find_conflict(self.view, begin)
        if conflict_region is None:
            return

        # Add the region to the selection
        self.view.show_at_center(conflict_region)
        current_selection.clear()
        current_selection.add(conflict_region)


class Keep(sublime_plugin.TextCommand):
    def run(self, edit, keep):
        # Reload settings
        settings.load()

        current_selection = self.view.sel()

        # Use the begin of the current selection for the search, or use 0 if nothing is selected
        begin = 0
        if len(current_selection) > 0:
            begin = current_selection[0].begin()

        conflict_region = find_conflict(self.view, begin)
        if conflict_region is None:
            return

        replace_text = extract(self.view, conflict_region, keep)

        if not replace_text:
            replace_text = ""

        self.view.replace(edit, conflict_region, replace_text)


class ListConflictFiles(sublime_plugin.WindowCommand, git_mixin.GitMixin):
    def run(self):
        # Reload settings
        settings.load()

        # Ensure git executable is available
        if not self.git_executable_available():
            sublime.error_message(msgs.get('git_executable_not_found'))
            return

        self.git_repo = self.determine_git_repo()
        if not self.git_repo:
            sublime.status_message(msgs.get('no_git_repo_found'))
            return

        conflict_files = self.get_conflict_files()
        if not conflict_files:
            sublime.status_message(msgs.get('no_conflict_files_found', self.git_repo))
            return

        self.show_quickpanel_selection(conflict_files)

    def get_conflict_files(self):
        # Search for conflicts using git executable
        conflict_files = self.git_command(
            ["diff", "--name-only", "--diff-filter=U"],
            repo=self.git_repo
        )

        conflict_files = conflict_files.split('\n')
        # Remove empty strings and sort the list
        # (TODO: sort also filenames only?)
        return sorted([x for x in conflict_files if x])

    def get_representation_list(self, conflict_files):
        """Returns a list with only filenames if the 'show_only_filenames'
        option is set, otherwise it returns just a clone of the given list"""
        result = None
        if settings.get('show_only_filenames'):
            result = []
            for string in conflict_files:
                result.append(string.rpartition('/')[2])
        else:
            result = list(conflict_files)

        # Add an "Open all ..." option
        result.insert(0, msgs.get('open_all'))

        return result

    def show_quickpanel_selection(self, conflict_files):
        full_path = [os.path.join(self.git_repo, x) for x in conflict_files]
        show_files = self.get_representation_list(conflict_files)

        # Show the conflict files in the quickpanel and open them on selection
        def open_conflict(index):
            if index < 0:
                return
            elif index == 0:
                # Open all ...
                self.open_files(*full_path)
            else:
                self.open_files(full_path[index - 1])

        self.window.show_quick_panel(show_files, open_conflict)

    def open_files(self, *files):
        for file in files:
            # Workaround sublime issue #39 using sublime.set_timeout
            # (open_file() does not set cursor when run from a quick panel callback)
            sublime.set_timeout(
                lambda file=file: init_view(self.window.open_file(file)),
                0
            )


def init_view(view):
    return  # TODO: Find a workaround for the cursor position bug

    if view.is_loading():
        sublime.set_timeout(lambda: init_view(view), 50)
    else:
        view.run_command("find_next_conflict")


class ScanForConflicts(sublime_plugin.EventListener):
    def on_activated(self, view):
        if settings.get('live_matching'):
            highlight_conflicts(view)

    def on_load(self, view):
        if settings.get('live_matching'):
            highlight_conflicts(view)

    def on_pre_save(self, view):
        if settings.get('live_matching'):
            highlight_conflicts(view)


# ST3 automatically calls plugin_loaded when the API is ready
# For ST2 we have to call the function manually
if _st_version < 3000:
    plugin_loaded()