#include <windows.h>

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "nv/api.h"
#include "nv/module.h"

#include "util/fs.h"
#include "util/mem.h"
#include "util/str.h"

#define NV_DRS_SETTINGS_FOLDER "C:\\ProgramData\\NVIDIA Corporation\\Drs"

#define printf_out(fmt, ...) \
    fprintf(stdout, fmt, ##__VA_ARGS__)
#define printf_err(fmt, ...) \
    fprintf(stderr, fmt, ##__VA_ARGS__)
#define printfln_out(fmt, ...) \
    fprintf(stdout, fmt "\n", ##__VA_ARGS__)
#define printfln_err(fmt, ...) \
    fprintf(stderr, fmt "\n", ##__VA_ARGS__)

#define PRINT_ERR_WITH_NVAPI_MESSAGE(status, fmt, ...) \
    NvAPI_ShortString error_str; \
    nv_api->NvAPI_GetErrorMessage(status, error_str); \
    fprintf(stderr, fmt ", reason: %s\n", ##__VA_ARGS__, error_str);

typedef struct displayconfig_path_info {
    NvU32 path_info_count;
    NV_DISPLAYCONFIG_PATH_INFO *path_info;
} displayconfig_path_info_t;

static bool _session_destroy(const nv_api_t *nv_api, NvDRSSessionHandle session)
{
    assert(nv_api);
    assert(session);

    NvAPI_Status status;

    status = nv_api->NvAPI_DRS_DestroySession(session);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Destroying driver settings session");
        return false;
    }

    return true;
}

static bool _session_create_and_settings_load(const nv_api_t *nv_api, NvDRSSessionHandle *session)
{
    assert(nv_api);
    assert(session);

    NvAPI_Status status;

    status = nv_api->NvAPI_DRS_CreateSession(session);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Creating driver settings session");
        return false;
    }

    status = nv_api->NvAPI_DRS_LoadSettings(*session);
    
    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Loading driver settings");
        
        return _session_destroy(nv_api, *session);
    }

    return true;
}

static bool _settings_save_and_session_destroy(const nv_api_t *nv_api, NvDRSSessionHandle session)
{
    assert(nv_api);
    assert(session);

    NvAPI_Status status;

    printfln_err("Saving ...");

    status = nv_api->NvAPI_DRS_SaveSettings(session);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Saving driver settings");
    
        return _session_destroy(nv_api, session);
    }

    return _session_destroy(nv_api, session);
}

static bool _profile_get(
        const nv_api_t *nv_api,
        const char *profile_name,
        NvDRSSessionHandle session,
        NvDRSProfileHandle *profile)
{
    NvAPI_UnicodeString nv_profile_name;
    NvAPI_Status status;
    wchar_t *profile_name_wstr;

    assert(nv_api);
    assert(session);
    assert(profile);

    profile_name_wstr = str_widen(profile_name);
    wstr_cpy(nv_profile_name, sizeof(nv_profile_name), profile_name_wstr);

    status = nv_api->NvAPI_DRS_FindProfileByName(session, nv_profile_name, profile);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Finding driver profile %s", profile_name);
        return false;
    }

    return true;
}

static bool _profile_setting_set(
    const nv_api_t *nv_api,
    NvDRSSessionHandle session,
    NvDRSProfileHandle profile,
    NVDRS_SETTING *setting)
{
    NvAPI_Status status;

    assert(nv_api);
    assert(setting);

    status = nv_api->NvAPI_DRS_SetSetting(session, profile, setting);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "Error setting driver settings");
        return false;
    }

    return true;
}

static void _display_config_free(displayconfig_path_info_t *displayconfig_path_info)
{
    assert(displayconfig_path_info);
    assert(displayconfig_path_info->path_info);

    for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) {
        assert(displayconfig_path_info->path_info[i].sourceModeInfo);
        assert(displayconfig_path_info->path_info[i].targetInfo);

        for (NvU32 j = 0; j < displayconfig_path_info->path_info[i].targetInfoCount; j++) {
            assert(displayconfig_path_info->path_info[i].targetInfo[i].details);

            free(displayconfig_path_info->path_info[i].targetInfo[i].details);
        }

        free(displayconfig_path_info->path_info[i].targetInfo);
        free(displayconfig_path_info->path_info[i].sourceModeInfo);
    }

    free(displayconfig_path_info->path_info);
}

static bool _display_config_get(const nv_api_t *nv_api, displayconfig_path_info_t *displayconfig_path_info)
{
    NvAPI_Status status;

    assert(nv_api);
    assert(displayconfig_path_info);

    displayconfig_path_info->path_info_count = 0;

    status = nv_api->NvAPI_DISP_GetDisplayConfig(&displayconfig_path_info->path_info_count, NULL);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting display config path info count");
        return false;
    }

    displayconfig_path_info->path_info = (NV_DISPLAYCONFIG_PATH_INFO*)
        xmalloc(sizeof(NV_DISPLAYCONFIG_PATH_INFO) * displayconfig_path_info->path_info_count);
    memset(displayconfig_path_info->path_info, 0, sizeof(NV_DISPLAYCONFIG_PATH_INFO) * displayconfig_path_info->path_info_count);

    for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) {
        displayconfig_path_info->path_info[i].version = NV_DISPLAYCONFIG_PATH_INFO_VER;
    }

    status = nv_api->NvAPI_DISP_GetDisplayConfig(&displayconfig_path_info->path_info_count, displayconfig_path_info->path_info);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting display config target info counts");

        free(displayconfig_path_info->path_info);

        return false;
    }

    for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) {
        if (displayconfig_path_info->path_info[i].version == NV_DISPLAYCONFIG_PATH_INFO_VER1 ||
                displayconfig_path_info->path_info[i].version == NV_DISPLAYCONFIG_PATH_INFO_VER2) {
            displayconfig_path_info->path_info[i].sourceModeInfo =
                (NV_DISPLAYCONFIG_SOURCE_MODE_INFO*) xmalloc(sizeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO));
        } else {
#ifdef NV_DISPLAYCONFIG_PATH_INFO_VER3
            displayconfig_path_info->path_info[i].sourceModeInfo =(NV_DISPLAYCONFIG_SOURCE_MODE_INFO*) malloc(
                displayconfig_path_info->path_info[i].sourceModeInfoCount * sizeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO));
#endif
        }

        memset(displayconfig_path_info->path_info[i].sourceModeInfo, 0, sizeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO));

        displayconfig_path_info->path_info[i].targetInfo = (NV_DISPLAYCONFIG_PATH_TARGET_INFO*) xmalloc(
            displayconfig_path_info->path_info[i].targetInfoCount * sizeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO));
   
        memset(displayconfig_path_info->path_info[i].targetInfo, 0,
            displayconfig_path_info->path_info[i].targetInfoCount * sizeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO));
        
        for (NvU32 j = 0 ; j < displayconfig_path_info->path_info[i].targetInfoCount ; j++) {
            displayconfig_path_info->path_info[i].targetInfo[j].details =
                (NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO*) xmalloc(sizeof(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO));    
            memset(displayconfig_path_info->path_info[i].targetInfo[j].details, 0,
                sizeof(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO));
            displayconfig_path_info->path_info[i].targetInfo[j].details->version =
                NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_VER;
        }
    }

    status = nv_api->NvAPI_DISP_GetDisplayConfig(&displayconfig_path_info->path_info_count, displayconfig_path_info->path_info);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting display config");

        _display_config_free(displayconfig_path_info);

        return false;
    }

    return true;
}

