#!/usr/bin/env python3
#
# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg
#
# This file is part of Timeline.
#
# Timeline 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.
#
# Timeline 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 Timeline.  If not, see <http://www.gnu.org/licenses/>.


"""
Our Jenkins server is about to be retired.

That means that the Windows installation executable can no longer
be built from Jenkins (calling a build slave).

This script is intended to be run on the local Windows box that
has the Jenkins slave installation to build the Windows installation
executable file.

"""

import sys
import os
import subprocess


class BuildError(Exception):
    pass


def get_build_version_from_input():
    """
    The first argument, if any, is the tagged version t obe built.
    For beta releases, the hg rev number is used.

    >>> get_build_version_from_input()

    >>> sys.argv.insert(1, '2.11.0')
    >>> get_build_version_from_input()
    '2.11.0'

    """
    try:
        return sys.argv[1]
    except IndexError:
        pass


def is_windows():
    return os.name == 'nt'


def hg_command(*args):
    """
    >>> hg_command('showconfig', 'paths.default')
    'http://hg.code.sf.net/p/thetimelineproj/main'

    """
    try:
        if is_windows():
            return subprocess.check_output(['hg'] + list(args)).decode('latin-1').strip()
        else:
            return subprocess.check_output(['hg'] + list(args)).decode().strip()
    except Exception as ex:
        raise BuildError(str(ex))


def os_command(*args):
    try:
        if is_windows():
            return subprocess.check_output(list(args)).decode('latin-1').strip()
        else:
            return subprocess.check_output(list(args)).decode().strip()
    except Exception as ex:
        raise BuildError(str(ex))


def get_hg_default_path():
    return hg_command('showconfig', 'paths.default')


def hg_pull():
    hg_command('pull', '--rev', 'default')


def hg_update():
    hg_command('update', '--clean', '--rev', 'default')


def hg_purge():
    hg_command('--config', 'extensions.purge=', 'clean', '--all')


def get_hg_node():
    return hg_command('log', '--rev', '.', '--template', '{node}')


def get_hg_rev():
    return hg_command('log', '--rev', '.', '--template', '{rev}')


def verify_version(build_version, node):
    """
    >>> verify_version('2.10.0', 'a821cfd8cbe1f28ac4d01ffe02746632161db27b')
    'exists'

    >>> verify_version(None, 'a821cfd8cbe1f28ac4d01ffe02746632161db27b')
    'exists'

    >>> verify_version(None, 'abcd')
    Traceback (most recent call last):
        ...
    jenkins-build-local.BuildError: unknown revision: abcd!
    """
    try:
        return hg_command('log', '--rev', build_version or node, '--template', 'exists')
    except:
        raise BuildError(f'unknown revision: {build_version or node}!')


def log_changesets(build_revision, node):
    return hg_command(
        'log',
        '--template',
        "<changeset node='{node}' author='{author|xmlescape}' rev='{rev}' date='{date}'><msg>{desc|xmlescape}</msg>{file_adds % '<addedFile>{file|xmlescape}</addedFile>'}{file_dels % '<deletedFile>{file|xmlescape}</deletedFile>'}{files % '<file>{file|xmlescape}</file>'}<parents>{parents}</parents></changeset>\n",
        '--rev',
        f"ancestors('default') and not ancestors({build_revision or node})",
        '--encoding',
        'UTF-8',
        '--encodingmode',
        'replace')


def upgrade_pip():
    os_command('python.exe', '-m', 'pip', 'install', '--upgrade', 'pip')


def display_python_version():
    log_subheader('Python Version')
    log(os_command('python', '-V'))


def create_virtual_environment():
    log_subheader('Crate virtual environment')
    log(os_command('python', '-m', 'venv', 'venv'))


def upgrade_pip():
    log_subheader('Upgrade pip')
    os.system('venv\\Scripts\\python -m pip install --upgrade pip')


def install_wxpython():
    log_subheader('Install wwPython')
    os_command('venv\\Scripts\\pip', 'install', '--force-reinstall', '-v', 'wxPython==4.1.1')


def install_humblewx():
    log_subheader('Install humblewx')
    os_command('venv\Scripts\\pip', 'install', 'humblewx')


def install_icalendar():
    log_subheader('Install icalendar')
    os_command('venv\\Scripts\\pip', 'install', 'icalendar')


def install_markdown():
    log_subheader('Install Markdown')
    os_command('venv\\Scripts\\pip', 'install', 'Markdown')


def run_tests():
    log_subheader('Run Tests')
    os_command('venv\\Scripts\\python', 'tools/execute-specs.py', '--write-testlist', 'testlist.txt')


