#include "TitleKey.hpp"
#include <lib9660.h>
#include <iostream>
#include <vector>
#include <filesystem>

#include "../Utils.hpp"

namespace Li::Dvd {


	static std::vector<sectorInfo*> sectorsList;
	static dvdcss_t drv;
	static int depth = 0;


	bool TitleKey::readSector(l9660_fs* fs, void* buf, uint32_t sector) {
		dvdcss_seek(drv, sector, DVDCSS_SEEK_KEY);
		dvdcss_read(drv, buf, 1, DVDCSS_READ_DECRYPT);

		return !dvdcss_was_error(drv);
	}

	bool TitleKey::IsSectorInFile(uint32_t currentSector) {
		for (sectorInfo* inf : sectorsList) {
			if (currentSector >= inf->startSector && currentSector < inf->endSector)
				return true;
		}
		return false;
	}

	uint32_t TitleKey::GetDistanceToNextFile(uint32_t currentSector) {

		uint32_t lowestDistance = 0xFFFFFFFF;
		for (sectorInfo* inf : sectorsList) {
			if (currentSector >= inf->startSector && currentSector < inf->endSector)
				return 0;

			int32_t distance = (inf->startSector - currentSector);
			if (distance < 0) continue;

			if (distance < lowestDistance) {
				lowestDistance = distance;
			}
		}

		return lowestDistance;

	}

	sectorInfo* TitleKey::GetSectorInfo(uint32_t currentSector) {

		for (sectorInfo* inf : sectorsList) {
			if (currentSector >= inf->startSector && currentSector < inf->endSector) {
				return inf;
			}
		}

		return nullptr;

	}

	bool TitleKey::readDir(l9660_dir* dir, int depth=0) {

		if (depth > 10) return false;

		bool success = true;
		for(int cnt = 0; cnt < 90000; ++cnt) {
			if (cnt >= 90000) return false;

			l9660_dirent* dent;
			if (l9660_readdir(dir, &dent) != L9660_OK) return false;

			if (dent == nullptr)
				break;
			
			if (dent->name_len < 2)
				continue;

			uint32_t cSector = *(uint32_t*)dent->sector.le;

			if ((dent->flags & DENT_ISDIR) != 0) {
				l9660_dir ndir;
				if (l9660_opendirat(&ndir, dir, dent->name) != L9660_OK) return false;
				success = readDir(&ndir, depth + 1);
			}
			else if (TitleKey::GetSectorInfo(cSector) == nullptr) {
				sectorInfo* file = new sectorInfo();
				file->startSector = cSector;

				dvdcss_title(drv, file->startSector);
				char* titleKey = dvdcss_get_cur_titlekey(drv);

				// check title is encrypted ...
				if (memcmp(titleKey, "\0\0\0\0\0", 5)) {
					// Round to sector boundary
					uint32_t sz = (*(uint32_t*)dent->size.le);
					if((sz % DVDCSS_BLOCK_SIZE) != 0)
						sz += (DVDCSS_BLOCK_SIZE - (sz % DVDCSS_BLOCK_SIZE));
					file->endSector = (file->startSector + (sz / DVDCSS_BLOCK_SIZE));

					sectorsList.push_back(file);
				}
				else {
					delete file;
				}
			}
			
		}


		return success;
	}

	bool TitleKey::FindTitleKeys(dvdcss_t drive) {
		drv = drive;
		sectorsList = std::vector<sectorInfo*>();

		l9660_dir rootDir;
		l9660_fs isoFs;
		l9660_status status;

		if (l9660_openfs(&isoFs, TitleKey::readSector) == L9660_OK) {
			if (l9660_fs_open_root(&rootDir, &isoFs) == L9660_OK) {
				return TitleKey::readDir(&rootDir);
			}
		}

		return false;
	}

	void TitleKey::FreeMemory() {
		for (sectorInfo* inf : sectorsList) {
			delete inf;
		}
		
		sectorsList.clear();

	}
}