#include "pch.h"
#include "HSTraining.h"
#include <filesystem>
#include <vector>

std::string HSTraining::normalizePathSeparators(const std::string& path) {
	std::string normalized = path;
	std::replace(normalized.begin(), normalized.end(), '\\', '/');
	return normalized;
}
//gives the filename to the shot class
void HSTraining::setUpShot() {


	fs::path pdPath = gameWrapper->GetDataFolder();
	std::string dPath = normalizePathSeparators(pdPath.string());
	std::string oldBindFile = dPath + "/HSShots/bind.txt";
	if (fs::exists(oldBindFile)) {
		try {
			fs::remove(oldBindFile);
		}
		catch (std::exception& e) {
			LOG("Could not delete old bind file, please delete it manually");
		}
	}
	read();
	cvarManager->loadCfg("HSTraining.cfg");
	IUse = 1;

	for (int i = 0; i < 13; i++) {
		ranActive[i] = false;
	}

	if (!fs::exists(dPath + "/HSShots/TrainingPacks")|| fs::exists(dPath + "/HSShots/Shots1.txt")) {
		oldToNewOrganization();
	}
	CVarWrapper TrainingPackNumCvar = cvarManager->getCvar("TrainingPackNum");
	if (!TrainingPackNumCvar) { return; }
	TrainingPackNumCvar.setValue(0);
	getTrainingPacks();
	loadTrainingPack(0);

}
void HSTraining::createTrainingPack(std::string packName) {
	fs::path pdPath = gameWrapper->GetDataFolder();
	std::string dPath = normalizePathSeparators(pdPath.string());
	std::string trainingPackDir = dPath + "/HSShots/TrainingPacks";
	std::string trainingPackLoc = trainingPackDir + "/" + packName;
	if (!fs::exists(trainingPackLoc)) {
		fs::create_directories(trainingPackLoc);
		trainingPacks.push_back(packName);
		Shot shot;
		shot.createDefaultShotFile(trainingPackLoc + "/");
		loadTrainingPack(trainingPacks.size() - 1);
	}
	else {
		LOG("Training Pack Exists");
	}

}