static bool _display_config_set(const nv_api_t *nv_api, displayconfig_path_info_t *displayconfig_path_info)
{
    NvAPI_Status status;

    assert(nv_api);
    assert(displayconfig_path_info);

    printfln_err("Validating display config...");

    status = nv_api->NvAPI_DISP_SetDisplayConfig(displayconfig_path_info->path_info_count, displayconfig_path_info->path_info, NV_DISPLAYCONFIG_VALIDATE_ONLY);
    
    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Validating display config");
        return false;
    }

    printfln_err("Saving display config...");

    status = nv_api->NvAPI_DISP_SetDisplayConfig(displayconfig_path_info->path_info_count, displayconfig_path_info->path_info, NV_DISPLAYCONFIG_SAVE_TO_PERSISTENCE | NV_DISPLAYCONFIG_DRIVER_RELOAD_ALLOWED);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Saving display config");
        return false;
    }

    return true;
}

static bool _display_config_visit(displayconfig_path_info_t *displayconfig_path_info, NvU32 display_id, bool (*display_config_filter)(NV_DISPLAYCONFIG_PATH_TARGET_INFO *target_info))
{
    NV_DISPLAYCONFIG_PATH_TARGET_INFO *target_info;

    for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) {
        target_info = &displayconfig_path_info->path_info[i].targetInfo[i];

        if (display_id != 0 && target_info->displayId != display_id) {
            continue;
        }

        return display_config_filter(target_info);
    }

    return false;
}

static void _ensure_drs_settings_folder_exists()
{
    // Even on a fresh install, this might not exist which result
    // in NVAPI_ACCESS_DENIED errors on many (or even all?) nvapi DRS functions
    if (!path_exists(NV_DRS_SETTINGS_FOLDER)) {
        printfln_err("NOTE: DRS settings folder to store profiles does not exist, creating...");
        path_mkdir(NV_DRS_SETTINGS_FOLDER);
    }
}

// -------------------------------------------------------------------------------------------------

static bool _nv_info(const nv_api_t *nv_api)
{
    NvAPI_ShortString interface_version;
    NvAPI_ShortString driver_version;
    NvU32 driver_version_number;
    NvAPI_Status status;

    printfln_err("Getting interface version...");

    status = nv_api->NvAPI_GetInterfaceVersionStringEx(interface_version);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting interface version string");
        return false;
    }

    printfln_err("Getting driver and branch version...");

    status = nv_api->NvAPI_SYS_GetDriverAndBranchVersion(&driver_version_number, driver_version);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting driver and branch version");
        return false;
    }

    printfln_out("Interface version: %s", interface_version);
    printfln_out("Driver version: %s", driver_version);
    printfln_out("Driver version number: %lu", driver_version_number);

    return true;
}

static bool _profile_create(const nv_api_t *nv_api, const char *profile_name, bool overwrite_exists)
{
    NvDRSSessionHandle session;
    NVDRS_PROFILE profile;
    wchar_t *profile_name_wstr;
    NvAPI_Status status;
    NvDRSProfileHandle profile_handle;

    assert(nv_api);
    assert(profile_name);

    if (!_session_create_and_settings_load(nv_api, &session)) {
        return false;
    }

    profile.version = NVDRS_PROFILE_VER;

    profile_name_wstr = str_widen(profile_name);
    wstr_cpy(profile.profileName, sizeof(profile.profileName), profile_name_wstr);
    free(profile_name_wstr);

    printfln_err("Creating profile %s...", profile_name);

    status = nv_api->NvAPI_DRS_CreateProfile(session, &profile, &profile_handle);

    if (status == NVAPI_PROFILE_NAME_IN_USE && overwrite_exists) {
        printfln_err("Profile %s already exists, enabled overwrite existing...", profile_name);

        if (!_profile_get(nv_api, profile_name, session, &profile_handle)) {
            _session_destroy(nv_api, session);

            return false;
        }

        status = nv_api->NvAPI_DRS_DeleteProfile(session, profile_handle);
        
        if (status != NVAPI_OK) {
            PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Deleting existing profile");
            return false;
        }

        // Retry
        status = nv_api->NvAPI_DRS_CreateProfile(session, &profile, &profile_handle);
    }

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Creating driver profile");
        
        _session_destroy(nv_api, session);
        return false;
    }

    return _settings_save_and_session_destroy(nv_api, session);
}

static bool _profile_delete(const nv_api_t *nv_api, const char *profile_name)
{
    NvDRSSessionHandle session;
    NvDRSProfileHandle profile;
    NvAPI_Status status;

    assert(nv_api);
    assert(profile_name);

    if (!_session_create_and_settings_load(nv_api, &session)) {
        return false;
    }

    if (!_profile_get(nv_api, profile_name, session, &profile)) {
        _session_destroy(nv_api, session);

        return false;
    }

    printfln_err("Deleting profile %s...", profile_name);

    status = nv_api->NvAPI_DRS_DeleteProfile(session, profile);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Deleting driver profile %s", profile_name);

        _session_destroy(nv_api, session);

        return false;
    }

    return _settings_save_and_session_destroy(nv_api, session);
}

static bool _profile_application_add(const nv_api_t *nv_api, const char *profile_name, const char *application_name)
{
    NvDRSSessionHandle session;
    NvDRSProfileHandle profile;
    NvAPI_Status status;
    NVDRS_APPLICATION application;
    wchar_t *application_name_wstr;

    assert(nv_api);
    assert(profile_name);
    assert(application_name);

    if (!_session_create_and_settings_load(nv_api, &session)) {
        return false;
    }

    if (!_profile_get(nv_api, profile_name, session, &profile)) {
        _session_destroy(nv_api, session);

        return false;
    }

    application.version = NVDRS_APPLICATION_VER;

    application_name_wstr = str_widen(application_name);
    wstr_cpy(application.appName, sizeof(application.appName), application_name_wstr);
    free(application_name_wstr);

    printfln_err("Adding application %s to profile %s...", application_name, profile_name);

    status = nv_api->NvAPI_DRS_CreateApplication(session, profile, &application);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Adding application %s to profile %s", application_name, profile_name);

        _session_destroy(nv_api, session);

        return false;
    }

    return _settings_save_and_session_destroy(nv_api, session);
}

