#include "xbox_ref_bridge.h"

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <string.h>
#include "xbox_ref/utils.h"
#include "xbox_ref/unlock.h"
#include "xbox_ref/xbox_ref_log.h"

#define printf xbox_ref_printf

static char xbox_ref_drive_letter_from_device(const char *device)
{
    if (!device || !device[0]) return 0;
    if (device[0] && device[1] == ':') return device[0];
    if (device[0] == '\\' && device[1] == '\\' && device[2] == '.' && device[3] == '\\' && device[4] && device[5] == ':') return device[4];
    return device[0];
}

static int xbox_ref_gdr8050l_dump_core(HANDLE hDevice, const char *device, const char *filename, char mode, BOOL ownsHandle)
{
    char driveLetter;
    XboxGameInfo game;
    BOOL isDualLayer = FALSE;
    bool isDualLayerBool = false;
    char mediaId[33] = {0};
    char finalFilename[MAX_PATH] = {0};
    BOOL filenameOverride;
    uint32_t totalSectors = 0;
    DWORD dwBytesReturned = 0;
    BOOL volumeLocked = FALSE;
    BOOL ok = FALSE;

    filenameOverride = (filename && filename[0]);

    if (mode != '1' && mode != '2') {
        xbox_ref_log_open_for_target(filename, 0);
        printf("[XBOX-REF] Fatal: unsupported Xbox reference dump mode '%c'.\n", mode);
        xbox_ref_log_close();
        return 1;
    }

    driveLetter = xbox_ref_drive_letter_from_device(device);
    if (!driveLetter) {
        xbox_ref_log_open_for_target(filename, 0);
        printf("[XBOX-REF] Fatal: could not parse drive letter from device '%s'.\n", device ? device : "(null)");
        xbox_ref_log_close();
        return 1;
    }

    xbox_ref_log_open_for_target(filename, driveLetter);
    printf("[XBOX-REF] Using copied original GDR-8050L dumper flow for drive %c:.\n", driveLetter);
    printf("[XBOX-REF] Handle mode: %s.\n", ownsHandle ? "opened by xbox_ref wrapper" : "using FriiDump's existing drive HANDLE");
    printf("[XBOX-REF] Preserving reference timing sleeps: tray 3000ms, ready-settle 1500ms/10000ms fallback, RefreshVolume 1000ms + post-refresh 2000ms.\n");

    if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) {
        printf("[XBOX-REF] Fatal: invalid drive handle for %c:.\n", driveLetter);
        goto cleanup;
    }

    printf("Opening drive %c: for Atomic Handshake...\n", driveLetter);
    EnsureDriveReady(hDevice, 30000);
    UnlockDrive(hDevice);

    AutomateTrayCycle(hDevice);

    printf("Re-applying 8050L Handshake after Media Change...\n");
    UnlockDrive(hDevice);

    RefreshVolume(hDevice);
    Sleep(2000);
    EnsureDriveReady(hDevice, 30000);

    printf("Setting drive to maximum performance mode...\n");
    SetDriveSpeedMax(hDevice);

    if (!DeviceIoControl(hDevice, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL)) {
        printf("[!] Warning: Could not lock volume. Close any open folders pointing to the drive.\n");
        printf("[XBOX-REF] The reference dumper treats this as fatal before XBE/title probing.\n");
        goto cleanup;
    }
    volumeLocked = TRUE;

    printf("Searching for Xbox Game Signature (Raw SCSI Scan)...\n");
    game = GetXboxGameInfo(hDevice);
    printf("Game Title Raw: %s\n", game.TitleName);
    ReadXboxGameDir(hDevice);

    totalSectors = GetXboxPhysicalSectors(hDevice);
    isDualLayerBool = false;
    GetDiscMetadata(hDevice, &totalSectors, &isDualLayerBool, &game);
    isDualLayer = isDualLayerBool ? TRUE : FALSE;
    GetMediaID(hDevice, mediaId);

    if (game.Success) {
        char titleCopy[sizeof(game.TitleName)];
        memset(titleCopy, 0, sizeof(titleCopy));
        strncpy(titleCopy, game.TitleName, sizeof(titleCopy) - 1);
        SanitizeFilename(titleCopy);
        if (filenameOverride) {
            strncpy(finalFilename, filename, sizeof(finalFilename) - 1);
            finalFilename[sizeof(finalFilename) - 1] = '\0';
            printf("[XBOX-REF] Output filename override: %s\n", finalFilename);
        } else {
            snprintf(finalFilename, sizeof(finalFilename), "%s[%s].%s",
                     titleCopy[0] ? titleCopy : "XboxDisc",
                     mediaId[0] ? mediaId : "UNKNOWN_ID",
                     (mode == '1') ? "iso" : "xiso");
            printf("[XBOX-REF] XBE/DMI-derived output filename: %s\n", finalFilename);
            xbox_ref_log_retarget(finalFilename);
        }
        DisplayXboxGameInfo(game);
        printf("\n--- DISC INFORMATION ---\n");
        printf("Physical Sectors: %u\n", totalSectors);
        printf("Layers:           %d\n", isDualLayer ? 2 : 1);
        printf("Media ID:         %s\n", mediaId);
        printf("Target File:      %s\n", finalFilename);
        printf("-------------------------------\n\n");
    } else {
        if (filenameOverride) {
            strncpy(finalFilename, filename, sizeof(finalFilename) - 1);
            finalFilename[sizeof(finalFilename) - 1] = '\0';
            printf("Error: Could not retrieve Xbox game information; using override filename %s.\n", finalFilename);
        } else {
            printf("Error: Could not retrieve Xbox game information and no output filename override was supplied.\n");
            printf("       Rerun with -i <file> or -X <file> if metadata probing cannot complete.\n");
            goto cleanup;
        }
    }

    if (totalSectors != (uint32_t)(REDUMP_SECTORS)) {
        printf("[WARNING] Unlocked drive-reported sectors: %u does not match expected Xbox game-view sectors: %u\n", totalSectors, (uint32_t)(REDUMP_SECTORS));
        printf("-Homebrew games may have different sizes.\n\n");
    }

    if (totalSectors == 0) {
        printf("Error: Could not determine total sectors for dumping.\n");
        goto cleanup;
    }

    printf("Starting dump with copied DumpXboxGameDisc() mode %c...\n", mode);
    DumpXboxGameDisc(hDevice, finalFilename, mode, totalSectors, isDualLayerBool, FALSE);
    ok = TRUE;