//moves all shots in the older organization scheme to the new one.
void HSTraining::oldToNewOrganization() {
	fs::path pdPath = gameWrapper->GetDataFolder();
	std::string dPath = normalizePathSeparators(pdPath.string());
	std::string sourceDir = dPath + "/HSShots";
	std::string trainingPackDir = dPath + "/HSShots/TrainingPacks";
	std::string newTrainingPackLoc = trainingPackDir + "/Your Old Shots";
	fs::create_directories(trainingPackDir);
	for (int i = 2; fs::exists(newTrainingPackLoc);i++) {
		if (newTrainingPackLoc.back() == 's') {
			newTrainingPackLoc = newTrainingPackLoc + std::to_string(i);
		}
		else {

			newTrainingPackLoc = newTrainingPackLoc.substr(0, newTrainingPackLoc.size() - 1) + std::to_string(i);
			
		}
		
	}
	
	try {
		// Ensure target directory exists
		if (!fs::exists(newTrainingPackLoc)) {
			fs::create_directories(newTrainingPackLoc);
		}

		// Iterate through all files in the source directory and copies them to the specified directory
		for (const auto& entry : fs::directory_iterator(sourceDir)) {
			if (entry.is_regular_file()) {
				const fs::path& filePath = entry.path();
				std::string baseName = filePath.stem().string();
				while (std::isdigit(baseName.back())) {
					baseName.pop_back();
				}

				// Only copy .txt files
				if (filePath.extension() == ".txt" && baseName == "Shots") {
					fs::path targetPath = fs::path(newTrainingPackLoc) / filePath.filename();
					fs::copy_file(filePath, targetPath, fs::copy_options::overwrite_existing);
				}
			}
		}
		//Iterate through all files in the source directory and deletes only the moved shots.
		for (const auto& entry : fs::directory_iterator(sourceDir)) {
			if (entry.is_regular_file()) {
				const fs::path& filePath = entry.path();
				std::string baseName = filePath.stem().string();
				while (std::isdigit(baseName.back())) {
					baseName.pop_back();
				}

				// Only copy .txt files
				if (filePath.extension() == ".txt" && baseName == "Shots") {
					try {
						fs::remove(filePath);
					}
					catch (std::exception& e) {
						LOG("Could not delete old shot file, please delete it manually");
					}
					
				}
			}
		}
	}
	catch (const std::exception& e) {
		//std::cerr << "Error: " << e.what() << '\n';
	}

}
//gets all shotNames for the current training path
std::vector<std::string> HSTraining::getAllShotNames(int TrainingPackNum) {
	fs::path pdPath = gameWrapper->GetDataFolder();
	std::string dPath = normalizePathSeparators(pdPath.string());
	if(TrainingPackNum >= trainingPacks.size() || TrainingPackNum < 0) {
		return {};
	}
	std::string trainingPackFolderLoc = dPath + "/HSShots/TrainingPacks/" + trainingPacks[TrainingPackNum];
	std::vector<std::string> fileList;

	try {
		for (const auto& entry : fs::directory_iterator(trainingPackFolderLoc)) {
			if (entry.is_regular_file()) {
				const fs::path& filePath = entry.path();
				std::string baseName = filePath.stem().string();
				if (filePath.extension() == ".txt" || filePath.extension() == ".shot") {
					std::string fileName = entry.path().filename().string();
					char cutoff = '.';
					std::string name;
					size_t pos = fileName.find_last_of(cutoff); // find last occurrence
					if (pos != std::string::npos) {
						name = fileName.substr(0, pos); // keep everything before it
					}
					fileList.push_back(name);
				}

			}
		}
	}
	catch (const fs::filesystem_error& e) {
		//std::cerr << "Filesystem error: " << e.what() << '\n';
	}
	return fileList;

}
//copies the training pack
void HSTraining::copyTrainingPack(int packNum, std::string newName) {
	fs::path pdPath = gameWrapper->GetDataFolder();
	std::string dPath = normalizePathSeparators(pdPath.string());
	std::string trainingPackDir = dPath + "/HSShots/TrainingPacks/";
	std::string trainingPackLoc = trainingPackDir + trainingPacks[packNum] + "/";
	std::string trainingPackNewLoc = trainingPackDir + newName + "/";
	if (fs::exists(trainingPackLoc) && !fs::exists(trainingPackNewLoc)) {
		fs::create_directories(trainingPackNewLoc);

		std::vector<std::string> allShotNamesToCopy = getAllShotNames(packNum);

		for (std::string shotName1 : allShotNamesToCopy) {
			Shot original;
			Shot copy;
			original.readShotFile(trainingPackLoc, shotName1);
			copy.createShotFile(trainingPackNewLoc, original.getHeight(), original.getLength(), original.getWidth(), original.getSpeedX(), original.getSpeedY(), original.getSpeedZ(), original.getCarX(), original.getCarY(), original.getCarZ(), original.getCarYaw(), original.getShotName(), original.getBallSpeed(), original.getCarPitch(), original.getCarRoll());
			//updates old shots
			if (copy.getBallSpeed() == 0) {
				copy.setShot(copy.getHeight(), copy.getLength(), copy.getWidth(), copy.getSpeedX(), copy.getSpeedY(), copy.getSpeedZ(), copy.getCarX(), copy.getCarY(), copy.getCarZ(), copy.getCarYaw(), copy.getShotName(), 3000, copy.getCarPitch(), copy.getCarRoll());
			}
			if (copy.getCarPitch() == 137463) {
				copy.setShot(copy.getHeight(), copy.getLength(), copy.getWidth(), copy.getSpeedX(), copy.getSpeedY(), copy.getSpeedZ(), copy.getCarX(), copy.getCarY(), copy.getCarZ(), copy.getCarYaw(), copy.getShotName(), copy.getBallSpeed(), 0, copy.getCarRoll());
			}
			if (copy.getCarRoll() == 137463) {
				copy.setShot(copy.getHeight(), copy.getLength(), copy.getWidth(), copy.getSpeedX(), copy.getSpeedY(), copy.getSpeedZ(), copy.getCarX(), copy.getCarY(), copy.getCarZ(), copy.getCarYaw(), copy.getShotName(), copy.getBallSpeed(), copy.getCarPitch(), 0);
			}
		}
		trainingPacks.push_back(newName);

	}
	else {
		LOG("Training Pack Exists");
	}
}
void HSTraining::renameTrainingPack(int packNum, std::string newName) {

	fs::path dataPath = gameWrapper->GetDataFolder();
	std::string baseDir = normalizePathSeparators(dataPath.string()) + "/HSShots/TrainingPacks/";

	std::string oldName = trainingPacks[packNum];
	fs::path oldDir = baseDir + oldName;
	fs::path newDir = baseDir + newName;

	try {
		if (!fs::exists(oldDir)) {
			LOG("Original training pack folder does not exist.");
			return;
		}

		if (fs::exists(newDir)) {
			LOG("Cannot rename. A training pack with that name already exists.");
			return;
		}

		// Attempt to rename the directory
		fs::rename(oldDir, newDir);
		

		// Update internal name list
		trainingPacks[packNum] = newName;

		//// Refresh the list (optional)
		//getTrainingPacks  

		LOG("Training pack renamed successfully.");
		return;
	}
	catch (const fs::filesystem_error& e) {
		//std::cerr << "Filesystem error: " << e.what() << '\n';
		return;
	}
	catch (const std::exception& e) {
		//std::cerr << "Error: " << e.what() << '\n';
		return;
	}

}
void HSTraining::deleteTrainingPack(int packNum) {
	fs::path dataPath = gameWrapper->GetDataFolder();
	std::string baseDir = normalizePathSeparators(dataPath.string()) + "/HSShots/TrainingPacks/";
	std::string trainingPackPath = normalizePathSeparators(dataPath.string()) + "/HSShots/TrainingPacks/" + trainingPacks[packNum];

	if (fs::exists(trainingPackPath)) {
		try {
				fs::remove_all(trainingPackPath);
				LOG("TrainingPack removed");
		}
		catch (std::exception& e) {
			LOG("Could not delete TrainingPack, please delete it manually");
		}
	}
	getTrainingPacks();
	if (trainingPacks.empty()) {
		LOG("TrainingPack empty");
		createTrainingPack("Empty Training Pack");
		getTrainingPacks();
	}
	CVarWrapper TrainingPackNumCvar = cvarManager->getCvar("TrainingPackNum");
	if (!TrainingPackNumCvar) { return; }
	int TrainingPackNum = TrainingPackNumCvar.getIntValue();
	if (TrainingPackNum == packNum) {
		TrainingPackNumCvar.setValue(0);
		loadTrainingPack(0);
	}
}
int extractLeadingNumber(const std::string& s) {
	int num = 0;
	size_t i = 0;
	// Extract digits from start until non-digit or ')'
	while (i < s.size() && (std::isdigit(s[i]) || s[i] == ' ')) {
		if (std::isdigit(s[i])) {
			num = num * 10 + (s[i] - '0');
		}
		i++;
	}
	if (num == 0) {
		num = 2147483640;
	}
	return num;
}