static bool _profile_gsync_disable(const nv_api_t *nv_api, const char *profile_name)
{
    NvDRSSessionHandle session;
    NvDRSProfileHandle profile;
    NVDRS_SETTING setting;

    assert(nv_api);
    assert(profile_name);

    if (!_session_create_and_settings_load(nv_api, &session)) {
        return false;
    }

    if (!_profile_get(nv_api, profile_name, session, &profile)) {
        _session_destroy(nv_api, session);

        return false;
    }
    
    setting.version = NVDRS_SETTING_VER;
    setting.settingId = VRR_APP_OVERRIDE_ID;
    setting.settingType = NVDRS_DWORD_TYPE;
    setting.u32PredefinedValue = VRR_APP_OVERRIDE_FIXED_REFRESH;
    setting.u32CurrentValue = VRR_APP_OVERRIDE_FIXED_REFRESH;

    printfln_err("Disabling G-SYNC for profile %s...", profile_name);

    if (!_profile_setting_set(nv_api, session, profile, &setting)) {
        _session_destroy(nv_api, session);

        return false;
    }

    return _settings_save_and_session_destroy(nv_api, session);
}

static bool _profile_gpu_power_state_max(const nv_api_t *nv_api, const char *profile_name)
{
    NvDRSSessionHandle session;
    NvDRSProfileHandle profile;
    NVDRS_SETTING setting;

    assert(nv_api);
    assert(profile_name);

    if (!_session_create_and_settings_load(nv_api, &session)) {
        return false;
    }

    if (!_profile_get(nv_api, profile_name, session, &profile)) {
        _session_destroy(nv_api, session);

        return false;
    }

    setting.version = NVDRS_SETTING_VER;
    setting.settingId = PREFERRED_PSTATE_ID;
    setting.settingType = NVDRS_DWORD_TYPE;
    setting.u32PredefinedValue = PREFERRED_PSTATE_PREFER_MAX;
    setting.u32CurrentValue = PREFERRED_PSTATE_PREFER_MAX;

    printfln_err("Setting GPU power state to maximum for profile %s...", profile_name);

    if (!_profile_setting_set(nv_api, session, profile, &setting)) {
        _session_destroy(nv_api, session);  

        return false;
    }

    return _settings_save_and_session_destroy(nv_api, session);
}

static bool _display_primary_display_id(const nv_api_t *nv_api)
{
    NvAPI_Status status;
    NvPhysicalGpuHandle gpu_handle[NVAPI_MAX_PHYSICAL_GPUS];
    NvU32 gpu_count;
    NvU32 display_id_count;
    NV_GPU_DISPLAYIDS *display_ids;
 
    assert(nv_api);
    
    status = nv_api->NvAPI_EnumPhysicalGPUs(gpu_handle, &gpu_count);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Enumerating physical GPUs");
        return false;
    }

    // Assuming first physical GPU, first display = primary display
    if (gpu_count > 0) {
        status = nv_api->NvAPI_GPU_GetConnectedDisplayIds(gpu_handle[0], NULL, &display_id_count, 0);

        if (status != NVAPI_OK) {
            PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting connected display count of GPU");
            return false;
        }

        if (display_id_count > 0) {
            display_ids = (NV_GPU_DISPLAYIDS*) xmalloc(sizeof(NV_GPU_DISPLAYIDS) * display_id_count);
            
            for (NvU32 j = 0; j < display_id_count; j++) {
                display_ids[j].version = NV_GPU_DISPLAYIDS_VER;
            }

            status = nv_api->NvAPI_GPU_GetConnectedDisplayIds(gpu_handle[0], display_ids, &display_id_count, 0);

            if (status != NVAPI_OK) {
                free(display_ids);
                PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting connected display ids of GPU");
                return false;
            }

            printfln_out("0x%lX", display_ids[0].displayId);

            free(display_ids);

            return true;
        }
    }

    return false;
}

static bool _displays_list(const nv_api_t *nv_api)
{
    NvAPI_Status status;
    NvPhysicalGpuHandle gpu_handle[NVAPI_MAX_PHYSICAL_GPUS];
    NvU32 gpu_count;
    NvU32 display_id_count;
    NV_GPU_DISPLAYIDS *display_ids;
    const char *connector_type;

    assert(nv_api);
    
    status = nv_api->NvAPI_EnumPhysicalGPUs(gpu_handle, &gpu_count);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Enumerating physical GPUs");
        return false;
    }

    printfln_err("Display ID, Connector Type, Active, Connected, Physically Connected");

    for (NvU32 i = 0; i < gpu_count; i++) {
        // Get display count per GPU
        display_id_count = 0;
        status = nv_api->NvAPI_GPU_GetConnectedDisplayIds(gpu_handle[i], NULL, &display_id_count, 0);

        if (status != NVAPI_OK) {
            PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting connected display count of GPU");
            return false;
        }

        if (display_id_count > 0) {
            display_ids = (NV_GPU_DISPLAYIDS*) xmalloc(sizeof(NV_GPU_DISPLAYIDS) * display_id_count);
            
            for (NvU32 j = 0; j < display_id_count; j++) {
                display_ids[j].version = NV_GPU_DISPLAYIDS_VER;
            }

            status = nv_api->NvAPI_GPU_GetConnectedDisplayIds(gpu_handle[i], display_ids, &display_id_count, 0);

            if (status != NVAPI_OK) {
                free(display_ids);
                PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting connected display ids of GPU");
                return false;
            }

            for (NvU32 j = 0; j < display_id_count; j++) {
                switch (display_ids[j].connectorType) {
                    case NV_MONITOR_CONN_TYPE_VGA:
                        connector_type = "VGA";
                        break;
                    case NV_MONITOR_CONN_TYPE_COMPONENT:
                        connector_type = "Component";
                        break;
                    case NV_MONITOR_CONN_TYPE_SVIDEO:
                        connector_type = "S-Video";
                        break;
                    case NV_MONITOR_CONN_TYPE_HDMI:
                        connector_type = "HDMI";
                        break;
                    case NV_MONITOR_CONN_TYPE_DVI:
                        connector_type = "DVI";
                        break;
                    case NV_MONITOR_CONN_TYPE_LVDS:
                        connector_type = "LVDS";
                        break;
                    case NV_MONITOR_CONN_TYPE_DP:
                        connector_type = "DP";
                        break;
                    case NV_MONITOR_CONN_TYPE_COMPOSITE:
                        connector_type = "Composite";
                        break;
                    default:
                        connector_type = "unknown";
                        break;
                }

                printfln_out("0x%lX, %s, %s, %s, %s", 
                    display_ids[j].displayId,
                    connector_type,
                    display_ids[j].isActive ? "active" : "inactive",
                    display_ids[j].isConnected ? "connected" : "disconnected",
                    display_ids[j].isPhysicallyConnected ? "connected" : "disconnected");
            }

            free(display_ids);
        }
    }

    return true;    
}

