// Copyright 2017 plutoo
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include "service_guard.h"
#include "services/fatal.h"

static Service g_smSrv;

#define MAX_OVERRIDES 32

static struct {
    u64    name;
    Handle handle;
} g_smOverrides[MAX_OVERRIDES];

static size_t g_smOverridesNum = 0;

void smAddOverrideHandle(u64 name, Handle handle)
{
    if (g_smOverridesNum == MAX_OVERRIDES)
        fatalSimple(MAKERESULT(Module_Libnx, LibnxError_TooManyOverrides));

    size_t i = g_smOverridesNum;

    g_smOverrides[i].name   = name;
    g_smOverrides[i].handle = handle;

    g_smOverridesNum++;
}

Handle smGetServiceOverride(u64 name)
{
    for (size_t i = 0; i < g_smOverridesNum; i++)
        if (g_smOverrides[i].name == name)
            return g_smOverrides[i].handle;

    return INVALID_HANDLE;
}

NX_GENERATE_SERVICE_GUARD(sm);

Result _smInitialize(void)
{
    Handle sm_handle;
    Result rc = svcConnectToNamedPort(&sm_handle, "sm:");
    while (R_VALUE(rc) == KERNELRESULT(NotFound)) {
        svcSleepThread(50000000ul);
        rc = svcConnectToNamedPort(&sm_handle, "sm:");
    }

    if (R_SUCCEEDED(rc)) {
        serviceCreate(&g_smSrv, sm_handle);
    }

    Handle tmp;
    if (R_SUCCEEDED(rc) && smGetServiceOriginal(&tmp, smEncodeName("")) == 0x415) {
        const struct {
            u64 pid_placeholder;
        } in = { 0 };

        rc = serviceDispatchIn(&g_smSrv, 0, in, .in_send_pid = true);
    }

    return rc;
}

void _smCleanup(void)
{
    serviceClose(&g_smSrv);
}

Service *smGetServiceSession(void)
{
    return &g_smSrv;
}

Result smGetService(Service* service_out, const char* name)
{
    u64 name_encoded = smEncodeName(name);
    Handle handle = smGetServiceOverride(name_encoded);
    bool own_handle = false;
    Result rc = 0;

    if (handle == INVALID_HANDLE) {
        own_handle = true;
        rc = smGetServiceOriginal(&handle, name_encoded);
    }

    if (R_SUCCEEDED(rc)) {
        serviceCreate(service_out, handle);
        service_out->own_handle = own_handle;
    }

    return rc;
}

Result smGetServiceOriginal(Handle* handle_out, u64 name)
{
    const struct {
        u64 service_name;
    } in = { name };

    return serviceDispatchIn(&g_smSrv, 1, in,
        .out_handle_attrs = { SfOutHandleAttr_HipcMove },
        .out_handles = handle_out,
    );
}

Result smRegisterService(Handle* handle_out, const char* name, bool is_light, int max_sessions)
{
    const struct {
        u64 service_name;
        u32 is_light;
        u32 max_sessions;
    } in = { smEncodeName(name), !!is_light, max_sessions };

    return serviceDispatchIn(&g_smSrv, 2, in,
        .out_handle_attrs = { SfOutHandleAttr_HipcMove },
        .out_handles = handle_out,
    );
}

Result smUnregisterService(const char* name)
{
    const struct {
        u64 service_name;
    } in = { smEncodeName(name) };

    return serviceDispatchIn(&g_smSrv, 3, in);
}