bool helpSortArray(const std::string& a, const std::string& b) {
	int numA = extractLeadingNumber(a);
	int numB = extractLeadingNumber(b);

	if (numA != numB) return numA < numB;

	// If numbers are equal, compare strings after the number part
	size_t posA = a.find(')');
	size_t posB = b.find(')');

	std::string restA = (posA != std::string::npos) ? a.substr(posA + 1) : "";
	std::string restB = (posB != std::string::npos) ? b.substr(posB + 1) : "";

	return restA < restB;
}

//loads the selected training pack
void HSTraining::loadTrainingPack(int packNum) {
	fs::path pdPath = gameWrapper->GetDataFolder();
	std::string dPath = normalizePathSeparators(pdPath.string());
	if (!trainingPacks.empty()) {
		std::string trainingPackLoc = dPath + "/HSShots/TrainingPacks/" + trainingPacks[packNum] + "/";
		std::vector<Shot> SD;
		std::vector<std::string> allShotNamesToCopy = getAllShotNames(packNum);
		if (!allShotNamesToCopy.empty()) {
			for (std::string shotName1 : allShotNamesToCopy) {
				Shot shot;
				bool shouldLoad = shot.readShotFile(trainingPackLoc, shotName1);
				//updates old shots
				if (shot.getBallSpeed() == 0) {
					shot.setShot(shot.getHeight(), shot.getLength(), shot.getWidth(), shot.getSpeedX(), shot.getSpeedY(), shot.getSpeedZ(), shot.getCarX(), shot.getCarY(), shot.getCarZ(), shot.getCarYaw(), shot.getShotName(), 3000, shot.getCarPitch(), shot.getCarRoll());
				}
				if (shot.getCarPitch() == 137463) {
					shot.setShot(shot.getHeight(), shot.getLength(), shot.getWidth(), shot.getSpeedX(), shot.getSpeedY(), shot.getSpeedZ(), shot.getCarX(), shot.getCarY(), shot.getCarZ(), shot.getCarYaw(), shot.getShotName(), shot.getBallSpeed(), 0, shot.getCarRoll());
				}
				if (shot.getCarRoll() == 137463) {
					shot.setShot(shot.getHeight(), shot.getLength(), shot.getWidth(), shot.getSpeedX(), shot.getSpeedY(), shot.getSpeedZ(), shot.getCarX(), shot.getCarY(), shot.getCarZ(), shot.getCarYaw(), shot.getShotName(), shot.getBallSpeed(), shot.getCarPitch(), 0);
				}
				if (shouldLoad) {
					SD.push_back(shot);
				}
			}
			std::sort(SD.begin(), SD.end(), [](const Shot& a, const Shot& b) {
				return helpSortArray(a.getShotName(), b.getShotName());
				});
			ShotData = SD;
			isShotActive.assign(ShotData.size(), false);
			randomizer = false;
			trainingActive = false;
			CVarWrapper shotNumberCvar = cvarManager->getCvar("shotNumber1");
			if (!shotNumberCvar) { return; }
			shotNumberCvar.setValue(0);
			loadShot();
		}
		else {
			Shot shot;
			shot.createDefaultShotFile(trainingPackLoc);
		}

	}
	else {
		HSTraining::createTrainingPack("Training Pack 1");
	}

}
bool HSTraining::createNewShot(int TrainingPackNum, float height1, float length1, float width1, float speedX1, float speedY1, float speedZ1, float carX1, float carY1, float carZ1, int carYaw1, std::string nameShot1, int carPitch1, int carRoll1) {
	fs::path pdPath = gameWrapper->GetDataFolder();
	std::string dPath = normalizePathSeparators(pdPath.string());
	std::string trainingPackLoc = dPath + "/HSShots/TrainingPacks/" + trainingPacks[TrainingPackNum] + "/";
	std::string shotLoc = trainingPackLoc + nameShot1 + ".shot";
	if (fs::exists(trainingPackLoc) && !fs::exists(shotLoc)) {
		CVarWrapper speedBallCvar = cvarManager->getCvar("cool_speed");
		if (!speedBallCvar) { return false; }
		int speedBall = speedBallCvar.getIntValue();
		Shot shot;
		shot.createShotFile(trainingPackLoc, height1, length1, width1, speedX1, speedY1, speedZ1, carX1, carY1, carZ1, carYaw1, nameShot1, speedBall, carPitch1, carRoll1);
		loadTrainingPack(TrainingPackNum);
		return true;
	}
	else {
		return false;
	}

}
//finds all training packs
void HSTraining::getTrainingPacks() {
	fs::path pdPath = gameWrapper->GetDataFolder();
	std::string dPath = normalizePathSeparators(pdPath.string());
	std::string trainingPackFolderLoc = dPath + "/HSShots/TrainingPacks";
	std::vector<std::string> packList;

	try {
		for (const auto& entry : fs::directory_iterator(trainingPackFolderLoc)) {
			if (entry.is_directory()) {
				packList.push_back(entry.path().filename().string());
			}
		}
	}
	catch (const fs::filesystem_error& e) {
		/*std::cerr << "Filesystem error: " << e.what() << '\n';*/
	}
	trainingPacks = packList;


}
//copies a shot and moves it to either the same training pack or a different one
bool HSTraining::transferShot(int senderPackNum, int receiverPackNum, int shotnum, std::string newName) {
	fs::path pdPath = gameWrapper->GetDataFolder();
	std::string dPath = normalizePathSeparators(pdPath.string());
	std::string trainingPackDir = dPath + "/HSShots/TrainingPacks/";
	std::string trainingPackLoc = trainingPackDir + trainingPacks[senderPackNum] + "/";
	std::string trainingPackNewLoc = trainingPackDir + trainingPacks[receiverPackNum] + "/";
	Shot original;
	Shot copy;
	if(shotnum >= ShotData.size() || shotnum < 0) {
		return false;
	}
	if (fs::exists(trainingPackLoc) && fs::exists(trainingPackNewLoc)) {
		original.readShotFile(trainingPackLoc, ShotData[shotnum].getShotName());
		copy.createShotFile(trainingPackNewLoc, original.getHeight(), original.getLength(), original.getWidth(), original.getSpeedX(), original.getSpeedY(), original.getSpeedZ(), original.getCarX(), original.getCarY(), original.getCarZ(), original.getCarYaw(), newName, original.getBallSpeed(), original.getCarPitch(), original.getCarRoll());
		//updates old shots
		if (copy.getBallSpeed() == 0) {
			copy.setShot(copy.getHeight(), copy.getLength(), copy.getWidth(), copy.getSpeedX(), copy.getSpeedY(), copy.getSpeedZ(), copy.getCarX(), copy.getCarY(), copy.getCarZ(), copy.getCarYaw(), copy.getShotName(), 3000, copy.getCarPitch(), copy.getCarRoll());
		}
		if (copy.getCarPitch() == 137463) {
			copy.setShot(copy.getHeight(), copy.getLength(), copy.getWidth(), copy.getSpeedX(), copy.getSpeedY(), copy.getSpeedZ(), copy.getCarX(), copy.getCarY(), copy.getCarZ(), copy.getCarYaw(), copy.getShotName(), copy.getBallSpeed(), 0, copy.getCarRoll());
		}
		if (copy.getCarRoll() == 137463) {
			copy.setShot(copy.getHeight(), copy.getLength(), copy.getWidth(), copy.getSpeedX(), copy.getSpeedY(), copy.getSpeedZ(), copy.getCarX(), copy.getCarY(), copy.getCarZ(), copy.getCarYaw(), copy.getShotName(), copy.getBallSpeed(), copy.getCarPitch(), 0);
		}
		return true;
	}
	else {
		return false;
	}
}