static bool _display_config_print(const nv_api_t *nv_api, NvU32 display_id)
{
    displayconfig_path_info_t displayconfig_path_info;
    NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 *target_info;
    NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 *source_mode_info;

    assert(nv_api);

    if (!_display_config_get(nv_api, &displayconfig_path_info)) {
        return false;
    }

    if (display_id != 0) {
        printfln_err("Applying display ID filter: %lX", display_id);
    }

    printfln_err("Num total adapters in config: %ld", displayconfig_path_info.path_info_count);

    for (NvU32 i = 0; i < displayconfig_path_info.path_info_count; i++) {
        printfln_err("Num total displays in adapter: %ld", displayconfig_path_info.path_info[i].targetInfoCount);

        for (NvU32 j = 0; j < displayconfig_path_info.path_info[i].targetInfoCount; j++) {
            target_info = &displayconfig_path_info.path_info[i].targetInfo[j];
            source_mode_info = &displayconfig_path_info.path_info[i].sourceModeInfo[j];

            if (display_id != 0 && target_info->displayId != display_id) {
                continue;
            }

            printfln_out("--------------------------------");
            printfln_out("Adapter source ID %ld", displayconfig_path_info.path_info[i].sourceId);
            printfln_out("Display ID: %lX", target_info->displayId);
            printfln_out("Resolution width: %ld", source_mode_info->resolution.width);
            printfln_out("Resolution height: %ld", source_mode_info->resolution.height);
            printfln_out("Color depth: %d", source_mode_info->colorFormat);
            printfln_out("Position x: %d", source_mode_info->position.x);
            printfln_out("Position y: %d", source_mode_info->position.y);
            printfln_out("Spanning orientation: %d", source_mode_info->spanningOrientation);
            printfln_out("GDI primary: %d", source_mode_info->bGDIPrimary);
            printfln_out("SLI focus: %d", source_mode_info->bSLIFocus);

            if (target_info->details) {
                switch (target_info->details->rotation) {
                    case NV_ROTATE_0:
                        printfln_out("Rotation: 0 degrees");
                        break;
                    case NV_ROTATE_90:
                        printfln_out("Rotation: 90 degrees");
                        break;
                    case NV_ROTATE_180:
                        printfln_out("Rotation: 180 degrees");
                        break;
                    case NV_ROTATE_270:
                        printfln_out("Rotation: 270 degrees");
                        break;
                    case NV_ROTATE_IGNORED:
                        printfln_out("Rotation: ignored");
                        break;
                    default:
                        printfln_out("Rotation: unknown (%d)", target_info->details->rotation);
                        break;
                }

                switch (target_info->details->scaling) {
                    case NV_SCALING_DEFAULT:
                        printfln_out("Scaling: default");
                        break;
                    case NV_SCALING_GPU_SCALING_TO_CLOSEST:
                        printfln_out("Scaling: GPU scaling to closest");
                        break;
                    case NV_SCALING_GPU_SCALING_TO_NATIVE:
                        printfln_out("Scaling: GPU scaling to native");
                        break;
                    case NV_SCALING_GPU_SCANOUT_TO_NATIVE:
                        printfln_out("Scaling: GPU scanout to native");
                        break;
                    case NV_SCALING_GPU_SCALING_TO_ASPECT_SCANOUT_TO_NATIVE:
                        printfln_out("Scaling: GPU scaling to aspect scanout to native");
                        break;
                    case NV_SCALING_GPU_SCALING_TO_ASPECT_SCANOUT_TO_CLOSEST:
                        printfln_out("Scaling: GPU scaling to aspect scanout to closest");
                        break;
                    case NV_SCALING_GPU_SCANOUT_TO_CLOSEST:
                        printfln_out("Scaling: GPU scanout to closest");
                        break;
                    case NV_SCALING_GPU_INTEGER_ASPECT_SCALING:
                        printfln_out("Scaling: GPU integer aspect scaling");
                        break;
                    default:
                        printfln_out("Scaling: unknown (%d)", target_info->details->scaling);
                        break;
                }

                printfln_out("Refresh rate (non interlaced): %ld", target_info->details->refreshRate1K * 1000);
                printfln_out("Interlaced: %d", target_info->details->interlaced);
                printfln_out("Primary: %d", target_info->details->primary);
                printfln_out("Disable virtual mode support: %d", target_info->details->disableVirtualModeSupport);
                printfln_out("Is preferred unscaled target: %d", target_info->details->isPreferredUnscaledTarget);

                switch (target_info->details->connector) {
                    case NVAPI_GPU_CONNECTOR_VGA_15_PIN:
                        printfln_out("Connector: VGA 15 Pin");
                        break;
                    case NVAPI_GPU_CONNECTOR_TV_COMPOSITE:
                        printfln_out("Connector: TV Composite");
                        break;
                    case NVAPI_GPU_CONNECTOR_TV_SVIDEO:
                        printfln_out("Connector: TV S-Video");
                        break;
                    case NVAPI_GPU_CONNECTOR_TV_HDTV_COMPONENT:
                        printfln_out("Connector: TV HDTV Component");
                        break;
                    case NVAPI_GPU_CONNECTOR_TV_SCART:
                        printfln_out("Connector: TV SCART");
                        break;
                    case NVAPI_GPU_CONNECTOR_TV_COMPOSITE_SCART_ON_EIAJ4120:
                        printfln_out("Connector: TV Composite SCART on EIAJ4120");
                        break;
                    case NVAPI_GPU_CONNECTOR_TV_HDTV_EIAJ4120:
                        printfln_out("Connector: TV HDTV EIAJ4120");
                        break;
                    case NVAPI_GPU_CONNECTOR_PC_POD_HDTV_YPRPB:
                        printfln_out("Connector: PC POD HDTV YPRPB");
                        break;
                    case NVAPI_GPU_CONNECTOR_PC_POD_SVIDEO:
                        printfln_out("Connector: PC POD S-Video");
                        break;
                    case NVAPI_GPU_CONNECTOR_PC_POD_COMPOSITE:
                        printfln_out("Connector: PC POD Composite");
                        break;
                    case NVAPI_GPU_CONNECTOR_DVI_I_TV_SVIDEO:
                        printfln_out("Connector: DVI-I TV S-Video");
                        break;
                    case NVAPI_GPU_CONNECTOR_DVI_I_TV_COMPOSITE:
                        printfln_out("Connector: DVI-I TV Composite");
                        break;
                    case NVAPI_GPU_CONNECTOR_DVI_I:
                        printfln_out("Connector: DVI-I");
                        break;
                    case NVAPI_GPU_CONNECTOR_DVI_D:
                        printfln_out("Connector: DVI-D");
                        break;
                    case NVAPI_GPU_CONNECTOR_ADC:
                        printfln_out("Connector: ADC");
                        break;
                    case NVAPI_GPU_CONNECTOR_LFH_DVI_I_1:
                        printfln_out("Connector: LFH DVI-I 1");
                        break;
                    case NVAPI_GPU_CONNECTOR_LFH_DVI_I_2:
                        printfln_out("Connector: LFH DVI-I 2");
                        break;
                    case NVAPI_GPU_CONNECTOR_SPWG:
                        printfln_out("Connector: SPWG");
                        break;
                    case NVAPI_GPU_CONNECTOR_OEM:
                        printfln_out("Connector: OEM");
                        break;
                    case NVAPI_GPU_CONNECTOR_DISPLAYPORT_EXTERNAL:
                        printfln_out("Connector: DisplayPort External");
                        break;
                    case NVAPI_GPU_CONNECTOR_DISPLAYPORT_INTERNAL:
                        printfln_out("Connector: DisplayPort Internal");
                        break;
                    case NVAPI_GPU_CONNECTOR_DISPLAYPORT_MINI_EXT:
                        printfln_out("Connector: DisplayPort Mini External");
                        break;
                    case NVAPI_GPU_CONNECTOR_HDMI_A:
                        printfln_out("Connector: HDMI A");
                        break;
                    case NVAPI_GPU_CONNECTOR_HDMI_C_MINI:
                        printfln_out("Connector: HDMI C Mini");
                        break;
                    case NVAPI_GPU_CONNECTOR_LFH_DISPLAYPORT_1:
                        printfln_out("Connector: LFH DisplayPort 1");
                        break;
                    case NVAPI_GPU_CONNECTOR_LFH_DISPLAYPORT_2:
                        printfln_out("Connector: LFH DisplayPort 2");
                        break;
                    case NVAPI_GPU_CONNECTOR_VIRTUAL_WFD:
                        printfln_out("Connector: Virtual WFD");
                        break;
                    case NVAPI_GPU_CONNECTOR_USB_C:
                        printfln_out("Connector: USB C");
                        break;
                    default:
                        printfln_out("Connector: unknown (%d)", target_info->details->connector);
                        break;
                }

                switch (target_info->details->tvFormat) {
                    case NV_DISPLAY_TV_FORMAT_NONE:
                        printfln_out("TV format: none");
                        break;
                    case NV_DISPLAY_TV_FORMAT_SD_NTSCM:
                        printfln_out("TV format: SD NTSC-M");
                        break;
                    case NV_DISPLAY_TV_FORMAT_SD_NTSCJ:
                        printfln_out("TV format: SD NTSC-J");
                        break;
                    case NV_DISPLAY_TV_FORMAT_SD_PALM:
                        printfln_out("TV format: SD PAL-M");
                        break;
                    case NV_DISPLAY_TV_FORMAT_SD_PALBDGH:
                        printfln_out("TV format: SD PAL-BDGH");
                        break;
                    case NV_DISPLAY_TV_FORMAT_SD_PALN:
                        printfln_out("TV format: SD PAL-N");
                        break;
                    case NV_DISPLAY_TV_FORMAT_SD_PALNC:
                        printfln_out("TV format: SD PAL-NC");
                        break;
                    case NV_DISPLAY_TV_FORMAT_SD_576i:
                        printfln_out("TV format: SD 576i");
                        break;
                    case NV_DISPLAY_TV_FORMAT_SD_480i:
                        printfln_out("TV format: SD 480i");
                        break;
                    case NV_DISPLAY_TV_FORMAT_ED_480p:
                        printfln_out("TV format: ED 480p");
                        break;
                    case NV_DISPLAY_TV_FORMAT_ED_576p:
                        printfln_out("TV format: ED 576p");
                        break;
                    case NV_DISPLAY_TV_FORMAT_HD_720p:
                        printfln_out("TV format: HD 720p");
                        break;
                    case NV_DISPLAY_TV_FORMAT_HD_1080i:
                        printfln_out("TV format: HD 1080i");
                        break;
                    case NV_DISPLAY_TV_FORMAT_HD_1080p:
                        printfln_out("TV format: HD 1080p");
                        break;
                    case NV_DISPLAY_TV_FORMAT_HD_720p50:
                        printfln_out("TV format: HD 720p50");
                        break;
                    case NV_DISPLAY_TV_FORMAT_HD_1080p24:
                        printfln_out("TV format: HD 1080p24");
                        break;
                    case NV_DISPLAY_TV_FORMAT_HD_1080i50:
                        printfln_out("TV format: HD 1080i50");
                        break;
                    case NV_DISPLAY_TV_FORMAT_HD_1080p50:
                        printfln_out("TV format: HD 1080p50");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp30_3840:
                        printfln_out("TV format: UHD 4Kp30 3840");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp25_3840:
                        printfln_out("TV format: UHD 4Kp25 3840");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp24_3840:
                        printfln_out("TV format: UHD 4Kp24 3840");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp24_SMPTE:
                        printfln_out("TV format: UHD 4Kp24 SMPTE");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp50_3840:
                        printfln_out("TV format: UHD 4Kp50 3840");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp60_3840:
                        printfln_out("TV format: UHD 4Kp60 3840");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp30_4096:
                        printfln_out("TV format: UHD 4Kp30 4096");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp25_4096:
                        printfln_out("TV format: UHD 4Kp25 4096");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp24_4096:
                        printfln_out("TV format: UHD 4Kp24 4096");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp50_4096:
                        printfln_out("TV format: UHD 4Kp50 4096");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp60_4096:
                        printfln_out("TV format: UHD 4Kp60 4096");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_8Kp24_7680:
                        printfln_out("TV format: UHD 8Kp24 7680");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_8Kp25_7680:
                        printfln_out("TV format: UHD 8Kp25 7680");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_8Kp30_7680:
                        printfln_out("TV format: UHD 8Kp30 7680");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_8Kp48_7680:
                        printfln_out("TV format: UHD 8Kp48 7680");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_8Kp50_7680:
                        printfln_out("TV format: UHD 8Kp50 7680");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_8Kp60_7680:
                        printfln_out("TV format: UHD 8Kp60 7680");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_8Kp100_7680:
                        printfln_out("TV format: UHD 8Kp100 7680");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_8Kp120_7680:
                        printfln_out("TV format: UHD 8Kp120 7680");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp48_3840:
                        printfln_out("TV format: UHD 4Kp48 3840");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp48_4096:
                        printfln_out("TV format: UHD 4Kp48 4096");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp100_3840:
                        printfln_out("TV format: UHD 4Kp100 3840");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp100_4096:
                        printfln_out("TV format: UHD 4Kp100 4096");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp120_4096:
                        printfln_out("TV format: UHD 4Kp120 4096");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp120_3840:
                        printfln_out("TV format: UHD 4Kp120 3840");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp100_5120:
                        printfln_out("TV format: UHD 4Kp100 5120");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp120_5120:
                        printfln_out("TV format: UHD 4Kp120 5120");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp24_5120:
                        printfln_out("TV format: UHD 4Kp24 5120");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp25_5120:
                        printfln_out("TV format: UHD 4Kp25 5120");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp30_5120:
                        printfln_out("TV format: UHD 4Kp30 5120");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp48_5120:
                        printfln_out("TV format: UHD 4Kp48 5120");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp50_5120:
                        printfln_out("TV format: UHD 4Kp50 5120");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_4Kp60_5120:
                        printfln_out("TV format: UHD 4Kp60 5120");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_10Kp24_10240:
                        printfln_out("TV format: UHD 10Kp24 10240");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_10Kp25_10240:
                        printfln_out("TV format: UHD 10Kp25 10240");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_10Kp30_10240:
                        printfln_out("TV format: UHD 10Kp30 10240");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_10Kp48_10240:
                        printfln_out("TV format: UHD 10Kp48 10240");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_10Kp50_10240:
                        printfln_out("TV format: UHD 10Kp50 10240");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_10Kp60_10240:
                        printfln_out("TV format: UHD 10Kp60 10240");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_10Kp100_10240:
                        printfln_out("TV format: UHD 10Kp100 10240");
                        break;
                    case NV_DISPLAY_TV_FORMAT_UHD_10Kp120_10240:
                        printfln_out("TV format: UHD 10Kp120 10240");
                        break;
                    case NV_DISPLAY_TV_FORMAT_SD_OTHER:
                        printfln_out("TV format: SD Other");
                        break;
                    case NV_DISPLAY_TV_FORMAT_ED_OTHER:
                        printfln_out("TV format: ED Other");
                        break;
                    case NV_DISPLAY_TV_FORMAT_HD_OTHER:
                        printfln_out("TV format: HD Other");
                        break;
                    case NV_DISPLAY_TV_FORMAT_ANY:
                        printfln_out("TV format: Any");
                        break;
                    default:
                        printfln_out("TV format: unknown (%d)", target_info->details->tvFormat);
                        break;
                }

                switch (target_info->details->timingOverride) {
                    case NV_TIMING_OVERRIDE_CURRENT:
                        printfln_out("Timing override: current");
                        break;
                    case NV_TIMING_OVERRIDE_AUTO:
                        printfln_out("Timing override: auto");
                        break;
                    case NV_TIMING_OVERRIDE_EDID:
                        printfln_out("Timing override: EDID");
                        break;
                    case NV_TIMING_OVERRIDE_DMT:
                        printfln_out("Timing override: VESA DMT");
                        break;
                    case NV_TIMING_OVERRIDE_DMT_RB:
                        printfln_out("Timing override: VESA DMT RB");
                        break;
                    case NV_TIMING_OVERRIDE_CVT:
                        printfln_out("Timing override: VESA CVT");
                        break;
                    case NV_TIMING_OVERRIDE_CVT_RB:
                        printfln_out("Timing override: VESA CVT RB");
                        break;
                    case NV_TIMING_OVERRIDE_GTF:
                        printfln_out("Timing override: VESA GTF");
                        break;
                    case NV_TIMING_OVERRIDE_EIA861:
                        printfln_out("Timing override: EIA 861x pre-defined timing");
                        break;
                    case NV_TIMING_OVERRIDE_ANALOG_TV:
                        printfln_out("Timing override: analog SD/HDTV timing");
                        break;
                    case NV_TIMING_OVERRIDE_CUST:
                        printfln_out("Timing override: NV custom timings");
                        break;
                    case NV_TIMING_OVERRIDE_NV_PREDEFINED:
                        printfln_out("Timing override: NV pre-defined timing (basically the PsF timings)");
                        break;
                    case NV_TIMING_OVERRIDE_NV_ASPR:
                        printfln_out("Timing override: NV ASPR");
                        break;
                    case NV_TIMING_OVERRIDE_SDI:
                        printfln_out("Timing override: SDI");
                        break;
                    case NV_TIMING_OVRRIDE_MAX:
                        printfln_out("Timing override: max");
                        break;
                    default:
                        printfln_out("Timing override: unknown (%d)", target_info->details->timingOverride);
                        break;
                }

                printfln_out("Override custom (backend raster) timing");

                printfln_out("Horizontal visible: %d", target_info->details->timing.HVisible);
                printfln_out("Horizontal border: %d", target_info->details->timing.HBorder);
                printfln_out("Horizontal front porch: %d", target_info->details->timing.HFrontPorch);
                printfln_out("Horizontal sync width: %d", target_info->details->timing.HSyncWidth);
                printfln_out("Horizontal total: %d", target_info->details->timing.HTotal);
                printfln_out("Horizontal sync polarity: %s", target_info->details->timing.HSyncPol == 1 ? "negative" : "positive");

                printfln_out("Vertical visible: %d", target_info->details->timing.VVisible);
                printfln_out("Vertical border: %d", target_info->details->timing.VBorder);
                printfln_out("Vertical front porch: %d", target_info->details->timing.VFrontPorch);
                printfln_out("Vertical sync width: %d", target_info->details->timing.VSyncWidth);
                printfln_out("Vertical total: %d", target_info->details->timing.VTotal);
                printfln_out("Vertical sync polarity: %s", target_info->details->timing.VSyncPol == 1 ? "negative" : "positive");

                printfln_out("Interlaced: %s", target_info->details->timing.interlaced == 1 ? "interlaced" : "progressive");
                printfln_out("Pixel clock: %lu kHz", target_info->details->timing.pclk * 10);

                printfln_out("Flag: 0x%lX", target_info->details->timing.etc.flag);
                printfln_out("Logical refresh rate to present: %d", target_info->details->timing.etc.rr);
                printfln_out("Physical vertical refresh rate: %f Hz", target_info->details->timing.etc.rrx1k * 0.001);
                printfln_out("Display aspect ratio: %lu:%lu", target_info->details->timing.etc.aspect >> 16, target_info->details->timing.etc.aspect & 0xFFFF);
                printfln_out("Bit-wise pixel repetition factor (1 = no pixel repetition, 2 = each pixel repeats twice horizontally, etc.): %d", target_info->details->timing.etc.rep);
                printfln_out("Timing status: 0x%lX", target_info->details->timing.etc.status);
                printfln_out("Timing name: %s", target_info->details->timing.etc.name);
            }

            printfln_out("--------------------------------");
        }
    }

    _display_config_free(&displayconfig_path_info);

    return true;
}

