#
# Copyright 2009, 2013 Red Hat, Inc.
#
# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.

from .device import Device, DeviceSeclabel
from ..xmlbuilder import XMLBuilder, XMLChildProperty, XMLProperty
from .. import xmlutil


def _set_host_helper(obj, hostparam, portparam, val):
    def parse_host(val):
        host, ignore, port = (val or "").partition(":")
        return host or None, port or None

    host, port = parse_host(val)
    if port and not host:
        host = "127.0.0.1"
    xmlutil.set_prop_path(obj, hostparam, host)
    if port:
        xmlutil.set_prop_path(obj, portparam, port)


class CharSource(XMLBuilder):
    XML_NAME = "source"
    _XML_PROP_ORDER = [
        "bind_host",
        "bind_service",
        "mode",
        "connect_host",
        "connect_service",
        "path",
        "channel",
    ]

    def set_friendly_connect(self, val):
        _set_host_helper(self, "connect_host", "connect_service", val)

    def set_friendly_bind(self, val):
        _set_host_helper(self, "bind_host", "bind_service", val)

    def set_friendly_host(self, val):
        _set_host_helper(self, "host", "service", val)

    seclabels = XMLChildProperty(DeviceSeclabel)

    host = XMLProperty("./@host")
    service = XMLProperty("./@service", is_int=True)
    path = XMLProperty("./@path")
    channel = XMLProperty("./@channel")
    master = XMLProperty("./@master")
    slave = XMLProperty("./@slave")
    mode = XMLProperty("./@mode")
    tls = XMLProperty("./@tls", is_yesno=True)

    # for qemu-vdagent channel
    clipboard_copypaste = XMLProperty("./clipboard/@copypaste", is_yesno=True)
    mouse_mode = XMLProperty("./mouse/@mode")

    # It's weird to track these properties here, since the XML is set on
    # the parent, but this is how libvirt does it internally, which means
    # everything that shares a charsource has these values too.
    protocol = XMLProperty("./../protocol/@type")
    log_file = XMLProperty("./../log/@file")
    log_append = XMLProperty("./../log/@append", is_onoff=True)

    # Convenience source helpers for setting connect/bind host and service
    connect_host = XMLProperty("./../source[@mode='connect']/@host")
    connect_service = XMLProperty("./../source[@mode='connect']/@service", is_int=True)
    bind_host = XMLProperty("./../source[@mode='bind']/@host")
    bind_service = XMLProperty("./../source[@mode='bind']/@service", is_int=True)


class _DeviceChar(Device):
    """
    Base class for all character devices. Shouldn't be instantiated
    directly.
    """

    TYPE_PTY = "pty"
    TYPE_DEV = "dev"
    TYPE_STDIO = "stdio"
    TYPE_PIPE = "pipe"
    TYPE_FILE = "file"
    TYPE_VC = "vc"
    TYPE_NULL = "null"
    TYPE_TCP = "tcp"
    TYPE_UDP = "udp"
    TYPE_UNIX = "unix"
    TYPE_SPICEVMC = "spicevmc"
    TYPE_SPICEPORT = "spiceport"
    TYPE_NMDM = "nmdm"
    TYPE_QEMUVDAGENT = "qemu-vdagent"

    CHANNEL_NAME_SPICE = "com.redhat.spice.0"
    CHANNEL_NAME_QEMUGA = "org.qemu.guest_agent.0"
    CHANNEL_NAME_LIBGUESTFS = "org.libguestfs.channel.0"
    CHANNEL_NAME_SPICE_WEBDAV = "org.spice-space.webdav.0"
    CHANNEL_NAMES = [
        CHANNEL_NAME_SPICE,
        CHANNEL_NAME_QEMUGA,
        CHANNEL_NAME_LIBGUESTFS,
        CHANNEL_NAME_SPICE_WEBDAV,
    ]

    def set_friendly_target(self, val):
        _set_host_helper(self, "target_address", "target_port", val)

    _XML_PROP_ORDER = ["type", "source", "target_type", "target_name", "target_state"]

    type = XMLProperty("./@type")
    source = XMLChildProperty(CharSource, is_single=True)

    target_address = XMLProperty("./target/@address")
    target_port = XMLProperty("./target/@port", is_int=True)
    target_type = XMLProperty("./target/@type")
    target_name = XMLProperty("./target/@name")
    target_state = XMLProperty("./target/@state")
    target_model_name = XMLProperty("./target/model/@name")

    ##################
    # Default config #
    ##################

    def set_defaults(self, _guest):
        if not self.source.mode and self.type in [self.TYPE_UNIX, self.TYPE_TCP]:
            self.source.mode = "bind"
        if not self.target_type and self.DEVICE_TYPE == "channel":
            self.target_type = "virtio"
        if not self.target_name and (
            self.type == self.TYPE_SPICEVMC or self.type == self.TYPE_QEMUVDAGENT
        ):
            self.target_name = self.CHANNEL_NAME_SPICE


class DeviceConsole(_DeviceChar):
    @staticmethod
    def get_console_duplicate(guest, serial):
        """
        Determine if the passed serial device has a duplicate
        <console> device in the passed Guest
        """
        if serial.DEVICE_TYPE != "serial":
            return

        consoles = guest.devices.console
        if not consoles:
            return  # pragma: no cover

        console = consoles[0]
        if console.type == serial.type and (
            console.target_type is None or console.target_type == "serial"
        ):
            return console

    XML_NAME = "console"


class DeviceSerial(_DeviceChar):
    XML_NAME = "serial"


class DeviceParallel(_DeviceChar):
    XML_NAME = "parallel"


class DeviceChannel(_DeviceChar):
    XML_NAME = "channel"
