# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.

from virtinst import DeviceTpm

from ..lib import uiutil
from ..baseclass import vmmGObjectUI


def _tpm_pretty_model(val):
    labels = {
        DeviceTpm.MODEL_TIS: _("TIS"),
        DeviceTpm.MODEL_CRB: _("CRB"),
        DeviceTpm.MODEL_SPAPR: _("SPAPR"),
    }
    return labels.get(val, val)


_EDIT_TPM_ENUM = range(1, 5)
(
    _EDIT_TPM_TYPE,
    _EDIT_TPM_DEVICE_PATH,
    _EDIT_TPM_MODEL,
    _EDIT_TPM_VERSION,
) = _EDIT_TPM_ENUM


class vmmTPMDetails(vmmGObjectUI):
    __gsignals__ = {
        "changed": (vmmGObjectUI.RUN_FIRST, None, []),
    }

    def __init__(self, vm, builder, topwin):
        super().__init__("tpmdetails.ui", None, builder=builder, topwin=topwin)
        self.vm = vm
        self.conn = vm.conn

        self._active_edits = []

        def _e(edittype):
            def signal_cb(*args):
                self._change_cb(edittype)

            return signal_cb

        self.builder.connect_signals(
            {
                "on_tpm_type_changed": _e(_EDIT_TPM_TYPE),
                "on_tpm_device_path_changed": _e(_EDIT_TPM_DEVICE_PATH),
                "on_tpm_model_changed": _e(_EDIT_TPM_MODEL),
                "on_tpm_version_changed": _e(_EDIT_TPM_VERSION),
            }
        )

        self._init_ui()
        self.top_box = self.widget("top-box")

    def _cleanup(self):
        self.vm = None
        self.conn = None

    ##############
    # UI helpers #
    ##############

    def _init_ui(self):
        domcaps = self.vm.get_domain_capabilities()

        # We could check domcaps for this, but emulated is really the
        # preferred default here, so just let it fail
        rows = [
            [DeviceTpm.TYPE_EMULATOR, _("Emulated")],
            [DeviceTpm.TYPE_PASSTHROUGH, _("Passthrough")],
        ]
        uiutil.build_simple_combo(self.widget("tpm-type"), rows, sort=False)

        rows = [[None, _("Hypervisor default")]]
        if domcaps.devices.tpm.present:
            values = domcaps.devices.tpm.get_enum("model").get_values()
        else:
            values = [DeviceTpm.MODEL_CRB, DeviceTpm.MODEL_TIS]
        for v in values:
            rows.append([v, _tpm_pretty_model(v)])

        uiutil.build_simple_combo(self.widget("tpm-model"), rows, sort=False)

        rows = [
            [None, _("Hypervisor default")],
            [DeviceTpm.VERSION_2_0, DeviceTpm.VERSION_2_0],
            [DeviceTpm.VERSION_1_2, DeviceTpm.VERSION_1_2],
        ]
        uiutil.build_simple_combo(self.widget("tpm-version"), rows, sort=False)

    def _sync_ui(self):
        devtype = uiutil.get_list_selection(self.widget("tpm-type"))

        uiutil.set_grid_row_visible(
            self.widget("tpm-device-path"), devtype == DeviceTpm.TYPE_PASSTHROUGH
        )
        uiutil.set_grid_row_visible(self.widget("tpm-version"), devtype == DeviceTpm.TYPE_EMULATOR)

    ##################
    # Public UI APIs #
    ##################

    def reset_state(self):
        self.widget("tpm-device-path").set_text("/dev/tpm0")
        uiutil.set_list_selection(self.widget("tpm-type"), DeviceTpm.TYPE_EMULATOR)

        default_model = DeviceTpm.default_model(self.vm.xmlobj)
        uiutil.set_list_selection(self.widget("tpm-model"), default_model)
        uiutil.set_list_selection(self.widget("tpm-version"), None)

    def set_dev(self, dev):
        self.reset_state()

        uiutil.set_list_selection(self.widget("tpm-type"), dev.type)
        uiutil.set_list_selection(self.widget("tpm-model"), dev.model)
        uiutil.set_list_selection(self.widget("tpm-version"), dev.version)
        self.widget("tpm-device-path").set_text(dev.device_path or "")

        self._active_edits = []

    ########################
    # Device building APIs #
    ########################

    def _set_values(self, dev):
        typ = uiutil.get_list_selection(self.widget("tpm-type"))
        model = uiutil.get_list_selection(self.widget("tpm-model"))
        device_path = self.widget("tpm-device-path").get_text()
        version = uiutil.get_list_selection(self.widget("tpm-version"))

        if not self.widget("tpm-device-path").get_visible():
            device_path = None
        if not self.widget("tpm-version").get_visible():
            version = None

        if _EDIT_TPM_TYPE in self._active_edits:
            dev.type = typ
        if _EDIT_TPM_MODEL in self._active_edits:
            dev.model = model
        if _EDIT_TPM_DEVICE_PATH in self._active_edits:
            dev.device_path = device_path
        if _EDIT_TPM_VERSION in self._active_edits:
            dev.version = version

        return dev

    def build_device(self):
        self._active_edits = _EDIT_TPM_ENUM[:]

        conn = self.conn.get_backend()
        dev = DeviceTpm(conn)
        self._set_values(dev)

        dev.validate()
        return dev

    def update_device(self, dev):
        newdev = DeviceTpm(dev.conn, parsexml=dev.get_xml())
        self._set_values(newdev)
        return newdev

    #############
    # Listeners #
    #############

    def _change_cb(self, edittype):
        self._sync_ui()
        if edittype not in self._active_edits:
            self._active_edits.append(edittype)
        self.emit("changed")