static bool _custom_resolution_set(
        const nv_api_t *nv_api,
        NvU32 display_id,
        uint16_t screen_width,
        uint16_t screen_height,
        float screen_refresh_rate,
        uint8_t test_only_timeout_sec)
{
    NV_CUSTOM_DISPLAY custom_display;
    NV_TIMING_FLAG flag;
    NV_TIMING_INPUT timing;
    NvAPI_Status status;
    
    assert(nv_api);

    memset(&custom_display, 0, sizeof(NV_CUSTOM_DISPLAY));
    memset(&flag, 0, sizeof(NV_TIMING_FLAG));
    memset(&timing, 0, sizeof(NV_TIMING_INPUT));

    custom_display.version = NV_CUSTOM_DISPLAY_VER;
    custom_display.width = screen_width;
    custom_display.height = screen_height;
    custom_display.depth = 32;
    custom_display.colorFormat = NV_FORMAT_A8R8G8B8;
    custom_display.srcPartition.x = 0;
    custom_display.srcPartition.y = 0;
    custom_display.srcPartition.w = 1;
    custom_display.srcPartition.h = 1;
    custom_display.xRatio = 1;
    custom_display.yRatio = 1;

    timing.version = NV_TIMING_INPUT_VER;
    timing.height = screen_height;
    timing.width = screen_width;
    timing.rr = screen_refresh_rate;
    timing.flag = flag;
    timing.type = NV_TIMING_OVERRIDE_CVT_RB;

    printfln_err("Calculating custom display timing for display ID 0x%lX", display_id);
    
    status = nv_api->NvAPI_DISP_GetTiming(display_id, &timing, &custom_display.timing);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Calculating custom display timing");
        return false;
    }

    status = nv_api->NvAPI_DISP_TryCustomDisplay(&display_id, 1, &custom_display);

    if (status != NVAPI_OK) {
        PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Trying custom display configuration");
        return false;
    }

    if (test_only_timeout_sec == 0) {
        printfln_err("Persisting custom display configuration");

        status = nv_api->NvAPI_DISP_SaveCustomDisplay(&display_id, 1, true, true);

        if (status != NVAPI_OK) {
            PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Saving custom display configuration");
            return false;
        }

        printfln_err("Custom display configuration persisted");
    } else {
        printfln_err("Trying custom display configuration for %d seconds...", test_only_timeout_sec);

        Sleep(test_only_timeout_sec * 1000);

        printfln_err("Reverting custom display configuration, not persisting");

        status = nv_api->NvAPI_DISP_RevertCustomDisplayTrial(&display_id, 1);

        if (status != NVAPI_OK) {
            PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Reverting custom display configuration");
            return false;
        }
    }

    return true;
}

