#
# This is an extension to the Nautilus file manager to allow better
# integration with the Subversion source control system.
#
# Copyright (C) 2006-2008 by Jason Field <jason@jasonfield.com>
# Copyright (C) 2007-2008 by Bruce van der Kooij <brucevdkooij@gmail.com>
# Copyright (C) 2008-2010 by Adam Plumb <adamplumb@gmail.com>
#
# RabbitVCS is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# RabbitVCS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with RabbitVCS;  If not, see <http://www.gnu.org/licenses/>.
#

"""
Concrete VCS implementation for Mercurial functionality.
"""
from __future__ import absolute_import

import os.path
from datetime import datetime

from mercurial import commands, ui, hg

from rabbitvcs.util.strings import S

import rabbitvcs.vcs
import rabbitvcs.vcs.status
import rabbitvcs.vcs.log
import rabbitvcs.vcs.mercurial.util
from rabbitvcs.vcs.branch import BranchEntry
from rabbitvcs.util.log import Log

log = Log("rabbitvcs.vcs.mercurial")

from rabbitvcs import gettext

_ = gettext.gettext


class Revision(object):
    """
    Implements a simple revision object as a wrapper around the gittyup revision
    object.  This allows us to provide a standard interface to the object data.
    """

    def __init__(self, kind, value=None):
        self.kind = kind.upper()
        self.value = value

        if self.kind == "HEAD":
            self.value = "HEAD"

        self.is_revision_object = True

    def __str__(self):
        if self.value:
            return S(self.value)
        return S(self.kind)

    def __unicode__(self):
        return self.__str__().unicode()

    def short(self):
        if self.value:
            return S(self.value)[0:7]
        else:
            return self.kind

    def __repr__(self):
        return self.__str__()

    def primitive(self):
        return self.value


class Mercurial(object):
    STATUS = {
        "normal": "C",
        "added": "A",
        "removed": "R",
        "modified": "M",
        "untracked": "?",
        "missing": "!",
    }

    STATUS_REVERSE = {
        "C": "normal",
        "A": "added",
        "R": "removed",
        "M": "modified",
        "?": "untracked",
        "!": "missing",
    }

    STATUSES_FOR_REVERT = ["missing", "modified", "removed"]

    STATUSES_FOR_COMMIT = ["untracked", "missing", "modified", "added", "removed"]

    STATUSES_FOR_STAGE = ["untracked"]

    STATUSES_FOR_UNSTAGE = ["added"]

    def __init__(self, repo=None):
        self.vcs = rabbitvcs.vcs.VCS_MERCURIAL
        self.interface = "mercurial"

        self.ui = ui.ui()
        self.repository = None
        if repo:
            self.repository_path = repo
            self.repository = hg.repository(self.ui, self.repository_path)

        self.cache = rabbitvcs.vcs.status.StatusCache()

    def set_repository(self, path):
        self.repository_path = path
        self.repository = hg.repository(self.ui, self.repository_path)

    def get_repository(self):
        return self.repository_path

    def find_repository_path(self, path):
        path_to_check = path
        while path_to_check != "/" and path_to_check != "":
            if os.path.isdir(os.path.join(path_to_check, ".hg")):
                return path_to_check

            path_to_check = os.path.split(path_to_check)[0]

        return None

    def get_relative_path(self, path):
        if path == self.repository_path:
            return ""

        return rabbitvcs.vcs.mercurial.util.relativepath(self.repository_path, path)

    def get_absolute_path(self, path):
        return os.path.join(self.repository_path, path).rstrip("/")

    def statuses(self, path, recurse=True, invalidate=False):
        mercurial_statuses = self.repository.status(clean=True, unknown=True)

        # the status method returns a series of tuples filled with files matching
        # the statuses below
        tuple_order = [
            "modified",
            "added",
            "removed",
            "missing",
            "unknown",
            "ignored",
            "clean",
        ]

        # go through each tuple (each of which has a defined status), and
        # generate a flat list of rabbitvcs statuses
        statuses = []
        index = 0
        directories = {}
        for status_tuple in mercurial_statuses:
            content = tuple_order[index]
            for item in status_tuple:
                st_path = self.get_absolute_path(item)

                rabbitvcs_status = rabbitvcs.vcs.status.MercurialStatus(
                    {"path": st_path, "content": content}
                )
                statuses.append(rabbitvcs_status)

                # determine the statuses of the parent folders
                dir_content = content
                if content in self.STATUSES_FOR_REVERT:
                    dir_content = "modified"

                path_to_check = os.path.dirname(st_path)
                while True:
                    if (
                        path_to_check not in directories
                        or directories[path_to_check] not in self.STATUSES_FOR_COMMIT
                    ):
                        rabbitvcs_status = rabbitvcs.vcs.status.MercurialStatus(
                            {"path": path_to_check, "content": dir_content}
                        )
                        statuses.append(rabbitvcs_status)
                        directories[path_to_check] = dir_content

                    if path_to_check == "" or path_to_check == self.repository_path:
                        break

                    path_to_check = os.path.split(path_to_check)[0]

            index += 1

        return statuses

    def status(self, path, summarize=True, invalidate=False):
        all_statuses = self.statuses(path, invalidate=invalidate)

        if summarize:
            path_status = None
            for st in all_statuses:
                if st.path == path:
                    path_status = st
                    break

            if path_status:
                path_status.summary = path_status.single
            else:
                path_status = rabbitvcs.vcs.status.Status.status_unknown(path)
        else:
            path_status = all_statuses[0]

        return path_status

    def is_working_copy(self, path):
        if os.path.isdir(path) and os.path.isdir(os.path.join(path, ".hg")):
            return True
        return False

    def is_in_a_or_a_working_copy(self, path):
        if self.is_working_copy(path):
            return True

        return self.find_repository_path(os.path.split(path)[0]) != ""

    def is_versioned(self, path):
        if self.is_working_copy(path):
            return True

        st = self.status(path)
        try:
            return st.is_versioned()
        except Exception as e:
            log.error(e)
            return False

        return False

    def is_locked(self, path):
        return False

    def get_items(self, paths, statuses=[]):
        if paths is None:
            return []

        items = []
        for path in paths:
            st = self.statuses(path, invalidate=True)
            for st_item in st:
                if st_item.content == "modified" and os.path.isdir(st_item.path):
                    continue

                if st_item.content in statuses or len(statuses) == 0:
                    items.append(st_item)

        return items

    #
    # Actions
    #