cleanup:
    if (volumeLocked)
        DeviceIoControl(hDevice, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
    if (ownsHandle && hDevice != INVALID_HANDLE_VALUE && hDevice != NULL) {
        printf("Issuing STOP UNIT / spin-down after dump attempt... ");
        if (StopDriveUnit(hDevice)) printf("OK\n"); else printf("FAILED\n");
        CloseDrive(hDevice);
    } else {
        printf("[XBOX-REF] Returning to FriiDump; final STOP UNIT cleanup will use the same handle.\n");
    }
    printf("[XBOX-REF] Log file complete: %s\n", xbox_ref_log_path() ? xbox_ref_log_path() : "(unavailable)");
    xbox_ref_log_close();
    return ok ? 0 : 1;
}

int xbox_ref_gdr8050l_dump_with_handle(void *native_handle, const char *device, const char *filename, char mode)
{
    return xbox_ref_gdr8050l_dump_core((HANDLE)native_handle, device, filename, mode, FALSE);
}

int xbox_ref_gdr8050l_dump(const char *device, const char *filename, char mode)
{
    char driveLetter;
    HANDLE hDevice;

    driveLetter = xbox_ref_drive_letter_from_device(device);
    if (!driveLetter) {
        xbox_ref_log_open_for_target(filename, 0);
        printf("[XBOX-REF] Fatal: could not parse drive letter from device '%s'.\n", device ? device : "(null)");
        xbox_ref_log_close();
        return 1;
    }

    hDevice = OpenDrive(driveLetter);
    if (hDevice == INVALID_HANDLE_VALUE) {
        xbox_ref_log_open_for_target(filename, driveLetter);
        printf("[XBOX-REF] Fatal: Cannot open drive %c:. Run as Administrator.\n", driveLetter);
        xbox_ref_log_close();
        return 1;
    }

    return xbox_ref_gdr8050l_dump_core(hDevice, device, filename, mode, TRUE);
}
#else
int xbox_ref_gdr8050l_dump_with_handle(void *native_handle, const char *device, const char *filename, char mode)
{
    (void)native_handle; (void)device; (void)filename; (void)mode; return 1;
}

int xbox_ref_gdr8050l_dump(const char *device, const char *filename, char mode)
{
    (void)device; (void)filename; (void)mode; return 1;
}
#endif