static bool _display_config_gpu_scaling_enable_filter(NV_DISPLAYCONFIG_PATH_TARGET_INFO *target_info)
{
    if (target_info->details == NULL) {
        printfln_err("ERROR: No details found for target info");
        return false;
    }

    if (target_info->details->scaling == NV_SCALING_GPU_SCALING_TO_NATIVE) {
        printfln_err("GPU scaling to native resolution is already enabled");
        return false;
    } else {
        target_info->details->scaling = NV_SCALING_GPU_SCALING_TO_NATIVE;
        printfln_err("Enabling GPU scaling to native resolution");
        return true;
    }
}

static bool _display_config_gpu_scaling_to_native_resolution_enable(const nv_api_t *nv_api, NvU32 display_id)
{
    displayconfig_path_info_t displayconfig_path_info;
    bool changes_to_save;

    assert(nv_api);

    if (!_display_config_get(nv_api, &displayconfig_path_info)) {
        return false;
    }

    changes_to_save = _display_config_visit(&displayconfig_path_info, display_id, _display_config_gpu_scaling_enable_filter);

    if (changes_to_save) {
        if (!_display_config_set(nv_api, &displayconfig_path_info)) {
            _display_config_free(&displayconfig_path_info);
            return false;
        }
    }

    _display_config_free(&displayconfig_path_info);

    return true;
}

