#!/usr/bin/env python

# TODO: openbmc/openbmc#2994 remove python 2 support
try:  # python 2
    import gobject
except ImportError:  # python 3
    from gi.repository import GObject as gobject

import dbus
import dbus.mainloop.glib
import dbus.service
from obmc.dbuslib.bindings import DbusObjectManager, DbusProperties, get_dbus

DBUS_NAME = "org.openbmc.control.Chassis"
OBJ_NAME = "/org/openbmc/control/chassis0"
CONTROL_INTF = "org.openbmc.Control"

MACHINE_ID = "/etc/machine-id"

POWER_OFF = 0
POWER_ON = 1

BOOTED = 100


class ChassisControlObject(DbusProperties, DbusObjectManager):
    def getUuid(self):
        uuid = ""
        try:
            with open(MACHINE_ID) as f:
                data = f.readline().rstrip("\n")
                if len(data) == 32:
                    uuid = data
                else:
                    print("ERROR:  UUID is not formatted correctly: " + data)
        except Exception:
            print("ERROR: Unable to open uuid file: " + MACHINE_ID)

        return uuid

    def __init__(self, bus, name):
        super(ChassisControlObject, self).__init__(conn=bus, object_path=name)
        # load utilized objects
        self.dbus_objects = {
            "power_control": {
                "bus_name": "org.openbmc.control.Power",
                "object_name": "/org/openbmc/control/power0",
                "interface_name": "org.openbmc.control.Power",
            },
            "host_services": {
                "bus_name": "org.openbmc.HostServices",
                "object_name": "/org/openbmc/HostServices",
                "interface_name": "org.openbmc.HostServices",
            },
            "systemd": {
                "bus_name": "org.freedesktop.systemd1",
                "object_name": "/org/freedesktop/systemd1",
                "interface_name": "org.freedesktop.systemd1.Manager",
            },
        }

        # uuid
        self.Set(DBUS_NAME, "uuid", self.getUuid())
        self.Set(DBUS_NAME, "reboot", 0)

        bus.add_signal_receiver(
            self.power_button_signal_handler,
            dbus_interface="org.openbmc.Button",
            signal_name="Released",
            path="/org/openbmc/buttons/power0",
        )
        bus.add_signal_receiver(
            self.long_power_button_signal_handler,
            dbus_interface="org.openbmc.Button",
            signal_name="PressedLong",
            path="/org/openbmc/buttons/power0",
        )
        bus.add_signal_receiver(
            self.softreset_button_signal_handler,
            dbus_interface="org.openbmc.Button",
            signal_name="Released",
            path="/org/openbmc/buttons/reset0",
        )

        bus.add_signal_receiver(
            self.host_watchdog_signal_handler,
            dbus_interface="org.openbmc.Watchdog",
            signal_name="WatchdogError",
        )

    def getInterface(self, name):
        o = self.dbus_objects[name]
        obj = bus.get_object(o["bus_name"], o["object_name"], introspect=False)
        return dbus.Interface(obj, o["interface_name"])

    @dbus.service.method(DBUS_NAME, in_signature="", out_signature="")
    def powerOn(self):
        print("Turn on power and boot")
        self.Set(DBUS_NAME, "reboot", 0)
        intf = self.getInterface("systemd")
        f = getattr(intf, "StartUnit")
        f.call_async("obmc-host-start@0.target", "replace")
        return None

    @dbus.service.method(DBUS_NAME, in_signature="", out_signature="")
    def powerOff(self):
        print("Turn off power")
        intf = self.getInterface("systemd")
        f = getattr(intf, "StartUnit")
        f.call_async("obmc-chassis-hard-poweroff@0.target", "replace")
        return None

    @dbus.service.method(DBUS_NAME, in_signature="", out_signature="")
    def softPowerOff(self):
        print("Soft off power")
        intf = self.getInterface("systemd")
        f = getattr(intf, "StartUnit")
        f.call_async("obmc-host-shutdown@0.target", "replace")
        return None

    @dbus.service.method(DBUS_NAME, in_signature="", out_signature="")
    def reboot(self):
        print("Rebooting")
        if self.getPowerState() != POWER_OFF:
            self.Set(DBUS_NAME, "reboot", 1)
            self.powerOff()
        return None

    @dbus.service.method(DBUS_NAME, in_signature="", out_signature="")
    def softReboot(self):
        print("Soft Rebooting")
        if self.getPowerState() != POWER_OFF:
            self.Set(DBUS_NAME, "reboot", 1)
            self.softPowerOff()
        return None

    @dbus.service.method(DBUS_NAME, in_signature="", out_signature="")
    def quiesce(self):
        intf = self.getInterface("systemd")
        f = getattr(intf, "StartUnit")
        f.call_async("obmc-host-quiesce@0.target", "replace")
        return None

    @dbus.service.method(DBUS_NAME, in_signature="", out_signature="i")
    def getPowerState(self):
        intf = self.getInterface("power_control")
        return intf.getPowerState()

    # Signal handler

    def power_button_signal_handler(self):
        # toggle power, power-on / soft-power-off
        state = self.getPowerState()
        if state == POWER_OFF:
            self.powerOn()
        elif state == POWER_ON:
            self.softPowerOff()

    def long_power_button_signal_handler(self):
        print("Long-press button, hard power off")
        self.powerOff()

    def softreset_button_signal_handler(self):
        self.softReboot()

    def host_watchdog_signal_handler(self):
        print("Watchdog Error, Going to quiesce")
        self.quiesce()


if __name__ == "__main__":
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    bus = get_dbus()
    obj = ChassisControlObject(bus, OBJ_NAME)
    mainloop = gobject.MainLoop()

    obj.unmask_signals()
    name = dbus.service.BusName(DBUS_NAME, bus)

    print("Running ChassisControlService")
    mainloop.run()

# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4