#!python

# Python script to build/clean containers associated for a project
#

from __future__ import print_function
import argparse
import os
import shlex
import subprocess
import sys
from dockerutils import *
if sys.version_info < (3, 0):
    from ConfigParser import ConfigParser
else:
    from configparser import ConfigParser


def is_multistage(mode):
    return 'as builder' in open('docker/{mode}/Dockerfile'.format(mode=mode)).read()


def run_pre_script(script):
    print('Running pre-build-script: "{script}"'.format(script=script))
    return subprocess.call(shlex.split(script), cwd=os.getcwd())


def run_post_script(script):
    print('Running post-build-script: "{script}"'.format(script=script))
    return subprocess.call(shlex.split(script), cwd=os.getcwd())


def build(image, image_name, image_tag, config, pull=False):
    pre_script = config.get(image, 'pre_build_script') if config.has_option(image, 'pre_build_script') else None
    post_script = config.get(image, 'post_build_script') if config.has_option(image, 'post_build_script') else None

    if pre_script:
        rc = run_pre_script(pre_script)
        if rc != 0:
            print('pre-build-script failed: {rc}'.format(rc=rc))
            return rc

    rc = 0
    pull_base = ''
    args = config.get(image, 'build_args') if config.has_option(image, 'build_args') else ''

    if pull:
        pull_base = '--pull'
    if is_multistage(image):
        # if this is a multistage build and it follows the conventions, tag the builder image
        # otherwise, a prune will remove the layers used during the builder phase and subsequent
        # builds will take longer than required
        rc = image_operation(
            'docker build {pull_base} --compress -t {image_name}-builder:{image_tag} -f docker/{image}/Dockerfile --target builder {args} .'
                .format(pull_base=pull_base, image_name=image_name, image=image, image_tag=image_tag, args=args))
    if not rc:
        rc = image_operation(
            'docker build {pull_base} --compress -t {image_name}:{image_tag} -f docker/{image}/Dockerfile {args} .'
                .format(pull_base=pull_base, image_name=image_name, image=image, image_tag=image_tag, args=args))

    if rc != 0:
        print('docker build failed: {rc}'.format(rc=rc))
        return rc

    if post_script:
        rc = run_post_script(post_script)

    return rc


def clean(image, image_name, image_tag, pull=False):
    rc = image_operation('docker rmi {image_name}:{image_tag}'.format(image_name=image_name, image_tag=image_tag))
    if is_multistage(image):
        image_operation('docker rmi {image_name}-builder:{image_tag}'.format(image_name=image_name, image_tag=image_tag))
    return rc


def image_operation(operation):
    print('\n\n============================================================================')
    print('{operation}\n\n'.format(operation=operation))
    return subprocess.call(shlex.split(operation), cwd=os.getcwd())


if __name__ == '__main__':
    try:
        root_dir = get_root_dir()
    except ValueError as e:
        print(str(e))
        sys.exit(1)
    with cd(root_dir):
        config = ConfigParser()
        config.optionxform = str
        config.read(os.path.join('docker', 'dockerutils.cfg'))

        if os.path.isfile('setup.cfg'):
            config_versioneer = ConfigParser()
            config_versioneer.optionxform = str
            config_versioneer.read('setup.cfg')
            if config_versioneer.has_section('versioneer'):
                gen_version_file()

        with pip_conf(root_dir):
            image_types = get_image_types()

            parser = argparse.ArgumentParser()
            parser.add_argument("image", choices=['all', 'clean'] + image_types, help="image to build")
            parser.add_argument("-f", "--force_build_base", help="Build the base along with the requested image",
                                action='store_true')
            parser.add_argument("-i", "--image_name", help="use this image name rather than the default")
            parser.add_argument("-t", "--image_tag", help="use this image tag rather than the default")
            parser.add_argument("-p", "--pull_base", help="pull the base image as part fo the build",
                                action='store_true')
            args = parser.parse_args()

            images_to_build = []
            fn = build
            if args.image == 'all':
                images_to_build = image_types
            elif args.image == 'clean':
                images_to_build = image_types
                fn = clean
            else:
                images_to_build = []
                if not args.image == 'base' and args.force_build_base and 'base' in image_types:
                    images_to_build.append('base')
                images_to_build.append(args.image)

            for image in images_to_build:
                image_name, image_tag = get_image_designation(image, config)
                if args.image_name:
                    image_name = args.image_name
                if args.image_tag:
                    image_tag = args.image_tag

                pull_FROM_on_force = False
                image_config = {}
                if config.has_section(image) and config.has_option(image, 'pull_FROM_on_force'):
                    pull_FROM_on_force = config.get(image, 'pull_FROM_on_force')

                if os.path.isfile('docker/{image}/Dockerfile'.format(image=image)):
                    rc = fn(image, image_name, image_tag, config=config,
                            pull=args.pull_base or (args.force_build_base and pull_FROM_on_force))
                    # because an image may not be present on the clean, ignore a non-zero return code
                    if rc and not args.image == 'clean':
                        sys.exit(rc)
    sys.exit(0)
