#!/usr/bin/env python3
"""
Generate RST formatted log that can be used as input for the final report.

Run the command with Blender's git path as an argument:

   log_from_git --source /path/to/blender
"""

import sys
import os
import re

sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'modules'))
from git_log import GitCommit, GitCommitIter


def date_to_first_day_of_week(dt, first_day=1):
    from datetime import timedelta
    dt = dt - timedelta(days=(dt.weekday() + first_day) % 7)
    return dt.replace(hour=0, minute=0, second=0)


re_task = re.compile(r'\bT(\d+)\b')
re_diff = re.compile(r'\bD(\d+)\b')


def plain_text_to_rst(body):
    body = re_task.sub(r':task:`\1`', body)
    body = re_diff.sub(r':diff:`\1`', body)
    return body


def ignore_cleanup_commit(c):
    subject = c.subject.lower()
    if subject.startswith(('cleanup:', 'code cleanup:', 'code refactor:')):
        return True
    return False


def rst_write_commit(
        fw, c, rev_prefix, *,
        use_author=False,
        use_date=False,
):
    import textwrap
    subject = c.subject
    body = c.body[len(subject):]
    subject = plain_text_to_rst(subject)
    body = plain_text_to_rst(body)
    fw('- {}\n'.format(subject.rstrip('.:')))
    fw('  :rev:`{:s}{}`\n'.format(rev_prefix, c.sha1.decode('ASCII')))

    body = body.strip('\n')
    if body:
        fw('\n')
        fw(textwrap.indent(body.strip('\n'), '  '))
        fw('\n')

    info = []
    if use_author:
        info.append(c.author)
    if use_date:
        # only for context: YYYY-MM-DD (Day's Name)
        info.append(c.date.strftime('%Y-%m-%d (%a)'))

    if info:
        # Indent so it's easier to jump between blocks (depending on editor features).
        fw('\n  .. ' + ' '.join(info) + '\n')


def argparse_create():
    import argparse

    # When --help or no args are given, print this help
    epilog = 'This script is typically used to help write release notes or weekly reports.'

    parser = argparse.ArgumentParser(description=__doc__, epilog=epilog)

    parser.add_argument(
        '--source', dest='source_dir',
        metavar='PATH',
        required=True,
        help='Path to git repository',
    )
    parser.add_argument(
        '--range', dest='range_sha1',
        metavar='SHA1_RANGE',
        required=False,
        default='HEAD',
        help=(
            'Range to use, eg: 169c95b8..HEAD '
            'or optionally comma separated SHA1 values to log explicit commits.'
        ),
    )
    parser.add_argument(
        '--author', dest='author',
        metavar='AUTHOR',
        type=str,
        required=False,
        help='Optionally match a single author',
    )
    parser.add_argument(
        '--output', dest='output',
        metavar='FILENAME',
        required=False,
        help='Fileneme to write to (instead of default output name)',
    )
    parser.add_argument(
        '--ignore-cleanup', dest='ignore_cleanup',
        action='store_true',
        required=False,
        help='Ignore cleanup & refactor commits',
    )
    parser.add_argument(
        '--quiet', dest='quiet',
        action='store_true',
        required=False,
        help='Do no print any information.',
    )
    parser.add_argument(
        '--rev-prefix', dest='rev_prefix',
        metavar='REV_PREFIX',
        default='B',
        type=str,
        required=False,
        help='The prefix to use for the SHA1',
    )
    return parser


def rst_write_log(
        source_dir, rev_prefix, *,
        output=None,
        range_sha1=None,
        author=None,
        ignore_cleanup=False,
        quiet=False,
        extra_args=(),
):
    if not quiet:
        print('{} ...'.format(output))
    with open(output, 'w', encoding='utf-8') as fh:
        fw = fh.write
        if range_sha1 and (',' in range_sha1):
            commit_iter = (
                c
                for sha1 in range_sha1.split(',')
                for c in GitCommitIter(source_dir, sha1.strip(), extra_args=extra_args + ('-1',))
            )
        else:
            commit_iter = GitCommitIter(source_dir, range_sha1 or 'HEAD', extra_args=extra_args)

        for c in commit_iter:
            if ignore_cleanup and ignore_cleanup_commit(c):
                continue
            if author is None or author == c.author:
                rst_write_commit(fw, c, rev_prefix, use_author=author is None, use_date=True)


def rst_write_log_by_weeks(
        source_dir, rev_prefix, *,
        range_sha1=None,
        ignore_cleanup=False,
        extra_args=(),
):
    weeks_written_max = 3
    weeks_written = 0

    week_start_prev = None
    fh = None
    # All commits from now into the past.
    for c in GitCommitIter(source_dir, range_sha1 or 'HEAD', extra_args=extra_args):
        week_start_date = date_to_first_day_of_week(c.date)
        week_start = week_start_date.timetuple()
        if week_start != week_start_prev:
            if weeks_written == weeks_written_max:
                break
            weeks_written += 1
            week_text = week_start_date.strftime('%Y-%m-%d')
            if fh is not None:
                fh.close()
            fh = open(week_text + '.rst', 'w', encoding='utf-8')
            if not quiet:
                print('{}.rst ...'.format(week_text))
            fw = fh.write

            title = 'Week {}'.format(week_text)
            fw(title + '\n')
            fw(len(title) * '=' + '\n\n')
            week_start_prev = week_start

        if ignore_cleanup and ignore_cleanup_commit(c):
            continue
        rst_write_commit(fw, c, rev_prefix, use_author=True, use_date=True)


def main():
    args = argparse_create().parse_args()

    # Exclude merge commits: https://stackoverflow.com/a/8527200/432509
    extra_args = ('--no-merges', '--first-parent')

    if args.author or (args.range_sha1 and ',' in args.range_sha1):
        # for now write a single log for the author
        rst_write_log(
            args.source_dir,
            args.rev_prefix,
            output=args.output or 'log.rst',
            range_sha1=args.range_sha1,
            author=args.author,
            ignore_cleanup=args.ignore_cleanup,
            quiet=args.quiet,
            extra_args=extra_args,
        )
    else:
        # otherwise split by weeks (default)
        rst_write_log_by_weeks(
            args.source_dir,
            args.rev_prefix,
            range_sha1=args.range_sha1,
            ignore_cleanup=args.ignore_cleanup,
            extra_args=extra_args,
        )


if __name__ == '__main__':
    main()
