#!/usr/bin/env python3
#
# $NetBSD$
#
# Copyright (c) 2023 The NetBSD Foundation, Inc.
# All rights reserved.
#
# This code is derived from software contributed to The NetBSD Foundation
# by Thomas Klausner.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#

import argparse
import os
from pathlib import Path
import subprocess


def file_path(path):
    '''Check if argument is a valid file path.'''
    if Path(path).is_file():
        return str(Path(path).resolve())
    raise argparse.ArgumentTypeError(f"{path} is not a valid file path")


def dir_path(path):
    '''Check if argument is a valid directory path.'''
    if Path(path).is_dir():
        return str(Path(path).resolve())
    raise argparse.ArgumentTypeError(f"{path} is not a valid directory path")


parser = argparse.ArgumentParser(description='Convert CVS repository ' +
                                 'to Mercurial')
parser.add_argument('-a', dest='authormap', type=file_path,
                    help='use %(dest)s to map UNIX logins to names ' +
                    'and email addresses')
parser.add_argument('-b', dest='branchmap', type=file_path,
                    help='use %(dest)s to rename branches')
parser.add_argument('-f', dest='fixup_sql_script', type=file_path,
                    help='run %(dest)s on fossil database to fix problems')
parser.add_argument('-s', dest='merge_limit', type=int,
                    help='pass %(dest)s to cvs2fossil ' +
                    '(merge window time in seconds)')
parser.add_argument('source', type=dir_path,
                    help='source CVS repository master')
# may not exist yet, can't use dir_path
parser.add_argument('destination', type=str,
                    help='base name of target Mercurial repository')
args = parser.parse_args()

run_args = ['cvs2fossil', '-m']
if args.fixup_sql_script:
    run_args += ['-f', args.fixup_sql_script]
if args.merge_limit:
    run_args += ['-s', str(args.merge_limit)]
run_args += [args.source, args.destination]

# convert to fossil using cvs2fossil
subprocess.run(run_args, check=True)
# cvs2fossil creates {args.destination} and {args.destination}.fossil,
# but we don't need the former
os.remove(args.destination)
# export from fossil
with open(f'{args.destination}.fossil.export', 'wb') as output:
    subprocess.run(['fossil1', 'export', '-R', f'{args.destination}.fossil'],
                   stdout=output, check=True)
# import to hg
subprocess.run(['hg', 'init', f'{args.destination}.hg'], check=True)
subprocess.run(['hg', '-R', f'{args.destination}.hg', '--config',
                'extensions.fastimport=', 'fastimport',
                f'{args.destination}.fossil.export'], check=True)
# fix author or branch names
if args.authormap or args.branchmap:
    run_args = ['hg', 'convert']
    if args.authormap:
        run_args.append(f'--authormap={args.authormap}')
    if args.branchmap:
        run_args.append(f'--branchmap={args.branchmap}')
    run_args += [f'{args.destination}.hg', f'{args.destination}.rewrite.hg']
    subprocess.run(run_args, check=True)
    os.rename(f'{args.destination}.hg', f'{args.destination}.hg.old')
    os.rename(f'{args.destination}.rewrite.hg', f'{args.destination}.hg')