def build(build_version, node):
    log_subheader(f'Start Build {build_version or node}')

    os.chdir('tools')
    os.system('..\\venv\\Scripts\python generate-mo-files.py')
    log('mo file generated')

    os.chdir('winbuildtools')
    os.system('..\\..\\venv\\Scripts\python mod_paths.py .')
    log('paths modified')

    os.system(f'..\\..\\venv\\Scripts\python mod_iss_timeline_version.py . {build_version or node}')
    log('version file modified')

    log_subheader('Remove old build directory')
    os.system('rmdir /S /Q build >> nul')
    log_subheader('Remove old dist directory')
    os.system('rmdir /S /Q dist >> nul')
    log_subheader('Remove old out directory')
    os.system('del /S /Q inno\out >> nul')

    log_subheader('pyinstaller')
    os.system('pyinstaller timeline.spec')

    log_subheader('Copying icons and translations to dist')
    os.system('mkdir .\\dist\\icons\\event_icons')
    os.system('mkdir .\\dist\\translations')
    os.system('xcopy /S ..\\..\\icons\*.*  .\dist\icons\*.*')
    os.system('xcopy /S ..\\..\\translations\ca\*.*  .\dist\\translations\ca\*.*')
    os.system('xcopy /S ..\\..\\translations\de\*.*  .\dist\\translations\de\*.*')
    os.system('xcopy /S ..\\..\\translations\el\*.*  .\dist\\translations\el\*.*')
    os.system('xcopy /S ..\\..\\translations\es\*.*  .\dist\\translations\es\*.*')
    os.system('xcopy /S ..\\..\\translations\eu\*.*  .\dist\\translations\eu\*.*')
    os.system('xcopy /S ..\\..\\translations\fr\*.*  .\dist\\translations\fr\*.*')
    os.system('xcopy /S ..\\..\\translations\gl\*.*  .\dist\\translations\gl\*.*')
    os.system('xcopy /S ..\\..\\translations\he\*.*  .\dist\\translations\he\*.*')
    os.system('xcopy /S ..\\..\\translations\it\*.*  .\dist\\translations\it\*.*')
    os.system('xcopy /S ..\\..\\translations\ko\*.*  .\dist\\translations\ko\*.*')
    os.system('xcopy /S ..\\..\\translations\lt\*.*  .\dist\\translations\lt\*.*')
    os.system('xcopy /S ..\\..\\translations\nl\*.*  .\dist\\translations\nl\*.*')
    os.system('xcopy /S ..\\..\\translations\pl\*.*  .\dist\\translations\pl\*.*')
    os.system('xcopy /S ..\\..\\translations\pt\*.*  .\dist\\translations\pt\*.*')
    os.system('xcopy /S ..\\..\\translations\pt_BR\*.*  .\dist\\translations\pt_BR\*.*')
    os.system('xcopy /S ..\\..\\translations\ru\*.*  .\dist\\translations\ru\*.*')
    os.system('xcopy /S ..\\..\\translations\sv\*.*  .\dist\\translations\sv\*.*')
    os.system('xcopy /S ..\\..\\translations\tr\*.*  .\dist\\translations\tr\*.*')
    os.system('xcopy /S ..\\..\\translations\vi\*.*  .\dist\\translations\vi\*.*')
    os.system('xcopy /S ..\\..\\translations\zh_CH\*.*  .\dist\\translations\zh_CH\*.*')

    log('Create distributable')
    os.chdir('inno')
    os.system('iscc.exe timeline2Win32.iss')
    log_subheader('DONE')


def log_header():
    print('-' * 70)
    print(' jenkins-build-local.py')
    print('-' * 70)


def log_subheader(text):
    print(f'----({text})------')


def log(text):
    print(f'  {text}')


def main():
    log_header()
    hg_pull()
    hg_update()
    hg_purge()
    build_version = get_build_version_from_input()
    default_path = get_hg_default_path()
    node = get_hg_node()
    revision = get_hg_rev()
    log(f'Build version: {build_version}')
    log(f'Defult Path  : {default_path}')
    log(f'Node         : {node}')
    log(f'Revision     : {revision}')

    log_subheader('Verify version')
    verify_version(build_version, node)
    log('Verified that revision exists')

    log_subheader('Log changesets')
    changesets = log_changesets(build_version, node)
    print(changesets)

    display_python_version()
    create_virtual_environment()
    upgrade_pip()

    install_wxpython()
    install_humblewx()
    install_icalendar()
    install_markdown()
    run_tests()

    os.system('venv\\Scripts\pip list')

    build(build_version, node)


if __name__ == '__main__':
    if 'doctest' in sys.argv:
        import doctest

        sys.argv = sys.argv[:-1]
        doctest.testmod()
    else:
        try:
            main()
        except BuildError as ex:
            log('Build aborted')
            log(ex)
