// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <list>

#include "core/hle/service/am/am_types.h"
#include "core/hle/service/os/event.h"

namespace Core {
class System;
}

namespace Service::AM {

enum class ActivityState : u32 {
    ForegroundVisible = 0,
    ForegroundObscured = 1,
    BackgroundVisible = 2,
    BackgroundObscured = 3,
};

enum class FocusHandlingMode : u32 {
    AlwaysSuspend = 0,
    SuspendHomeSleep = 1,
    NoSuspend = 2,
};

enum class SuspendMode : u32 {
    NoOverride = 0,
    ForceResume = 1,
    ForceSuspend = 2,
};

class LifecycleManager {
public:
    explicit LifecycleManager(Core::System& system, KernelHelpers::ServiceContext& context,
                              bool is_application);
    ~LifecycleManager();

public:
    Event& GetSystemEvent();
    Event& GetOperationModeChangedSystemEvent();

public:
    bool IsApplication() {
        return m_is_application;
    }

    bool GetForcedSuspend() {
        return m_forced_suspend;
    }

    bool GetExitRequested() {
        return m_has_requested_exit;
    }

    ActivityState GetActivityState() {
        return m_activity_state;
    }

    FocusState GetAndClearFocusState() {
        m_acknowledged_focus_state = m_requested_focus_state;
        return m_acknowledged_focus_state;
    }

    void SetFocusState(FocusState state) {
        if (m_requested_focus_state != state) {
            m_has_focus_state_changed = true;
        }
        m_requested_focus_state = state;
        this->SignalSystemEventIfNeeded();
    }

    void RequestExit() {
        m_has_requested_exit = true;
        this->SignalSystemEventIfNeeded();
    }

    void RequestResumeNotification() {
        // NOTE: this appears to be a bug in am.
        // If an applet makes a concurrent request to receive resume notifications
        // while it is being suspended, the first resume notification will be lost.
        // This is not the case with other notification types.
        if (m_resume_notification_enabled) {
            m_has_resume = true;
        }
    }

    void OnOperationAndPerformanceModeChanged();

public:
    void SetFocusStateChangedNotificationEnabled(bool enabled) {
        m_focus_state_changed_notification_enabled = enabled;
        this->SignalSystemEventIfNeeded();
    }

    void SetOperationModeChangedNotificationEnabled(bool enabled) {
        m_operation_mode_changed_notification_enabled = enabled;
        this->SignalSystemEventIfNeeded();
    }

    void SetPerformanceModeChangedNotificationEnabled(bool enabled) {
        m_performance_mode_changed_notification_enabled = enabled;
        this->SignalSystemEventIfNeeded();
    }

    void SetResumeNotificationEnabled(bool enabled) {
        m_resume_notification_enabled = enabled;
    }

    void SetActivityState(ActivityState state) {
        m_activity_state = state;
    }

    void SetSuspendMode(SuspendMode mode) {
        m_suspend_mode = mode;
    }

    void SetForcedSuspend(bool enabled) {
        m_forced_suspend = enabled;
    }

public:
    void SetFocusHandlingMode(bool suspend);
    void SetOutOfFocusSuspendingEnabled(bool enabled);
    void RemoveForceResumeIfPossible();
    bool IsRunnable() const;
    bool UpdateRequestedFocusState();
    void SignalSystemEventIfNeeded();

public:
    void PushUnorderedMessage(AppletMessage message);
    bool PopMessage(AppletMessage* out_message);

private:
    FocusState GetFocusStateWhileForegroundObscured() const;
    FocusState GetFocusStateWhileBackground(bool is_obscured) const;

private:
    AppletMessage PopMessageInOrderOfPriority();
    bool ShouldSignalSystemEvent();

private:
    Event m_system_event;
    Event m_operation_mode_changed_system_event;

    std::list<AppletMessage> m_unordered_messages{};

    bool m_is_application{};
    bool m_focus_state_changed_notification_enabled{true};
    bool m_operation_mode_changed_notification_enabled{true};
    bool m_performance_mode_changed_notification_enabled{true};
    bool m_resume_notification_enabled{};

    bool m_requested_request_to_display_state{};
    bool m_acknowledged_request_to_display_state{};
    bool m_has_resume{};
    bool m_has_focus_state_changed{true};
    bool m_has_album_recording_saved{};
    bool m_has_album_screen_shot_taken{};
    bool m_has_auto_power_down{};
    bool m_has_sleep_required_by_low_battery{};
    bool m_has_sleep_required_by_high_temperature{};
    bool m_has_sd_card_removed{};
    bool m_has_performance_mode_changed{};
    bool m_has_operation_mode_changed{};
    bool m_has_requested_request_to_prepare_sleep{};
    bool m_has_acknowledged_request_to_prepare_sleep{};
    bool m_has_requested_exit{};
    bool m_has_acknowledged_exit{};
    bool m_applet_message_available{};

    bool m_forced_suspend{};
    FocusHandlingMode m_focus_handling_mode{FocusHandlingMode::SuspendHomeSleep};
    ActivityState m_activity_state{ActivityState::ForegroundVisible};
    SuspendMode m_suspend_mode{SuspendMode::NoOverride};
    FocusState m_requested_focus_state{};
    FocusState m_acknowledged_focus_state{};
};

} // namespace Service::AM