// -------------------------------------------------------------------------------------------------

static void _print_synopsis()
{
    printfln_err("Usage: nvgpu <command> <subcommand>");
    printfln_err("Commands:");
    printfln_err("  nv");
    printfln_err("    info                            Print information about the NVAPI module and driver");
    printfln_err("");
    printfln_err("  profile");
    printfln_err("    create <profile_name> [--overwrite-exists]");  
    printfln_err("                                  Create a new driver profile with the given name. If the profile already exists, the --overwrite-exists flag can be used to overwrite the existing profile.");
    printfln_err("    delete <profile_name>         Delete an existing driver profile");
    printfln_err("    application-add <profile_name> <application_name>");
    printfln_err("                                  Add an application to the driver profile. This will apply the profile to the application when the driver detects a process being launched, e.g. MyApplication.exe");
    printfln_err("    gsync-disable <profile_name>  Disable G-SYNC for the driver profile");
    printfln_err("    gpu-power-state-max <profile_name>  Set GPU power state to maximum for the driver profile");
    printfln_err("");
    printfln_err("  display");
    printfln_err("    primary-display-id            Print the ID of the primary display");
    printfln_err("    list                          List all connected displays and their display IDs");
    printfln_err("    config [display_id]           Print the current display configurations. Optionally, specify a display ID to get the configuration of that display only");
    printfln_err("    custom-resolution-set <display_id> <screen_width> <screen_height> <screen_refresh_rate>");
    printfln_err("                                  Set a custom display mode with the given parameters for the given display ID. The settings are persisted immediately. Ensure you tested these before with the custom-display-test command.");
    printfln_err("    custom-resolution-test <display_id> <screen_width> <screen_height> <screen_refresh_rate> [--test-timeout-secs n]");
    printfln_err("                                  Test a custom display mode for a limited amount of time. This will revert the display mode after the given amount of seconds and not persist the changes.");
    printfln_err("                                  test-timeout-secs: Optional. Number of seconds to test the custom display mode for. Default is 10 seconds.");
    printfln_err("    gpu-scaling-to-native-resolution-enable <display_id> Enable GPU scaling to native resolution for the given display ID");
}

static bool _cmd_nv_info(const nv_api_t *nv_api)
{
    return _nv_info(nv_api);
}

static bool _cmd_profile_create(const nv_api_t *nv_api, int argc, char **argv)
{
    const char *profile_name;
    bool overwrite_exists;

    if (argc < 1) {
        _print_synopsis();
        printfln_err("ERROR: Insufficient arguments");
        return false;
    }

    profile_name = argv[0];

    overwrite_exists = false;

    for (int i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "--overwrite-exists")) {
            overwrite_exists = true;
        }
    }

    return _profile_create(nv_api, profile_name, overwrite_exists);
}

static bool _cmd_profile_delete(const nv_api_t *nv_api, int argc, char **argv)
{
    const char *profile_name;

    if (argc < 1) {
        _print_synopsis();
        printfln_err("ERROR: Insufficient arguments");
        return false;
    }

    profile_name = argv[0];

    return _profile_delete(nv_api, profile_name);
}

static bool _cmd_profile_application_add(const nv_api_t *nv_api, int argc, char **argv)
{
    const char *profile_name;
    const char *application_name;

    if (argc < 2) {
        _print_synopsis();
        printfln_err("ERROR: Insufficient arguments");
        return false;
    }

    profile_name = argv[0];
    application_name = argv[1];

    return _profile_application_add(nv_api, profile_name, application_name);
}

static bool _cmd_profile_gsync_disable(const nv_api_t *nv_api, int argc, char **argv)
{
    const char *profile_name;

    if (argc < 1) {
        _print_synopsis();
        printfln_err("ERROR: Insufficient arguments");
        return false;
    }

    profile_name = argv[0];

    return _profile_gsync_disable(nv_api, profile_name);
}

static bool _cmd_profile_gpu_power_state_max(const nv_api_t *nv_api, int argc, char **argv)
{
    const char *profile_name;

    if (argc < 1) {
        _print_synopsis();
        printfln_err("ERROR: Insufficient arguments");
        return false;
    }

    profile_name = argv[0];

    return _profile_gpu_power_state_max(nv_api, profile_name);
}

static bool _cmd_display_primary_display_id(const nv_api_t *nv_api)
{
    return _display_primary_display_id(nv_api);
}

static bool _cmd_display_list(const nv_api_t *nv_api)
{
    return _displays_list(nv_api);
}

static bool _cmd_display_config(const nv_api_t *nv_api, int argc, char **argv)
{
    uint32_t display_id;

    if (argc > 0) {
        if (argv[0][0] == '0' && argv[0][1] == 'x') {
            display_id = strtoul(argv[0] + 2, NULL, 16);
        } else {
            display_id = strtoul(argv[0], NULL, 10);
        }
    } else {
        display_id = 0;
    }

    return _display_config_print(nv_api, display_id);
}

