# coding: utf8
# Debian Contributors data source data mining tools
#
# Copyright (C) 2013--2014  Enrico Zini <enrico@debian.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from .. import scanner
from ..types import *
import datetime
import logging

log = logging.getLogger(__name__)

__all__ = ["GitDirs", "GitLogs"]

class GitDirs(scanner.Scanner):
    """
    Scan git directories using file attributes to detect contributions.

    Generates `login` types of identifiers, using the usernames of the system
    where it is run.

    Example::

        contribution: committer
        method: gitdirs
        dirs: /srv/git.debian.org/git/collab-maint/*.git
        url: https://alioth.debian.org/users/{user}/
    """
    dirs = scanner.GlobField(blank=False, help_text="""
                             ``.git`` directories to scan. You can give one or more,
                             and even use shell-style globbing.
                             """)
    url = scanner.CharField(help_text="""
                            template used to build URLs to link to people's contributions.
                            ``{user}`` will be replaced with the username
                            """)

    def scan(self):
        from .utils.filesystem import Filesystem

        scan = Filesystem()
        for d in self.dirs:
            scan.scan_git_repo(d)
        if self.url:
            tpl = self.url
            for ident, begin, end in scan.contributions():
                yield ident, begin, end, tpl.format(user=ident.id)
        else:
            for ident, begin, end in scan.contributions():
                yield ident, begin, end, None


class GitLogs(scanner.Scanner):
    """
    Scan git logs, taking note of committer and author activity

    Generates `email` types of identifiers, trusting whatever is in the git
    log.

    Example::

        contribution: committer
        method: gitlogs
        dirs: /srv/git.debian.org/git/collab-maint/*.git
    """
    dirs = scanner.GlobField(blank=False, help_text="""
                             ``.git`` directories to scan. You can give one or more,
                             and even use shell-style globbing.
                             """)
    subdir = scanner.CharField(blank=True, help_text="""
                             Limit the scan to subdirectories in the repository.
                             """)
    author_map = scanner.IdentMapField(blank=True, help_text="""
                             Convert author emails using the given expressions
                             """)
    url = scanner.CharField(help_text="""
                            template used to build URLs to link to people's contributions.
                            ``{email}`` will be replaced with the email address.
                            """)

    def scan(self):
        from .utils.proc import stream_command_stdout
        from .utils.mine import Aggregate

        contribs = Aggregate()
        for d in self.dirs:
            try:
                cmd = ["git", "--git-dir", d, "log", "--all", "--pretty=tformat:%ae%x00%at%x00%ce%x00%ct"]
                if self.subdir:
                    cmd += ["--", self.subdir]
                for line in stream_command_stdout(cmd):
                    fields = line.split("\0")
                    if len(fields) != 4:
                        log.warn("Invalid git output in %s: %s", d, line.strip())
                        break
                    ae, at, ce, ct = fields
                    contribs.add(ae, int(at))
                    contribs.add(ce, int(ct))
            except RuntimeError as e:
                log.warn("Git failed in %s: %s", d, str(e))

        author_map = self.author_map

        for email, (begin, end) in contribs.items():
            for match, repl in author_map:
                email = match.sub(repl, email)
            ident = Identifier("email", email)
            begin = datetime.date.fromtimestamp(begin)
            end = datetime.date.fromtimestamp(end)
            if self.url:
                yield ident, begin, end, self.url.format(email=email)
            else:
                yield ident, begin, end, None
