#include "OpticalDrive.hpp"
#include "IoCtl.hpp"
#include "SDL.h"

#ifdef _WIN32
#include <Windows.h>
#endif

#define BUFFER_SIZE (0x1000)
#define NODISC ("NO DISC")

namespace Li::Scsi {


    void OpticalDrive::getDriveInformation(std::string drv) {
#ifdef _WIN32
        this->drivePath = drv.substr(0, drv.size() - 1);

        std::wstring wdrive = std::wstring(this->drivePath.begin(), this->drivePath.end());
        WCHAR* wChrVolumeName = new WCHAR[BUFFER_SIZE];

        if (!GetVolumeInformation(wdrive.c_str(), wChrVolumeName, BUFFER_SIZE, NULL, NULL, NULL, NULL, NULL))
        {
            this->volumeName = NODISC;
            this->discInDrive = false;
        }
        else {
            std::wstring wVolumeName = std::wstring(wChrVolumeName);
            this->volumeName = std::string(wVolumeName.begin(), wVolumeName.end());
            this->discInDrive = true;
        }

        delete wChrVolumeName;
#else
#error no way to get optical drive information on this platform
#endif
    }

    OpticalDrive::OpticalDrive(std::string drv) {
        this->getDriveInformation(drv);
    
        if (this->discInDrive) {
            IoCtl* ctl = new IoCtl(this->drivePath);
            this->supportedSpeeds = ctl->GetSupportedReadSpeeds();
            this->sectors = ctl->GetTotalSectors();

            this->hasCss = dvdcss_is_scrambled(ctl->GetDvdCssHandle());
            if (this->hasCss) {
                this->discKey = (std::byte*)dvdcss_get_cur_disckey(ctl->GetDvdCssHandle());
            }
            else {
                this->discKey = nullptr;
            }

            delete ctl;
        }
    }
    OpticalDrive::~OpticalDrive() {
        if(this->discInDrive)
            delete this->supportedSpeeds;
    }

    std::vector<uint32_t>* OpticalDrive::SupportedSpeeds() {
        return this->supportedSpeeds;
    }

    std::string OpticalDrive::VolumeName() {
        return this->volumeName;
    }

    std::string OpticalDrive::DrivePath() {
        return this->drivePath;
    }

    bool OpticalDrive::HasCss() {
        return this->hasCss;
    }

    std::byte* OpticalDrive::DiscKey() {
        return this->discKey;
    }

    uint32_t OpticalDrive::Sectors() {
        return this->sectors;
    }

    bool OpticalDrive::DiscInDrive() {
        return this->discInDrive;
    }

    std::vector<OpticalDrive*>* OpticalDrive::ListOpticalDrives() {
        std::vector<OpticalDrive*>* drives = new std::vector<OpticalDrive*>();
#ifdef _WIN32
        WCHAR* drivesList = new WCHAR[BUFFER_SIZE];
        GetLogicalDriveStrings(BUFFER_SIZE, drivesList);

        WCHAR* curDrive = drivesList;
        while (*curDrive != '\0') {

            UINT driveType = GetDriveType(curDrive);
            if (driveType == DRIVE_CDROM) {
                std::wstring wDrive = std::wstring(curDrive);
                std::string drive = std::string(wDrive.begin(), wDrive.end());
                OpticalDrive* opticalDrive = new OpticalDrive(drive);
                drives->push_back(opticalDrive);
            }
            curDrive += lstrlen(curDrive) + 1;
        }

        delete drivesList;
#else

        /*
        *   Looks like SDL cdrom support got removed at some point .. oh well
        * 
        // Use SDL
        int numCdDrives = SDL_CDNumDrives();
        for (int i = 0; i < numCdDrives; i++) {
            OpticalDrive* opticalDrive = new OpticalDrive(SDL_CDName(i));
            drives->push_back(opticalDrive);
        }
        */

#error No way to list optical drives on this platform!
#endif
        return drives;
    }

}