# Copyright (C) 2005-2011 Canonical Ltd
#
# This program 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.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

from __future__ import absolute_import

import itertools

from . import (
    bzrdir,
    )
from .. import (
    errors,
    lockdir,
    lockable_files,
    revision as _mod_revision,
    )
from ..decorators import only_raises
from ..repository import (
    format_registry,
    Repository,
    RepositoryFormat,
    )
from ..sixish import (
    viewvalues,
    )


class MetaDirRepository(Repository):
    """Repositories in the new meta-dir layout.

    :ivar _transport: Transport for access to repository control files,
        typically pointing to .bzr/repository.
    """

    def __init__(self, _format, a_bzrdir, control_files):
        super(MetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
        self._transport = control_files._transport

    def is_shared(self):
        """Return True if this repository is flagged as a shared repository."""
        return self._transport.has('shared-storage')

    def set_make_working_trees(self, new_value):
        """Set the policy flag for making working trees when creating branches.

        This only applies to branches that use this repository.

        The default is 'True'.
        :param new_value: True to restore the default, False to disable making
                          working trees.
        """
        with self.lock_write():
            if new_value:
                try:
                    self._transport.delete('no-working-trees')
                except errors.NoSuchFile:
                    pass
            else:
                self._transport.put_bytes(
                        'no-working-trees', '',
                        mode=self.controldir._get_file_mode())

    def make_working_trees(self):
        """Returns the policy for making working trees on new branches."""
        return not self._transport.has('no-working-trees')

    def update_feature_flags(self, updated_flags):
        """Update the feature flags for this branch.

        :param updated_flags: Dictionary mapping feature names to necessities
            A necessity can be None to indicate the feature should be removed
        """
        with self.lock_write():
            self._format._update_feature_flags(updated_flags)
            self.control_transport.put_bytes('format', self._format.as_string())

    def _find_parent_ids_of_revisions(self, revision_ids):
        """Find all parent ids that are mentioned in the revision graph.

        :return: set of revisions that are parents of revision_ids which are
            not part of revision_ids themselves
        """
        parent_ids = set(itertools.chain.from_iterable(viewvalues(
                self.get_parent_map(revision_ids))))
        parent_ids.difference_update(revision_ids)
        parent_ids.discard(_mod_revision.NULL_REVISION)
        return parent_ids


class RepositoryFormatMetaDir(bzrdir.BzrFormat, RepositoryFormat):
    """Common base class for the new repositories using the metadir layout."""

    rich_root_data = False
    supports_tree_reference = False
    supports_external_lookups = False
    supports_leaving_lock = True
    supports_nesting_repositories = True

    @property
    def _matchingcontroldir(self):
        matching = bzrdir.BzrDirMetaFormat1()
        matching.repository_format = self
        return matching

    def __init__(self):
        RepositoryFormat.__init__(self)
        bzrdir.BzrFormat.__init__(self)

    def _create_control_files(self, a_bzrdir):
        """Create the required files and the initial control_files object."""
        # FIXME: RBC 20060125 don't peek under the covers
        # NB: no need to escape relative paths that are url safe.
        repository_transport = a_bzrdir.get_repository_transport(self)
        control_files = lockable_files.LockableFiles(repository_transport,
                                'lock', lockdir.LockDir)
        control_files.create_lock()
        return control_files

    def _upload_blank_content(self, a_bzrdir, dirs, files, utf8_files, shared):
        """Upload the initial blank content."""
        control_files = self._create_control_files(a_bzrdir)
        control_files.lock_write()
        transport = control_files._transport
        if shared == True:
            utf8_files += [('shared-storage', b'')]
        try:
            transport.mkdir_multi(dirs, mode=a_bzrdir._get_dir_mode())
            for (filename, content_stream) in files:
                transport.put_file(filename, content_stream,
                    mode=a_bzrdir._get_file_mode())
            for (filename, content_bytes) in utf8_files:
                transport.put_bytes_non_atomic(filename, content_bytes,
                    mode=a_bzrdir._get_file_mode())
        finally:
            control_files.unlock()

    @classmethod
    def find_format(klass, a_bzrdir):
        """Return the format for the repository object in a_bzrdir.

        This is used by brz native formats that have a "format" file in
        the repository.  Other methods may be used by different types of
        control directory.
        """
        try:
            transport = a_bzrdir.get_repository_transport(None)
            format_string = transport.get_bytes("format")
            # GZ 2017-06-17: Where should format strings get decoded...
            format_text = format_string.decode("ascii")
        except errors.NoSuchFile:
            raise errors.NoRepositoryPresent(a_bzrdir)
        return klass._find_format(format_registry, 'repository', format_text)

    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
            basedir=None):
        RepositoryFormat.check_support_status(self,
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
            basedir=basedir)
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
            recommend_upgrade=recommend_upgrade, basedir=basedir)

