#!/usr/bin/env python

'''
Build a distribution ISO.

Typical usage:
    qemu-img create -f qcow2 ~/disk.img 2G
    ./make-dist.py ~/TempleOSCD-v502.ISO Shrine ~/disk.img


The process is mostly automated, though at one point it's necessary
to press "1" to select the boot drive.

This works by binary-patching the TempleOS dist ISO to enter a reduced
version of MFA immediately upon start-up. (see inject_templeos.py)
The OS installation script is also modified so no user input is required.

After booting into the installed OS, distribution install script is run,
which will typically include building and exporting a distribution ISO.
'''

from __future__ import print_function

import argparse
import os
import shutil
import subprocess
import sys
import time

parser = argparse.ArgumentParser()
parser.add_argument('install_iso')
parser.add_argument('distro_dir')
parser.add_argument('disk_img')
parser.add_argument('--skip-tos-install', dest='skip_tos_install',
        action='store_true')
args = parser.parse_args()

INSTALL_ISO = args.install_iso
DISTRO_DIR = args.distro_dir
DISK_IMG = args.disk_img

MEM_SIZE = 512
MFA_PORT = 7770
SNAIL_PORT = 7777
QEMU_START_DELAY = 3

SLAVE_HC_Z = 'AutoOSInstall/SlaveOnce.HC.Z'

AUTO_INSTALL_PATH = 'AutoOSInstall'
AUTO_INSTALL_TIMEOUT = 300

PATCHED_ISO = 'TempleSlave.iso'

QEMU_COMMAND = [
        'qemu-system-x86_64',
        '-hda', DISK_IMG,
        '-machine', 'kernel_irqchip=off',
        '-smp', 'cores=1',
        '-m', str(MEM_SIZE),
        '-rtc', 'base=localtime',
        '-soundhw', 'pcspk',
        '-serial', 'tcp::%d,server' % MFA_PORT]

ENABLE_KVM = True

if ENABLE_KVM:
    QEMU_COMMAND += ['-enable-kvm', '-cpu', 'host']

SELF_DIR = os.path.dirname(os.path.abspath(__file__))
INJECT_BIN = os.path.join(SELF_DIR, 'inject_templeos.py')
MFA_BIN = os.path.join(SELF_DIR, 'mfa.py')
SNAIL_BIN = os.path.join(SELF_DIR, 'snail.py')

def wait_for_subprocess(subpr, timeout):
    while timeout >= 0:
        if subpr.poll() is not None:
            return

        time.sleep(1)
        timeout -= 1

    raise Exception('Subprocess didn\'t finish in time')

def run_qemu_and_mfa(qemu_command, mfa_script, timeout, with_snail=False):
    if with_snail:
        qemu_command = qemu_command + ['-serial', 'null',
                                       '-serial', 'tcp::%d,server' % SNAIL_PORT]

    qemu = subprocess.Popen(qemu_command)
    time.sleep(QEMU_START_DELAY)

    if qemu.poll() is not None:
        raise Exception('QEMU failed to start')

    snail = None

    try:
        with open(mfa_script, 'rb') as script:
            mfa = subprocess.Popen([MFA_BIN], stdin=script)

            if with_snail:
                time.sleep(3)
                snail = subprocess.Popen([SNAIL_BIN])

            wait_for_subprocess(mfa, timeout)

            if mfa.returncode != 0:
                raise Exception('MFA script failed')
    finally:
        if snail: snail.kill()

        qemu.terminate()
        time.sleep(5)
        qemu.kill()

        wait_for_subprocess(qemu, 10)

if not args.skip_tos_install:
    shutil.copy(INSTALL_ISO, PATCHED_ISO)
    subprocess.check_call([INJECT_BIN, PATCHED_ISO, 'Once.HC.Z', SLAVE_HC_Z])

    run_qemu_and_mfa(QEMU_COMMAND + ['-cdrom', PATCHED_ISO, '-boot', 'd'],
            os.path.join(AUTO_INSTALL_PATH, 'install.script'), AUTO_INSTALL_TIMEOUT)

mkdist_script = os.path.join(DISTRO_DIR, 'mkdist.script')
mkdist_timeout = float(open(os.path.join(DISTRO_DIR, 'mkdist-timeout')).read())

print()
print('=====================================================')
print(' Ready to install %s. Press Enter to continue.' % DISTRO_DIR)
print(' As soon as the TempleOS boot menu appears, press 1.')

if sys.version_info[0] < 3:
    raw_input()
else:
    input()

run_qemu_and_mfa(QEMU_COMMAND, mkdist_script, mkdist_timeout, with_snail=False)

print('Success.')
