#pragma once

#include <3ds/types.h>
#include <3ds/synchronization.h>
#include "intrusive_list.h"

#define FOREACH_PROCESS(list, process) \
for (process = ProcessList_GetFirst(list); !ProcessList_TestEnd(list, process); process = ProcessList_GetNext(process))

enum {
    PROCESSFLAG_NOTIFY_TERMINATION              = BIT(0),
    PROCESSFLAG_KIP                             = BIT(1),
    PROCESSFLAG_DEPENDENCIES_LOADED             = BIT(2),
    PROCESSFLAG_AUTOLOADED                      = BIT(3),
    PROCESSFLAG_NOTIFY_TERMINATION_TERMINATED   = BIT(4),
    PROCESSFLAG_NORMAL_APPLICATION              = BIT(5), // Official PM doesn't have this
};

typedef enum TerminationStatus {
    TERMSTATUS_RUNNING              = 0,
    TERMSTATUS_NOTIFICATION_SENT    = 1,
    TERMSTATUS_NOTIFICATION_FAILED  = 2,
    TERMSTATUS_TERMINATED           = 3,
} TerminationStatus;

typedef struct ProcessData {
    IntrusiveNode node;
    Handle handle;
    u32 pid;
    u64 titleId;
    u64 programHandle;
    u32 launchFlags;
    u8 flags;
    u8 terminatedNotificationVariation;
    TerminationStatus terminationStatus;
    u8 refcount;
    FS_MediaType mediaType;
} ProcessData;

typedef struct ProcessList {
    RecursiveLock lock;
    IntrusiveList list;
    IntrusiveList freeList;
} ProcessList;

static inline void ProcessList_Init(ProcessList *list, void *buf, size_t num)
{
    IntrusiveList_Init(&list->list);
    IntrusiveList_CreateFromBuffer(&list->freeList, buf, sizeof(ProcessData), sizeof(ProcessData) * num);
    RecursiveLock_Init(&list->lock);
}

static inline void ProcessList_Lock(ProcessList *list)
{
    RecursiveLock_Lock(&list->lock);
}

static inline void ProcessList_Unlock(ProcessList *list)
{
    RecursiveLock_Unlock(&list->lock);
}

static inline ProcessData *ProcessList_GetNext(const ProcessData *process)
{
    return (ProcessData *)process->node.next;
}

static inline ProcessData *ProcessList_GetPrev(const ProcessData *process)
{
    return (ProcessData *)process->node.next;
}

static inline ProcessData *ProcessList_GetFirst(const ProcessList *list)
{
    return (ProcessData *)list->list.first;
}

static inline ProcessData *ProcessList_GetLast(const ProcessList *list)
{
    return (ProcessData *)list->list.last;
}

static inline bool ProcessList_TestEnd(const ProcessList *list, const ProcessData *process)
{
    return IntrusiveList_TestEnd(&list->list, &process->node);
}

ProcessData *ProcessList_New(ProcessList *list);
void ProcessList_Delete(ProcessList *list, ProcessData *process);

ProcessData *ProcessList_FindProcessById(const ProcessList *list, u32 pid);
ProcessData *ProcessList_FindProcessByTitleId(const ProcessList *list, u64 titleId);
ProcessData *ProcessList_FindProcessByHandle(const ProcessList *list, Handle handle);

void ProcessData_Incref(ProcessData *process, u32 amount);
Result ProcessData_Notify(const ProcessData *process, u32 notificationId);
Result ProcessData_SendTerminationNotification(ProcessData *process);
