#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# qvm.py
#
# Copyright (C) 2021,2023 Franco Masotti (franco \D\o\T masotti {-A-T-} tutanota \D\o\T com)
#
# This program 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.
#
# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
# Original license header:
#
# qvm - Trivial management of 64 bit virtual machines with qemu.
#
# Written in 2016 by Franco Masotti (franco \D\o\T masotti {-A-T-} tutanota \D\o\T com)
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the public
# domain worldwide. This software is distributed without any warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication along
# with this software. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
r"""Run virtual machines."""

import shlex
import sys

import fpyutils
import yaml


def build_remote_command(prf: dict) -> str:
    if prf['system']['display']['enabled']:
        # See https://unix.stackexchange.com/a/83812
        # See also the 'TCP FORWARDING' section in man 1 ssh.
        ssh = '-f -p ' + prf['system']['network']['ports']['host'][
            'ssh'] + ' -L ' + prf['system']['network']['ports']['local'][
                'vnc'] + ':127.0.0.1:' + prf['system']['network']['ports'][
                    'host']['vnc'] + ' -l ' + prf['system']['users'][
                        'host'] + ' ' + prf['system']['network']['addresses'][
                            'host']
        ssh += ' sleep 10; vncviewer 127.0.0.1::' + prf['system']['network'][
            'ports']['local']['vnc']
    else:
        ssh = '-p ' + prf['system']['network']['ports']['guest'][
            'ssh'] + ' -l ' + prf['system']['users']['guest'] + ' ' + prf[
                'system']['network']['addresses']['host']

    return (prf['executables']['ssh'] + ' ' + ssh)


def build_local_command(prf: dict) -> str:
    head = str()

    # Generic options.
    generic_options: str = '-enable-kvm'

    # Memory.
    memory = '-m ' + prf['system']['memory']

    # CPU.
    cpu = '-smp ' + prf['system']['cpu']['cores'] + ' -cpu ' + prf['system'][
        'cpu']['type']

    # Display.
    if prf['system']['display']['enabled']:
        if prf['system']['display']['vnc']['enabled']:
            display_number = int(
                prf['system']['display']['vnc']['port']) - 5900
            display = '-display none -monitor pty -vnc 127.0.0.1:' + str(
                display_number)
        else:
            display = '-display gtk'
    else:
        display = '-display none'

    # Audio.
    if prf['system']['audio']['enabled']:
        audio = '-device ' + prf['system']['audio']['device']
        head += 'export QEMU_AUDIO_DRV=alsa;'
    else:
        audio = str()

    # Network.
    user_net: str = str()
    for i in prf['system']['network']['ports']:
        if i['type'] == 'user':
            # User Networking (SLIRP).
            if i['enabled']:
                user_net += ',hostfwd=tcp::' + i['host'] + '-:' + i['guest']
        else:
            # TAP, VDE, socket.
            print('error: not implemented. setting empty network')

    user_net = '-netdev user,id=user.0' + user_net + ' -device virtio-net-pci,netdev=user.0'

    # nw = user_nw + ' ' + tap_nw + ' ' + vde_nw + ' ' + socker_nw
    net: str = user_net

    # CD-ROM.
    if prf['system']['cdrom']['enabled']:
        cdrom = '-cdrom ' + prf['system']['cdrom']['device'] + ' -boot order=d'
    else:
        cdrom = str()

    # Mounts.
    mnt = str()
    for i in prf['system']['mount']:
        if i['enabled']:
            if i['type'] == 'virtfs':
                mnt += ' -virtfs local,path=' + i[
                    'path'] + ',security_model=passthrough,mount_tag=' + i[
                        'ount_tag']
            elif i['type'] == 'virtiofs':
                mnt += (
                    '-object memory-backend-file,id=mem,size=' +
                    prf['system']['memory'] + ',mem-path=/dev/shm,share=on' +
                    ' -numa node,memdev=mem' +
                    ' -chardev socket,id=char0,path=' + i['path'] +
                    ' -device vhost-user-fs-pci,queue-size=1024,chardev=char0,tag='
                    + i['mount_tag'])
            else:
                print('error: not implemented. cannot set mount')

    # Mass memory.
    hdd = str()
    for drive in prf['system']['drives']:
        hdd += ' -drive file=' + drive

    return (head + ' ' + prf['executables']['qemu'] + ' ' +
            prf['options']['start'] + ' ' + generic_options + ' ' + memory +
            ' ' + cpu + ' ' + display + ' ' + net + ' ' + cdrom + ' ' + audio +
            ' ' + mnt + ' ' + prf['options']['end'] + ' ' + hdd)


if __name__ == '__main__':
    configuration_file = shlex.quote(sys.argv[1])
    config = yaml.load(open(configuration_file, 'r'), Loader=yaml.SafeLoader)
    type = shlex.quote(sys.argv[2])
    profile = shlex.quote(sys.argv[3])

    prf = config[type][profile]
    if prf['enabled']:
        if type == 'local':
            command = build_local_command(prf)
        elif type == 'remote':
            command = build_remote_command(prf)

    fpyutils.shell.execute_command_live_output(command, dry_run=False)
