#include <napi.h>
#ifdef _WIN32
#include <windows.h>
#include <winioctl.h>
#include <string>
#include <vector>
#include <iostream>
#endif

using namespace Napi;

#ifdef _WIN32
// Windows-specific implementation using DeviceIoControl
class WindowsOpticalDrive {
public:
    static bool EjectDrive(const std::string& driveLetter) {
        std::wstring devicePath = L"\\\\.\\" + std::wstring(driveLetter.begin(), driveLetter.end());
        
        HANDLE hDevice = CreateFileW(
            devicePath.c_str(),
            GENERIC_READ,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL,
            OPEN_EXISTING,
            0,
            NULL
        );

        if (hDevice == INVALID_HANDLE_VALUE) {
            return false;
        }

        DWORD bytesReturned;
        BOOL result = DeviceIoControl(
            hDevice,
            IOCTL_STORAGE_EJECT_MEDIA,
            NULL,
            0,
            NULL,
            0,
            &bytesReturned,
            NULL
        );

        CloseHandle(hDevice);
        return result != 0;
    }

    static bool LoadDrive(const std::string& driveLetter) {
        std::wstring devicePath = L"\\\\.\\" + std::wstring(driveLetter.begin(), driveLetter.end());
        
        HANDLE hDevice = CreateFileW(
            devicePath.c_str(),
            GENERIC_READ,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL,
            OPEN_EXISTING,
            0,
            NULL
        );

        if (hDevice == INVALID_HANDLE_VALUE) {
            return false;
        }

        DWORD bytesReturned;
        BOOL result = DeviceIoControl(
            hDevice,
            IOCTL_STORAGE_LOAD_MEDIA,
            NULL,
            0,
            NULL,
            0,
            &bytesReturned,
            NULL
        );

        CloseHandle(hDevice);
        return result != 0;
    }
};
#endif

// Node.js wrapper functions
Value EjectDrive(const CallbackInfo& info) {
    Env env = info.Env();

    if (info.Length() < 1 || !info[0].IsString()) {
        TypeError::New(env, "Drive letter argument required").ThrowAsJavaScriptException();
        return env.Null();
    }

    std::string driveLetter = info[0].As<String>().Utf8Value();

#ifdef _WIN32
    bool success = WindowsOpticalDrive::EjectDrive(driveLetter);
    return Boolean::New(env, success);
#else
    TypeError::New(env, "Native optical drive operations only supported on Windows").ThrowAsJavaScriptException();
    return env.Null();
#endif
}

Value LoadDrive(const CallbackInfo& info) {
    Env env = info.Env();

    if (info.Length() < 1 || !info[0].IsString()) {
        TypeError::New(env, "Drive letter argument required").ThrowAsJavaScriptException();
        return env.Null();
    }

    std::string driveLetter = info[0].As<String>().Utf8Value();

#ifdef _WIN32
    bool success = WindowsOpticalDrive::LoadDrive(driveLetter);
    return Boolean::New(env, success);
#else
    TypeError::New(env, "Native optical drive operations only supported on Windows").ThrowAsJavaScriptException();
    return env.Null();
#endif
}

Object Init(Env env, Object exports) {
    exports.Set(String::New(env, "ejectDrive"), Function::New(env, EjectDrive));
    exports.Set(String::New(env, "loadDrive"), Function::New(env, LoadDrive));
    return exports;
}

NODE_API_MODULE(optical_drive_native, Init)