#!/usr/bin/env python3
#
# Install source packages to ~/opt/local.
#
# This script assumes that all source package dependencies are already
# installed.

import sys
import os
import subprocess
import filecmp
import shutil
import distutils.spawn

b2 = distutils.spawn.find_executable('b2')
if not b2:
    b2 = distutils.spawn.find_executable('bjam')
if not b2:
    print('warning: Boost.Build was not found, skipping source packages that require it.')


def emulate_backtick(args):
    try:
        output = subprocess.check_output(args, universal_newlines=True).rstrip('\n')
    except Exception:
        output = ''

    return output


def git_clone_update_pull_ff_only(url, directory):
    """Clone, update, and pull --ff-only the given url to the given
    directory.

    """
    if not os.path.exists(directory):
        command = ['git', 'clone', '--recurse-submodules', url, directory]
        subprocess.check_call(command)

    command = ['git', '-C', directory, 'pull', '--ff-only', '--recurse-submodules', '--prune', '--all']
    subprocess.check_call(command)
    command = ['git', '-C', directory, 'submodule', 'init']
    subprocess.check_call(command)
    command = ['git', '-C', directory, 'submodule', 'update', '--init', '--recursive']
    subprocess.check_call(command)


def install_tee3_c_style(basedir):
    git_clone_update_pull_ff_only(
        'https://github.com/tee3/tee3-c-style.git',
        os.path.join(basedir, 'src', 'tee3-c-style'))


def install_msvc_c_style(basedir):
    git_clone_update_pull_ff_only(
        'https://github.com/tee3/msvc-c-style.git',
        os.path.join(basedir, 'src', 'msvc-c-style'))


def install_b2_mode(basedir):
    git_clone_update_pull_ff_only(
        'https://github.com/tee3/b2-mode.git',
        os.path.join(basedir, 'src', 'b2-mode'))


def install_objj_mode(basedir):
    git_clone_update_pull_ff_only(
        'https://github.com/tee3/objj-mode.git',
        os.path.join(basedir, 'src', 'objj-mode'))


def install_git_svn_update_externals(basedir):
    git_clone_update_pull_ff_only(
        'https://github.com/tee3/git-svn-update-externals.git',
        os.path.join(basedir, 'src', 'git-svn-update-externals'))

    source = os.path.join(basedir, 'src', 'git-svn-update-externals', 'git-svn-update-externals')
    target = os.path.join(basedir, 'bin', 'git-svn-update-externals')

    if os.path.exists(target):
        if not filecmp.cmp(target, source, False):
            yn = input('%s is different than %s.  Overwrite anyway? (y/n): ' % (source, target))
            if yn == 'y':
                os.remove(target)

    if not os.path.exists(target):
        if hasattr(os, 'symlink'):
            os.symlink(source, target)
        else:
            shutil.copyfile(source, target)


def install_boost(basedir):
    boostdir = os.path.join(basedir, 'src', 'boost', 'boost.git')

    git_clone_update_pull_ff_only(
        'https://github.com/boostorg/boost.git',
        boostdir)

    command = ['git', '-C', boostdir, 'clean', '-d', '-x', '-f']
    subprocess.check_call(command)

    if sys.platform.startswith('win32'):
        command = [os.path.join(boostdir, 'bootstrap.bat')]
    else:
        command = [os.path.join(boostdir, 'bootstrap.sh')]
    subprocess.check_call(command, cwd=boostdir)


def install_commands_to_compilation_database(basedir):
    if not b2:
        return

    sourcedir = os.path.join(basedir, 'src', 'commands_to_compilation_database')

    git_clone_update_pull_ff_only(
        'https://github.com/tee3/commands_to_compilation_database.git',
        sourcedir)

    command = [
        b2,
        'install',
        '--prefix=' + basedir
    ]
    subprocess.check_call(command, cwd=sourcedir)


def install_tree_sitter(basedir):
    sourcedir = os.path.join(basedir, 'src', 'tree-sitter')

    major = 0
    minor = 20
    patch = 7

    version = f'{major}.{minor}.{patch}'

    if os.path.exists(sourcedir):
        command = [
            'git',
            'checkout',
            'master',
        ]
        subprocess.check_call(command, cwd=sourcedir)

    git_clone_update_pull_ff_only(
        'https://github.com/tree-sitter/tree-sitter.git',
        sourcedir)

    command = [
        'git',
        'checkout',
        f'v{version}',
    ]
    subprocess.check_call(command, cwd=sourcedir)

    command = [
        'make',
        f'VERSION={version}'
        f'SONAME_MAJOR={major}',
        f'SONAME_MINOR={minor}',
        'install',
    ]
    env = os.environ
    env['PREFIX'] = basedir
    subprocess.check_call(command, cwd=sourcedir, env=env)


# requires HOME.
if 'HOME' not in os.environ or os.environ['HOME'] == '':
    print('error: HOME is not set, aborting.')
    sys.exit(1)
HOME = os.environ['HOME']

# default the prefix to ~/opt/local
prefix = os.path.join(HOME, 'opt', 'local')

if __name__ == '__main__':
    r = 0

    # install desired source packages
    install_tee3_c_style(prefix)
    install_msvc_c_style(prefix)
    install_b2_mode(prefix)
    install_objj_mode(prefix)
    install_commands_to_compilation_database(prefix)
    install_git_svn_update_externals(prefix)
    install_boost(prefix)
    install_tree_sitter(prefix)

    sys.exit(r)
