/*
 * Copyright (c) 2018-2020 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 
#include 
#include 
#include 
namespace ams::tipc {
    struct ClientProcessId {
        os::ProcessId value;
    };
    static_assert(std::is_trivial::value && sizeof(ClientProcessId) == sizeof(os::ProcessId), "ClientProcessId");
}
namespace ams::tipc::impl {
    /* Machinery for filtering type lists. */
    template
    struct TupleCat;
    template
    struct TupleCat, std::tuple> {
        using type = std::tuple;
    };
    template typename Cond>
    struct TupleFilter {
        private:
            template
            struct ImplType;
            template
            struct ImplType> {
                using type = Res;
            };
            template
            struct ImplType> {
                using type = typename std::conditional::value,
                                                       typename ImplType>::type, std::tuple>::type,
                                                       typename ImplType>::type
                                                      >::type;
            };
        public:
            template
            using FilteredType = typename ImplType, T>::type;
    };
    enum class ArgumentType {
        InData,
        OutData,
        Buffer,
        InHandle,
        OutHandle,
        ProcessId,
    };
    template
    constexpr inline ArgumentType GetArgumentType = [] {
        if constexpr (tipc::IsBuffer) {
            return ArgumentType::Buffer;
        } else if constexpr (std::is_base_of::value) {
            return ArgumentType::InHandle;
        } else if constexpr (std::is_base_of::value) {
            return ArgumentType::OutHandle;
        } else if constexpr (std::is_base_of::value) {
            return ArgumentType::OutData;
        } else if constexpr (std::same_as) {
            return ArgumentType::ProcessId;
        } else if constexpr (std::is_trivial::value && !std::is_pointer::value) {
            return ArgumentType::InData;
        } else if constexpr (std::is_same::value) {
            return ArgumentType::InData;
        } else {
            static_assert(!std::is_same::value, "Invalid ArgumentType");
        }
    }();
    template
    struct ArgumentTypeFilter : public std::bool_constant == ArgT>{};
    template
    struct TypeEqualityFilter : public std::bool_constant::value>{};
    /* Argument type filters. */
    template
    using InDataFilter    = ArgumentTypeFilter;
    template
    using OutDataFilter   = ArgumentTypeFilter;
    template
    using BufferFilter    = ArgumentTypeFilter;
    template
    using InHandleFilter  = ArgumentTypeFilter;
    template
    using OutHandleFilter = ArgumentTypeFilter;
    template
    using ProcessIdFilter = ArgumentTypeFilter;
    /* Handle kind filters. */
    template
    using InMoveHandleFilter = TypeEqualityFilter;
    template
    using InCopyHandleFilter = TypeEqualityFilter;
    template
    using OutMoveHandleFilter = TypeEqualityFilter>;
    template
    using OutCopyHandleFilter = TypeEqualityFilter>;
    template
    struct BufferAttributeArrayGetter;
    template
    struct BufferAttributeArrayGetter> {
        static constexpr std::array value = { BufferAttributes..., };
    };
    template<>
    struct BufferAttributeArrayGetter> {
        static constexpr std::array value{};
    };
    template
    struct BufferAttributeCounter {
        template
        static constexpr size_t GetCount(const std::array &attributes_array) {
            size_t count = 0;
            for (size_t i = 0; i < Size; i++) {
                if (Predicate(attributes_array[i])) {
                    count++;
                }
            }
            return count;
        }
    };
    static constexpr size_t InBufferPredicate(const u32 attribute) {
        return (attribute & SfBufferAttr_In) && !(attribute & SfBufferAttr_Out);
    }
    static constexpr size_t OutBufferPredicate(const u32 attribute) {
        return !(attribute & SfBufferAttr_In) && (attribute & SfBufferAttr_Out);
    }
    template
    struct RawDataOffsetCalculator;
    template
    struct RawDataOffsetCalculator> {
        private:
            template
            struct LayoutHelper {
                static_assert(GetArgumentType == ArgumentType::InData, "LayoutHelper InData");
                static constexpr size_t Alignment = alignof(T);
                static constexpr size_t Size      = sizeof(T);
            };
            template
            struct LayoutHelper> {
                static_assert(GetArgumentType == ArgumentType::InData, "LayoutHelper OutData");
                static constexpr size_t Alignment = alignof(T);
                static constexpr size_t Size      = sizeof(T);
            };
        public:
            static constexpr std::array Offsets = [] {
                std::array offsets{};
                offsets[0] = 0;
                if constexpr (sizeof...(Ts) > 0) {
                    /* Get size, alignment for each type. */
                    const std::array sizes = { LayoutHelper::Size... };
                    const std::array aligns = { LayoutHelper::Alignment... };
                    /* We want to sort...by alignment. */
                    std::array map = {};
                    for (size_t i = 0; i < sizeof...(Ts); i++) { map[i] = i; }
                    /* TODO: Is sorting done on parameters? */
                    /* Sort?(map, aligns); */
                    /* Iterate over sorted sizes. */
                    size_t cur_offset = 0;
                    for (size_t i : map) {
                        cur_offset = util::AlignUp(cur_offset, aligns[i]);
                        offsets[i] = cur_offset;
                        cur_offset += sizes[i];
                    }
                    offsets[sizeof...(Ts)] = cur_offset;
                }
                return offsets;
            }();
    };
    struct ArgumentSerializationInfo {
        /* Type used to select from below fields. */
        ArgumentType arg_type;
        /* Raw data indexing. */
        size_t in_raw_data_index;
        size_t out_raw_data_index;
        /* Buffer indexing. */
        size_t buffer_index;
        bool is_send_buffer;
        size_t send_map_alias_index;
        size_t recv_map_alias_index;
        /* Handle indexing. */
        size_t in_move_handle_index;
        size_t in_copy_handle_index;
        size_t out_move_handle_index;
        size_t out_copy_handle_index;
    };
    template
    struct CommandMetaInfo;
    template
    struct CommandMetaInfo<_CommandId, std::tuple> {
        public:
            static constexpr u16 CommandId = _CommandId;
            using ArgsType  = std::tuple::type...>;
            using InDatas    = TupleFilter::FilteredType;
            using OutDatas   = TupleFilter::FilteredType;
            using Buffers    = TupleFilter::FilteredType;
            using InHandles  = TupleFilter::FilteredType;
            using OutHandles = TupleFilter::FilteredType;
            using ProcessIds = TupleFilter::FilteredType;
            static constexpr size_t NumInDatas    = std::tuple_size::value;
            static constexpr size_t NumOutDatas   = std::tuple_size::value;
            static constexpr size_t NumBuffers    = std::tuple_size::value;
            static constexpr size_t NumInHandles  = std::tuple_size::value;
            static constexpr size_t NumOutHandles = std::tuple_size::value;
            static constexpr size_t NumProcessIds = std::tuple_size::value;
            static constexpr bool   HasProcessId  = NumProcessIds >= 1;
            static_assert(NumBuffers <= 8, "Methods must take in <= 8 Buffers");
            static_assert(NumInHandles <= 8, "Methods must take in <= 8 Handles");
            static_assert(NumOutHandles <= 8, "Methods must output <= 8 Handles");
            /* Buffer marshalling. */
            static constexpr std::array BufferAttributes = BufferAttributeArrayGetter::value;
            static constexpr size_t NumInBuffers  = BufferAttributeCounter::GetCount(BufferAttributes);
            static constexpr size_t NumOutBuffers = BufferAttributeCounter::GetCount(BufferAttributes);
            /* In/Out data marshalling. */
            static constexpr std::array InDataOffsets = RawDataOffsetCalculator::Offsets;
            static constexpr size_t InDataSize  = util::AlignUp(InDataOffsets[NumInDatas], alignof(u32));
            static constexpr std::array OutDataOffsets = RawDataOffsetCalculator::Offsets;
            static constexpr size_t OutDataSize = util::AlignUp(OutDataOffsets[NumOutDatas], alignof(u32));
            /* Useful because reasons. */
            static constexpr size_t OutDataAlign = []() -> size_t {
                if constexpr (std::tuple_size::value) {
                    return alignof(typename std::tuple_element<0, OutDatas>::type);
                }
                return 0;
            }();
            /* Handle marshalling. */
            static constexpr size_t NumInMoveHandles = std::tuple_size::FilteredType>::value;
            static constexpr size_t NumInCopyHandles = std::tuple_size::FilteredType>::value;
            static constexpr size_t NumOutMoveHandles = std::tuple_size::FilteredType>::value;
            static constexpr size_t NumOutCopyHandles = std::tuple_size::FilteredType>::value;
            static_assert(NumInMoveHandles + NumInCopyHandles   == NumInHandles,  "NumInMoveHandles + NumInCopyHandles   == NumInHandles");
            static_assert(NumOutMoveHandles + NumOutCopyHandles == NumOutHandles, "NumOutMoveHandles + NumOutCopyHandles == NumOutHandles");
            /* tipc-specific accessors. */
            static constexpr bool HasInSpecialHeader = HasProcessId || NumInHandles > 0;
            static constexpr svc::ipc::MessageBuffer::MessageHeader InMessageHeader{CommandId, HasInSpecialHeader, 0, NumInBuffers, NumOutBuffers, 0, InDataSize / sizeof(u32), 0};
            static constexpr svc::ipc::MessageBuffer::SpecialHeader InSpecialHeader{HasProcessId, NumInCopyHandles, NumInMoveHandles, HasInSpecialHeader};
            static constexpr auto InMessageProcessIdIndex = svc::ipc::MessageBuffer::GetSpecialDataIndex(InMessageHeader, InSpecialHeader);
            static constexpr auto InMessageHandleIndex    = svc::ipc::MessageBuffer::GetSpecialDataIndex(InMessageHeader, InSpecialHeader) + (HasProcessId ? sizeof(u64) / sizeof(u32) : 0);
            static constexpr auto InMessageBufferIndex    = svc::ipc::MessageBuffer::GetMapAliasDescriptorIndex(InMessageHeader, InSpecialHeader);
            static constexpr auto InMessageRawDataIndex   = svc::ipc::MessageBuffer::GetRawDataIndex(InMessageHeader, InSpecialHeader);
            static constexpr bool HasOutSpecialHeader = NumOutHandles > 0;
            static constexpr svc::ipc::MessageBuffer::MessageHeader OutMessageHeader{CommandId, HasOutSpecialHeader, 0, 0, 0, 0, (OutDataSize / sizeof(u32)) + 1, 0};
            static constexpr svc::ipc::MessageBuffer::SpecialHeader OutSpecialHeader{false, NumOutCopyHandles, NumOutMoveHandles, HasOutSpecialHeader};
            static constexpr auto OutMessageHandleIndex  = svc::ipc::MessageBuffer::GetSpecialDataIndex(OutMessageHeader, OutSpecialHeader);
            static constexpr auto OutMessageResultIndex  = svc::ipc::MessageBuffer::GetRawDataIndex(OutMessageHeader, OutSpecialHeader);
            static constexpr auto OutMessageRawDataIndex = OutMessageResultIndex + 1;
        /* Construction of argument serialization structs. */
        private:
            template
            struct ArgumentSerializationInfoConstructor;
            template
            struct ArgumentSerializationInfoConstructor> {
                template
                static constexpr ArgumentSerializationInfo ProcessUpdate(ArgumentSerializationInfo ¤t_info) {
                    /* Save a copy of the current state to return. */
                    ArgumentSerializationInfo returned_info = current_info;
                    constexpr auto arg_type = GetArgumentType;
                    returned_info.arg_type = arg_type;
                    if constexpr (arg_type == ArgumentType::InData) {
                        /* New rawdata, so increment index. */
                        current_info.in_raw_data_index++;
                    } else if constexpr (arg_type == ArgumentType::OutData) {
                        /* New rawdata, so increment index. */
                        current_info.out_raw_data_index++;
                    } else if constexpr (arg_type == ArgumentType::InHandle) {
                        /* New InHandle, increment the appropriate index. */
                        if constexpr (std::is_same::value) {
                            current_info.in_move_handle_index++;
                        } else if constexpr (std::is_same::value) {
                            current_info.in_copy_handle_index++;
                        } else {
                            static_assert(!std::is_same::value, "Invalid InHandle kind");
                        }
                    } else if constexpr (arg_type == ArgumentType::OutHandle) {
                        /* New OutHandle, increment the appropriate index. */
                        if constexpr (std::is_same>::value) {
                            current_info.out_move_handle_index++;
                        } else if constexpr (std::is_same>::value) {
                            current_info.out_copy_handle_index++;
                        } else {
                            static_assert(!std::is_same::value, "Invalid OutHandle kind");
                        }
                    } else if constexpr (arg_type == ArgumentType::Buffer) {
                        /* New Buffer, increment the appropriate index. */
                        const auto attributes = BufferAttributes[current_info.buffer_index++];
                        if (attributes & SfBufferAttr_In) {
                            returned_info.is_send_buffer = true;
                            current_info.send_map_alias_index++;
                        } else if (attributes & SfBufferAttr_Out) {
                            returned_info.is_send_buffer = false;
                            current_info.recv_map_alias_index++;
                        }
                    } else if constexpr (arg_type == ArgumentType::ProcessId) {
                        /* Nothing needs to be done to track process ids. */
                    } else {
                        static_assert(!std::is_same::value, "Invalid ArgumentType");
                    }
                    return returned_info;
                }
                static constexpr std::array ArgumentSerializationInfos = [] {
                    ArgumentSerializationInfo current_info = {};
                    return std::array{ ProcessUpdate(current_info)... };
                }();
            };
        public:
            static constexpr std::array::value> ArgumentSerializationInfos = ArgumentSerializationInfoConstructor::ArgumentSerializationInfos;
    };
    template
    class OutRawHolder {
        public:
            static constexpr size_t Size = _Size;
            static constexpr size_t Align = _Align ? _Align : alignof(u8);
        private:
            alignas(Align) u8 data[Size];
        public:
            constexpr ALWAYS_INLINE OutRawHolder() : data() { /* ... */ }
            template
            constexpr ALWAYS_INLINE uintptr_t GetAddress() const {
                static_assert(Offset <= Size, "Offset <= Size");
                static_assert(TypeSize <= Size, "TypeSize <= Size");
                static_assert(Offset + TypeSize <= Size, "Offset + TypeSize <= Size");
                return reinterpret_cast(data + Offset);
            }
            constexpr ALWAYS_INLINE void CopyTo(const svc::ipc::MessageBuffer &buffer) const {
                if constexpr (Size > 0) {
                    buffer.SetRawArray(OutIndex, data, Size);
                }
            }
    };
    template
    class OutHandleHolder {
        public:
            static constexpr size_t NumMove = _NumMove;
            static constexpr size_t NumCopy = _NumCopy;
        private:
            MoveHandle move_handles[NumMove];
            CopyHandle copy_handles[NumCopy];
        public:
            constexpr ALWAYS_INLINE OutHandleHolder() : move_handles(), copy_handles() { /* ... */ }
            template
            constexpr ALWAYS_INLINE MoveHandle *GetMoveHandlePointer() {
                static_assert(Index < NumMove, "Index < NumMove");
                return move_handles + Index;
            }
            template
            constexpr ALWAYS_INLINE CopyHandle *GetCopyHandlePointer() {
                static_assert(Index < NumCopy, "Index < NumCopy");
                return copy_handles + Index;
            }
            ALWAYS_INLINE void CopyTo(const svc::ipc::MessageBuffer &buffer) const {
                #define _TIPC_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(n) do { if constexpr (NumCopy > n) { buffer.SetHandle(OutIndex + n, copy_handles[n].GetValue()); } } while (0)
                _TIPC_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(0);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(1);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(2);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(3);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(4);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(5);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(6);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(7);
                #undef _TIPC_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE
                #define _TIPC_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(n) do { if constexpr (NumMove > n) { buffer.SetHandle(OutIndex + NumCopy + n, move_handles[n].GetValue()); } } while (0)
                _TIPC_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(0);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(1);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(2);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(3);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(4);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(5);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(6);
                _TIPC_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(7);
                #undef _TIPC_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE
            }
    };
    template
    struct PrintIndex;
    template
    class CommandProcessor {
        public:
            /* Useful defines. */
            using ArgsType              = typename CommandMeta::ArgsType;
            using OutRawHolderType      = OutRawHolder;
            using OutHandleHolderType   = OutHandleHolder;
        private:
            static consteval u64 GetMessageHeaderForCheck(const svc::ipc::MessageBuffer::MessageHeader &header) {
                using Value = util::BitPack32::Field<0, BITSIZEOF(util::BitPack32)>;
                const util::BitPack32 *data = header.GetData();
                const u32 lower = data[0].Get();
                const u32 upper = data[1].Get();
                return static_cast(lower) | (static_cast(upper) << BITSIZEOF(u32));
            }
            static consteval u32 GetSpecialHeaderForCheck(const svc::ipc::MessageBuffer::SpecialHeader &header) {
                using Value = util::BitPack32::Field<0, BITSIZEOF(util::BitPack32)>;
                return header.GetHeader()->Get();
            }
            /* Argument deserialization. */
            template::type>
            static ALWAYS_INLINE typename std::tuple_element::type DeserializeArgumentImpl(const svc::ipc::MessageBuffer &message_buffer, const OutRawHolderType &out_raw_holder, OutHandleHolderType &out_handles_holder) {
                constexpr auto Info = CommandMeta::ArgumentSerializationInfos[Index];
                if constexpr (Info.arg_type == ArgumentType::InData) {
                    /* New in rawdata. */
                    constexpr size_t Offset   = CommandMeta::InDataOffsets[Info.in_raw_data_index];
                    constexpr size_t RawIndex = Offset / sizeof(u32);
                    static_assert(Offset == RawIndex * sizeof(u32)); /* TODO: Do unaligned data exist? */
                    if constexpr (!std::same_as) {
                        return message_buffer.GetRaw(CommandMeta::InMessageRawDataIndex + RawIndex);
                    } else {
                        return message_buffer.GetRaw(CommandMeta::InMessageRawDataIndex + RawIndex) & 1;
                    }
                } else if constexpr (Info.arg_type == ArgumentType::OutData) {
                    /* New out rawdata. */
                    constexpr size_t Offset = CommandMeta::OutDataOffsets[Info.out_raw_data_index];
                    return T(out_raw_holder.template GetAddress());
                } else if constexpr (Info.arg_type == ArgumentType::InHandle) {
                    /* New InHandle. */
                    if constexpr (std::is_same::value) {
                        constexpr auto HandleIndex = CommandMeta::InMessageHandleIndex + CommandMeta::NumInCopyHandles + Info.in_move_handle_index;
                        return T(message_buffer.GetHandle(HandleIndex));
                    } else if constexpr (std::is_same::value) {
                        constexpr auto HandleIndex = CommandMeta::InMessageHandleIndex + Info.in_copy_handle_index;
                        return T(message_buffer.GetHandle(HandleIndex));
                    } else {
                        static_assert(!std::is_same::value, "Invalid InHandle kind");
                    }
                } else if constexpr (Info.arg_type == ArgumentType::OutHandle) {
                    /* New OutHandle. */
                    if constexpr (std::is_same>::value) {
                        return T(out_handles_holder.template GetMoveHandlePointer());
                    } else if constexpr (std::is_same>::value) {
                        return T(out_handles_holder.template GetCopyHandlePointer());
                    } else {
                        static_assert(!std::is_same::value, "Invalid OutHandle kind");
                    }
                } else if constexpr (Info.arg_type == ArgumentType::Buffer) {
                    /* NOTE: There are currently no tipc commands which use buffers-with-attributes */
                    /*       If these are added (e.g., NonSecure buffers), implement checking here? */
                    constexpr size_t MapAliasDescriptorSize = svc::ipc::MessageBuffer::MapAliasDescriptor::GetDataSize();
                    if constexpr (Info.is_send_buffer) {
                        /* Input send buffer. */
                        constexpr auto BufferIndex = CommandMeta::InMessageBufferIndex + (Info.send_map_alias_index * MapAliasDescriptorSize / sizeof(util::BitPack32));
                        const svc::ipc::MessageBuffer::MapAliasDescriptor descriptor(message_buffer, BufferIndex);
                        return T(descriptor.GetAddress(), descriptor.GetSize());
                    } else {
                        /* Input receive buffer. */
                        constexpr auto BufferIndex = CommandMeta::InMessageBufferIndex + ((CommandMeta::NumInBuffers + Info.recv_map_alias_index) * MapAliasDescriptorSize / sizeof(util::BitPack32));
                        const svc::ipc::MessageBuffer::MapAliasDescriptor descriptor(message_buffer, BufferIndex);
                        return T(descriptor.GetAddress(), descriptor.GetSize());
                    }
                } else if constexpr (Info.arg_type == ArgumentType::ProcessId) {
                    return T{ os::ProcessId{ message_buffer.GetProcessId(CommandMeta::InMessageProcessIdIndex) } };
                } else {
                    static_assert(!std::is_same::value, "Invalid ArgumentType");
                }
            }
        public:
            static ALWAYS_INLINE Result ValidateCommandFormat(const svc::ipc::MessageBuffer &message_buffer) {
                /* Validate the message header. */
                constexpr auto ExpectedMessageHeader = GetMessageHeaderForCheck(CommandMeta::InMessageHeader);
                R_UNLESS(message_buffer.Get64(0) == ExpectedMessageHeader, tipc::ResultInvalidMessageFormat());
                /* Validate the special header. */
                if constexpr (CommandMeta::HasInSpecialHeader) {
                    constexpr auto ExpectedSpecialHeader = GetSpecialHeaderForCheck(CommandMeta::InSpecialHeader);
                    constexpr auto SpecialHeaderIndex    = svc::ipc::MessageBuffer::MessageHeader::GetDataSize() / sizeof(util::BitPack32);
                    R_UNLESS(message_buffer.Get32(SpecialHeaderIndex) == ExpectedSpecialHeader, tipc::ResultInvalidMessageFormat());
                }
                return ResultSuccess();
            }
            template
            static ALWAYS_INLINE auto DeserializeArgument(const svc::ipc::MessageBuffer &message_buffer, const OutRawHolderType &out_raw_holder, OutHandleHolderType &out_handles_holder) {
                return DeserializeArgumentImpl(message_buffer, out_raw_holder, out_handles_holder);
            }
            static ALWAYS_INLINE void SerializeResults(const svc::ipc::MessageBuffer &message_buffer, const Result &result, const OutRawHolderType &out_raw_holder, const OutHandleHolderType &out_handles_holder) {
                /* Set output headers. */
                message_buffer.Set(CommandMeta::OutMessageHeader);
                if constexpr (CommandMeta::HasOutSpecialHeader) {
                    message_buffer.Set(CommandMeta::OutSpecialHeader);
                }
                /* Set output handles. */
                out_handles_holder.CopyTo(message_buffer);
                /* Set output result. */
                message_buffer.Set(CommandMeta::OutMessageResultIndex, result.GetValue());
                /* Set output data. */
                out_raw_holder.CopyTo(message_buffer);
            }
    };
    struct FunctionTraits {
        public:
            template
            static std::tuple GetArgumentsImpl(R(C::*)(A...));
            template
            static R GetReturnImpl(R(C::*)(A...));
    };
    template
    constexpr ALWAYS_INLINE Result InvokeServiceCommandImpl(ClassType *object, const svc::ipc::MessageBuffer &message_buffer) {
        using Return             = decltype(FunctionTraits::GetReturnImpl(ServiceCommandImpl));
        using TrueArgumentsTuple = decltype(FunctionTraits::GetArgumentsImpl(ServiceCommandImpl));
        using CommandMeta        = CommandMetaInfo<_CommmandId, TrueArgumentsTuple>;
        using Processor          = CommandProcessor;
        /* TODO: ValidateClassType is valid? */
        constexpr bool ReturnsResult = std::is_same::value;
        constexpr bool ReturnsVoid   = std::is_same::value;
        static_assert(ReturnsResult || ReturnsVoid, "Service Commands must return Result or void.");
        /* Validate that the command is valid. */
        R_TRY(Processor::ValidateCommandFormat(message_buffer));
        /* Deserialize arguments. */
        typename Processor::OutRawHolderType out_raw_holder;
        typename Processor::OutHandleHolderType out_handles_holder;
        const Result command_result = [&](std::index_sequence) ALWAYS_INLINE_LAMBDA {
            if constexpr (ReturnsResult) {
                return (object->*ServiceCommandImpl)(Processor::template DeserializeArgument(message_buffer, out_raw_holder, out_handles_holder)...);
            } else {
                (object->*ServiceCommandImpl)(Processor::template DeserializeArgument(message_buffer, out_raw_holder, out_handles_holder)...);
                return ResultSuccess();
            }
        }(std::make_index_sequence::value>());
        /* Serialize output. */
        Processor::SerializeResults(message_buffer, command_result, out_raw_holder, out_handles_holder);
        return ResultSuccess();
    }
}