#pragma once

#include "gpu.h"

typedef enum __attribute__((__packed__)) FFGpuDriverConditionType
{
    FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID = 1 << 0,
    FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID = 1 << 1,
    FF_GPU_DRIVER_CONDITION_TYPE_LUID = 1 << 2,
    FF_GPU_DRIVER_CONDITION_TYPE_FORCE_UNSIGNED = UINT8_MAX,
} FFGpuDriverConditionType;

typedef struct FFGpuDriverPciBusId
{
    uint32_t domain;
    uint32_t bus;
    uint32_t device;
    uint32_t func;
} FFGpuDriverPciBusId;

typedef struct FFGpuDriverPciDeviceId
{
    uint32_t deviceId;
    uint32_t vendorId;
    uint32_t subSystemId;
    uint32_t revId;
} FFGpuDriverPciDeviceId;

// Use pciBusId if not NULL; use pciDeviceId otherwise
typedef struct FFGpuDriverCondition
{
    FFGpuDriverConditionType type;
    FFGpuDriverPciBusId pciBusId;
    FFGpuDriverPciDeviceId pciDeviceId;
    uint64_t luid;
} FFGpuDriverCondition;

// detect x if not NULL
typedef struct FFGpuDriverResult
{
    uint32_t* index;
    double* temp;
    FFGPUMemory* memory;
    FFstrbuf* memoryType;
    FFGPUMemory* sharedMemory;
    uint32_t* coreCount;
    double* coreUsage;
    FFGPUType* type;
    uint32_t* frequency;
    FFstrbuf* name;
} FFGpuDriverResult;

const char* ffDetectNvidiaGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName);
const char* ffDetectIntelGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName);
const char* ffDetectAmdGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName);
const char* ffDetectMthreadsGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName);

FF_MAYBE_UNUSED static inline bool getDriverSpecificDetectionFn(const char* vendor, __typeof__(&ffDetectNvidiaGpuInfo)* pDetectFn, const char** pDllName)
{
    if (vendor == FF_GPU_VENDOR_NAME_NVIDIA)
    {
        *pDetectFn = ffDetectNvidiaGpuInfo;
        #ifdef _WIN32
        *pDllName = "nvml.dll";
        #else
        *pDllName = "libnvidia-ml.so";
        #endif
    }
    else if (vendor == FF_GPU_VENDOR_NAME_MTHREADS)
    {
        *pDetectFn = ffDetectMthreadsGpuInfo;
        #ifdef _WIN32
        *pDllName = "mtml.dll";
        #else
        *pDllName = "libmtml.so";
        #endif
    }
    #ifdef _WIN32
    else if (vendor == FF_GPU_VENDOR_NAME_INTEL)
    {
        *pDetectFn = ffDetectIntelGpuInfo;
        #ifdef _WIN64
            *pDllName = "ControlLib.dll";
        #else
            *pDllName = "ControlLib32.dll";
        #endif
    }
    else if (vendor == FF_GPU_VENDOR_NAME_AMD)
    {
        *pDetectFn = ffDetectAmdGpuInfo;
        #ifdef _WIN64
            *pDllName = "atiadlxx.dll";
        #else
            *pDllName = "atiadlxy.dll";
        #endif
    }
    #endif
    else
    {
        *pDetectFn = NULL;
        *pDllName = NULL;
        return false;
    }

    return true;
}
