/*
 * Copyright (c) Atmosphère-NX
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
#pragma once
#include 
#include "htc_rpc_tasks.hpp"
#include "htc_htcmisc_rpc_tasks.hpp"
namespace ams::htc::server::rpc {
    /* For convenience. */
    template
    concept IsTypeCheckableTask = std::derived_from && requires (T &t) {
        { t.GetTaskType() } -> std::convertible_to;
    };
    static_assert(!IsTypeCheckableTask);
    static_assert(IsTypeCheckableTask);
    class RpcTaskTable {
        private:
            /* htcs::ReceiveSmallTask/htcs::ReceiveSendTask are the largest tasks, containing an inline 0xE000 buffer. */
            /* We allow for ~0x100 task overhead from the additional events those contain. */
            /* NOTE: Nintendo hardcodes a maximum size of 0xE1D8, despite SendSmallTask being 0xE098 as of latest check. */
            #if defined(ATMOSPHERE_OS_HORIZON)
            static constexpr size_t MaxTaskSize = 0xE100;
            #elif defined(ATMOSPHERE_OS_MACOS)
            static constexpr size_t MaxTaskSize = 0xE400;
            #else
            static constexpr size_t MaxTaskSize = 0xE1D8;
            #endif
            struct TaskStorage { alignas(alignof(void *)) std::byte _storage[MaxTaskSize]; };
        private:
            bool m_valid[MaxRpcCount]{};
            TaskStorage m_storages[MaxRpcCount]{};
        private:
            template
            ALWAYS_INLINE T *GetPointer(u32 index) {
                static_assert(alignof(T) <= alignof(TaskStorage));
                static_assert(sizeof(T)  <= sizeof(TaskStorage));
                return reinterpret_cast(std::addressof(m_storages[index]));
            }
            ALWAYS_INLINE bool IsValid(u32 index) {
                return index < MaxRpcCount && m_valid[index];
            }
        public:
            constexpr RpcTaskTable() = default;
            template requires std::derived_from
            T *New(u32 index) {
                /* Sanity check input. */
                AMS_ASSERT(!this->IsValid(index));
                /* Set valid. */
                m_valid[index] = true;
                /* Allocate the task. */
                T *task = this->GetPointer(index);
                /* Create the task. */
                std::construct_at(task);
                /* Return the task. */
                return task;
            }
            template requires std::derived_from
            T *Get(u32 index) {
                /* Check that the task is valid. */
                if (!this->IsValid(index)) {
                    return nullptr;
                }
                /* Get the task pointer. */
                T *task = this->GetPointer(index);
                /* Type check the task. */
                if constexpr (IsTypeCheckableTask) {
                    if (task->GetTaskType() != T::TaskType) {
                        task = nullptr;
                    }
                }
                /* Return the task. */
                return task;
            }
            template requires std::derived_from
            void Delete(u32 index) {
                /* Check that the task is valid. */
                if (!this->IsValid(index)) {
                    return;
                }
                /* Delete the task. */
                std::destroy_at(this->GetPointer(index));
                /* Mark the task as invalid. */
                m_valid[index] = false;
            }
            void Delete(u32 index) {
                if (this->IsValid(index)) {
                    this->Delete(index);
                }
            }
    };
}