# -*- coding: utf-8 -*-

#  Copyright © 2013  B. Clausius <barcc@gmx.de>
#
#  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 3 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, see <http://www.gnu.org/licenses/>.

# Code in this file is from the bzr-gtk project <https://launchpad.net/bzr-gtk>:
#    Branch: lp:bzr-gtk
#    Revision: 795
#    Filename: commit.py
#    Copyright © 2006  by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
#    License: GPL-2+

import re

from bzrlib import (
    #bencode,
    errors,
    osutils,
    trace,
    tsort,
    bzrdir,
    transport,
    )
from bzrlib.revision import NULL_REVISION


def _get_sorted_revisions(tip_revision, revision_ids, parent_map):
    """Get an iterator which will return the revisions in merge sorted order.

    This will build up a list of all nodes, such that only nodes in the list
    are referenced. It then uses MergeSorter to return them in 'merge-sorted'
    order.

    :param revision_ids: A set of revision_ids
    :param parent_map: The parent information for each node. Revisions which
        are considered ghosts should not be present in the map.
    :return: iterator from MergeSorter.iter_topo_order()
    """
    # MergeSorter requires that all nodes be present in the graph, so get rid
    # of any references pointing outside of this graph.
    parent_graph = {}
    for revision_id in revision_ids:
        if revision_id not in parent_map: # ghost
            parent_graph[revision_id] = []
        else:
            # Only include parents which are in this sub-graph
            parent_graph[revision_id] = [p for p in parent_map[revision_id]
                                            if p in revision_ids]
    sorter = tsort.MergeSorter(parent_graph, tip_revision)
    return sorter.iter_topo_order()


def _rev_to_pending_info(rev):
    date = osutils.format_date(rev.timestamp, rev.timezone or 0,
                               'original', date_fmt="%Y-%m-%d", show_offset=False)
    committer = re.sub('<.*@.*>', '', rev.committer).strip(' ')
    return [rev.revision_id, date, committer, rev.get_summary()]
    
def pending_revisions(wt):
    """Return a list of pending merges or None if there are none of them.

    Arguably this should be a core function, and
    ``bzrlib.status.show_pending_merges`` should be built on top of it.

    :return: [(rev, [children])]
    """
    parents = wt.get_parent_ids()
    if len(parents) < 2:
        return

    # The basic pending merge algorithm uses the same algorithm as
    # bzrlib.status.show_pending_merges
    pending = parents[1:]
    branch = wt.branch
    last_revision = parents[0]

    graph = branch.repository.get_graph()
    other_revisions = [last_revision]

    pm = []
    for merge in pending:
        try:
            merge_rev = branch.repository.get_revision(merge)
        except errors.NoSuchRevision:
            # If we are missing a revision, just print out the revision id
            trace.mutter("ghost: %r", merge)
            other_revisions.append(merge)
            continue
        merge_rev = _rev_to_pending_info(merge_rev)

        # Find all of the revisions in the merge source, which are not in the
        # last committed revision.
        merge_extra = graph.find_unique_ancestors(merge, other_revisions)
        other_revisions.append(merge)
        merge_extra.discard(NULL_REVISION)

        # Get a handle to all of the revisions we will need
        try:
            revisions = dict((rev.revision_id, rev) for rev in
                             branch.repository.get_revisions(merge_extra))
        except errors.NoSuchRevision:
            # One of the sub nodes is a ghost, check each one
            revisions = {}
            for revision_id in merge_extra:
                try:
                    rev = branch.repository.get_revisions([revision_id])[0]
                except errors.NoSuchRevision:
                    revisions[revision_id] = None
                else:
                    revisions[revision_id] = rev

         # Display the revisions brought in by this merge.
        rev_id_iterator = _get_sorted_revisions(merge, merge_extra,
                            branch.repository.get_parent_map(merge_extra))
        # Skip the first node
        num, first, depth, eom = rev_id_iterator.next()
        if first != merge:
            raise AssertionError('Somehow we misunderstood how'
                ' iter_topo_order works %s != %s' % (first, merge))
        children = []
        for num, sub_merge, depth, eom in rev_id_iterator:
            rev = revisions[sub_merge]
            if rev is None:
                trace.warning("ghost: %r", sub_merge)
                continue
            rev = _rev_to_pending_info(rev)
            children.append(rev)
        yield (merge_rev, children)
        
def init_repository(location):
    try:
        # compare the source of bzrlib.builtins.cmd_init_repository.run
        format = bzrdir.format_registry.make_bzrdir('default')
        
        to_transport = transport.get_transport(location)
        
        newdir = format.initialize_on_transport(to_transport)
        repo = newdir.create_repository(shared=True)
        repo.set_make_working_trees(True)
    except errors.BzrError as e:
        return str(e)
    return None
    
def init_branch(location):
    try:
        # compare the source of bzrlib.builtins.cmd_init.run
        format = bzrdir.format_registry.make_bzrdir('default')
        
        to_transport = transport.get_transport(location)
        
        try:
            a_bzrdir = bzrdir.BzrDir.open_from_transport(to_transport)
        except errors.NotBranchError:
            # really a NotBzrDir error...
            create_branch = bzrdir.BzrDir.create_branch_convenience
            branch = create_branch(to_transport.base, format=format,
                                   possible_transports=[to_transport],
                                   force_new_tree=None)
            a_bzrdir = branch.bzrdir
        else:
            from bzrlib.transport.local import LocalTransport
            if a_bzrdir.has_branch():
                if (isinstance(to_transport, LocalTransport)
                    and not a_bzrdir.has_workingtree()):
                        raise errors.BranchExistsWithoutWorkingTree(location)
                raise errors.AlreadyBranchError(location)
            branch = a_bzrdir.create_branch()
            a_bzrdir.create_workingtree()
    except errors.BzrError as e:
        return str(e)
    return None