static bool _cmd_custom_resolution_set(const nv_api_t *nv_api, int argc, char **argv)
{
    uint32_t display_id;
    uint16_t screen_width;
    uint16_t screen_height;
    float screen_refresh_rate;

    if (argc < 4) {
        _print_synopsis();
        printfln_err("ERROR: Insufficient arguments");
        return false;
    }

    if (argv[0][0] == '0' && argv[0][1] == 'x') {
        display_id = strtoul(argv[0] + 2, NULL, 16);
    } else {
        display_id = strtoul(argv[0], NULL, 10);
    }
    
    screen_width = atoi(argv[1]);
    screen_height = atoi(argv[2]);
    screen_refresh_rate = atof(argv[3]);

    printf_err("Setting custom resolution for display ID 0x%X: %dx%d@%f\n", display_id, screen_width, screen_height, screen_refresh_rate);

    return _custom_resolution_set(
        nv_api,
        display_id,
        screen_width,
        screen_height,
        screen_refresh_rate,
        0);
}

static bool _cmd_custom_resolution_test(const nv_api_t *nv_api, int argc, char **argv)
{
    uint32_t display_id;
    uint16_t screen_width;
    uint16_t screen_height;
    float screen_refresh_rate;
    uint8_t test_only_timeout_sec;

    if (argc < 5) {
        _print_synopsis();
        printfln_err("ERROR: Insufficient arguments");
        return false;
    }

    if (argv[0][0] == '0' && argv[0][1] == 'x') {
        display_id = strtoul(argv[0] + 2, NULL, 16);
    } else {
        display_id = strtoul(argv[0], NULL, 10);
    }

    screen_width = atoi(argv[1]);
    screen_height = atoi(argv[2]);
    screen_refresh_rate = atof(argv[3]);

    // Sane defaults for optional parameters
    test_only_timeout_sec = 10;

    for (int i = 4; i < argc; i++) {
        if (!strcmp(argv[i], "--test-timeout-secs")) {
            if (i + 1 < argc) {
                test_only_timeout_sec = atoi(argv[++i]);

                if (test_only_timeout_sec == 0) {
                    printfln_err("ERROR: Time out parameter must be greater than 0");
                    return false;
                }
            } else {
                printfln_err("ERROR: Missing argument for --test-timeout-secs");
                return false;
            }
        }
    }

    printf_err("Testing custom resolution for display ID %u: %dx%d@%f, timeout %d seconds\n", display_id, screen_width, screen_height, screen_refresh_rate, test_only_timeout_sec);

    return _custom_resolution_set(
        nv_api,
        display_id,
        screen_width,
        screen_height,
        screen_refresh_rate,
        test_only_timeout_sec);
}

static bool _cmd_display_gpu_scaling_to_native_resolution_enable(const nv_api_t *nv_api, int argc, char **argv)
{
    uint32_t display_id;

    if (argc < 1) {
        _print_synopsis();
        printfln_err("ERROR: Insufficient arguments");
        return false;
    }
    
    if (argv[0][0] == '0' && argv[0][1] == 'x') {
        display_id = strtoul(argv[0] + 2, NULL, 16);
    } else {
        display_id = strtoul(argv[0], NULL, 10);
    }

    return _display_config_gpu_scaling_to_native_resolution_enable(nv_api, display_id); 
}

// -------------------------------------------------------------------------------------------------

int main(int argc, char **argv)
{
    const char *command;
    const char *sub_command;
    nv_module_t *nv_module;
    nv_api_t nv_api;
    NvAPI_Status status;
    bool result;

    if (argc < 3) {
        _print_synopsis();
        printfln_err("ERROR: Insufficient arguments");
        exit(1);
    }

    command = argv[1];
    sub_command = argv[2];

    printfln_err("Loading NVAPI module...");

    nv_module_load(&nv_module);
    nv_module_api_get(nv_module, &nv_api);

    printfln_err("Initializing NVAPI...");

    status = nv_api.NvAPI_Initialize();

    if (status != NVAPI_OK) {
        NvAPI_ShortString error_str;
        nv_api.NvAPI_GetErrorMessage(status, error_str);
        fprintf(stderr, "ERROR: Initializing NVAPI, reason: %s\n", error_str);

        nv_module_free(&nv_module);
        exit(1);
    }

    printfln_err("Running command: %s %s", command, sub_command);

    if (!strcmp(command, "nv")) {
        if (!strcmp(sub_command, "info")) {
            result = _cmd_nv_info(&nv_api);
        } else {
            printfln_err("ERROR: Unknown sub-command: %s", sub_command);
            _print_synopsis();
            result = false;
        }
    } else if (!strcmp(command, "profile")) {
        _ensure_drs_settings_folder_exists();
        
        if (!strcmp(sub_command, "create")) {
            result = _cmd_profile_create(&nv_api, argc - 3, argv + 3);
        } else if (!strcmp(sub_command, "delete")) {
            result = _cmd_profile_delete(&nv_api, argc - 3, argv + 3);
        } else if (!strcmp(sub_command, "application-add")) {
            result = _cmd_profile_application_add(&nv_api, argc - 3, argv + 3);
        } else if (!strcmp(sub_command, "gsync-disable")) {
            result = _cmd_profile_gsync_disable(&nv_api, argc - 3, argv + 3);
        } else if (!strcmp(sub_command, "gpu-power-state-max")) {
            result = _cmd_profile_gpu_power_state_max(&nv_api, argc - 3, argv + 3);
        } else {
            printfln_err("ERROR: Unknown sub-command: %s", sub_command);
            _print_synopsis();
            result = false;
        }
    } else if (!strcmp(command, "display")) {
        if (!strcmp(sub_command, "primary-display-id")) {
            result = _cmd_display_primary_display_id(&nv_api);
        } else if (!strcmp(sub_command, "list")) {
            result = _cmd_display_list(&nv_api);
        } else if (!strcmp(sub_command, "config")) {
            result = _cmd_display_config(&nv_api, argc - 3, argv + 3);
        } else if (!strcmp(sub_command, "custom-resolution-set")) {
            result = _cmd_custom_resolution_set(&nv_api, argc - 3, argv + 3);
        } else if (!strcmp(sub_command, "custom-resolution-test")) {
            result = _cmd_custom_resolution_test(&nv_api, argc - 3, argv + 3);
        } else if (!strcmp(sub_command, "gpu-scaling-to-native-resolution-enable")) {
            result = _cmd_display_gpu_scaling_to_native_resolution_enable(&nv_api, argc - 3, argv + 3);
        } else {
            printfln_err("ERROR: Unknown sub-command: %s", sub_command);
            _print_synopsis();
            result = false;
        }
    } else {
        printfln_err("ERROR: Unknown command: %s", command);
        _print_synopsis();
        result = false;
    }

    status = nv_api.NvAPI_Unload();

    if (status != NVAPI_OK) {
        NvAPI_ShortString error_str;
        nv_api.NvAPI_GetErrorMessage(status, error_str);
        printfln_err("ERROR: Unloading NVAPI, reason: %s", error_str);
    }

    nv_module_free(&nv_module);
    
    if (result) {
        printfln_err("Command completed successfully");
        exit(0);
    } else {
        printfln_err("Command failed");
        exit(1);
    }
}