#pragma once

#include "config.h"

#include <functional>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/server/object.hpp>
#include <sdbusplus/timer.hpp>
#include <xyz/openbmc_project/Control/Host/server.hpp>
#include <xyz/openbmc_project/Ipmi/Internal/SoftPowerOff/server.hpp>
namespace phosphor
{
namespace ipmi
{

namespace Base = sdbusplus::xyz::openbmc_project::Ipmi::Internal::server;
using namespace sdbusplus::xyz::openbmc_project::Control::server;

namespace sdbusRule = sdbusplus::bus::match::rules;

namespace
{
using SoftPowerOffInherit = sdbusplus::server::object_t<Base::SoftPowerOff>;
}

/** @class SoftPowerOff
 *  @brief Responsible for coordinating Host SoftPowerOff operation
 */
class SoftPowerOff : public SoftPowerOffInherit
{
  public:
    /** @brief Constructs SoftPowerOff object.
     *
     *  @param[in] bus       - system dbus handler
     *  @param[in] event     - sd_event handler
     *  @param[in] objPath   - The Dbus path hosting SoftPowerOff function
     */
    SoftPowerOff(sdbusplus::bus::bus& bus, sd_event* event,
                 const char* objPath) :
        SoftPowerOffInherit(bus, objPath,
                            SoftPowerOffInherit::action::defer_emit),
        bus(bus), timer(event),
        hostControlSignal(
            bus,
            sdbusRule::type::signal() + sdbusRule::member("CommandComplete") +
                sdbusRule::path("/xyz/openbmc_project/control/host0") +
                sdbusRule::interface(CONTROL_HOST_BUSNAME) +
                sdbusRule::argN(0, convertForMessage(Host::Command::SoftOff)),
            std::bind(std::mem_fn(&SoftPowerOff::hostControlEvent), this,
                      std::placeholders::_1))
    {
        // Need to announce since we may get the response
        // very quickly on host shutdown command
        emit_object_added();

        // The whole purpose of this application is to send a host shutdown
        // command and watch for the soft power off to go through. We need
        // the interface added signal emitted before we send the shutdown
        // command just to attend to lightning fast response from host
        sendHostShutDownCmd();
    }

    /** @brief Tells if the objective of this application is completed */
    inline auto isCompleted()
    {
        return completed;
    }

    /** @brief Tells if the referenced timer is expired or not */
    inline auto isTimerExpired()
    {
        return timer.isExpired();
    }

    /** @brief overloaded property setter function
     *
     *  @param[in] value - One of SoftOffReceived / HostShutdown
     *
     *  @return Success or exception thrown
     */
    HostResponse responseReceived(HostResponse value) override;

    /** @brief Using the base class's getter method */
    using Base::SoftPowerOff::responseReceived;

    /** @brief Calls to start a timer
     *
     *  @param[in] usec - Time in microseconds
     *
     *  @return Success or exception thrown
     */
    int startTimer(const std::chrono::microseconds& usec);

  private:
    // Need this to send SMS_ATTN
    // TODO : Switch over to using mapper service in a different patch
    static constexpr auto HOST_IPMI_BUS = "org.openbmc.HostIpmi";
    static constexpr auto HOST_IPMI_OBJ = "/org/openbmc/HostIpmi/1";
    static constexpr auto HOST_IPMI_INTF = "org.openbmc.HostIpmi";

    /* @brief sdbusplus handle */
    sdbusplus::bus::bus& bus;

    /** @brief Reference to Timer object */
    Timer timer;

    /** @brief Marks the end of life of this application.
     *
     *  This is set to true if host gives appropriate responses
     *  for the sequence of commands.
     */
    bool completed = false;

    /** @brief Subscribe to host control signals
     *
     *  Protocol is to send the host power off request to the host
     *  control interface and then wait for a signal indicating pass/fail
     **/
    sdbusplus::bus::match_t hostControlSignal;

    /** @brief Sends host control command to tell host to shut down
     *
     *  After sending the command, wait for a signal indicating the status
     *  of the command.
     *
     *  After receiving the initial response, start a timer for 30 minutes
     *  to let host do a clean shutdown of partitions. When the response is
     *  received from the host, it indicates that BMC can do a power off.
     *  If BMC fails to get any response, then a hard power off would
     *  be forced.
     *
     *  @return - Does not return anything. Error will result in exception
     *            being thrown
     */
    void sendHostShutDownCmd();

    /** @brief Callback function on host control signals
     *
     * @param[in]  msg       - Data associated with subscribed signal
     *
     */
    void hostControlEvent(sdbusplus::message::message& msg);
};
} // namespace ipmi
} // namespace phosphor