#include "MPFW_Physics.h"
#include <algorithm>
#include <print>
#include <thread>
#include <mutex>

#include <filesystem>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>


#include "MPFW_Utils.h"


// simple aabb
bool __collides(MPFW::Physics::Box a, MPFW::Physics::Box b) {
	return
		a.min.x <= b.max.x &&
		a.max.x >= b.min.x &&
		a.min.y <= b.max.y &&
		a.max.y >= b.min.y &&
		a.min.z <= b.max.z &&
		a.max.z >= b.min.z;
}


MPFW::Physics::Box MPFW::Physics::GenerateBoundingBoxPolygon(Polygon poly)
{
	Polygon polyCopy = poly;
	std::vector<float> x;
	std::vector<float> y;
	std::vector<float> z;

	for (int i = 0; i < polyCopy.size(); i++) {
		x.push_back(polyCopy[i].x);
		y.push_back(polyCopy[i].y);
		z.push_back(polyCopy[i].z);
	}

	std::sort(x.begin(), x.end());
	std::sort(y.begin(), y.end());
	std::sort(z.begin(), z.end());

	Box retval;
	retval.min.x = x[0];
	retval.min.y = y[0];
	retval.min.z = z[0];

	retval.max.x = x[x.size()-1];
	retval.max.y = y[y.size()-1];
	retval.max.z = z[z.size()-1];
	return retval;
}

std::mutex collSetMutex;

void PolygonCalcThread(MPFW::Physics::CollisionSet* collSet, std::vector<MPFW::Physics::CollisionFace> pStructs, int v) {
	for (int p = 0; p < pStructs.size(); p++) {
		if (collSet->collisionFaces.size() - 1 != p) {
			collSet->collisionFaces = std::vector<MPFW::Physics::CollisionFace>(0);
			p = 0;
		}
		if (__collides(MPFW::Physics::GenerateBoundingBoxPolygon(pStructs[p].polygon), collSet->size)) {
			std::lock_guard<std::mutex> guard(collSetMutex);
			collSet->collisionFaces.push_back(pStructs[p]);
		}
		std::print("v:{},p:{}\n", v, p);
	}
}

MPFW::Physics::CollisionMap MPFW::Physics::GenerateCollisionMap(std::vector<Polygon> polygons, std::vector<Vector3> totalVertices, CollisionMapGenParams cmgp, std::string cacheName)
{


	if (std::filesystem::exists(std::string("data/cache/collisiondata/" + cacheName))) {


		std::ifstream fileHandle(std::string("data/cache/collisiondata/" + cacheName), std::ios::binary);
		

		if (fileHandle.good() && fileHandle.is_open()) {
			std::stringstream fileStringStream; fileStringStream << fileHandle.rdbuf();
			std::string file = fileStringStream.str();


			if (Utils::Strings::StringIndexToInteger_4b_le(file, 0) == 0x01) {






				CachedCollisionMap ccm;

				ccm.cachedCollisionMapVersion = Utils::Strings::StringIndexToInteger_4b_le(file, 0);
				ccm.collisionSetCount = Utils::Strings::StringIndexToInteger_4b_le(file, 4);



				CollisionMap retval;

				ccm.collisionSets.resize(ccm.collisionSetCount);

				std::memcpy(
					ccm.collisionSets.data(),
					Utils::Strings::IterativeStringExcerpt(file, 8, ccm.collisionSetCount * sizeof(CachedCollisionSet)).data(),
					ccm.collisionSetCount * sizeof(CachedCollisionSet));

				for (int i = 0; i < ccm.collisionSets.size(); i++) {
					CollisionSet cs;
					cs.size.max.x = ccm.collisionSets[i].maxx;
					cs.size.max.y = ccm.collisionSets[i].maxy;
					cs.size.max.z = ccm.collisionSets[i].maxz;
					cs.size.min.x = ccm.collisionSets[i].minx;
					cs.size.min.y = ccm.collisionSets[i].miny;
					cs.size.min.z = ccm.collisionSets[i].minz;





					// std::string subbuffer = 






					retval.collisionSets.push_back(cs);

				}


				return retval;


			}


		}


		

	}

	std::vector<Polygon> polygonsCopy = polygons;
	std::vector<std::thread> polygonCalculationsThreads;
	std::vector<CollisionFace> pStructs(polygons.size());

	for (int cFCount = 0; cFCount < polygons.size(); cFCount++) {
		CollisionFace cface;
		cface.polygon = polygonsCopy[cFCount]; // assuming polygonsCopy == polygons, because it should be.



		// calculating normals (if polygon has 3 vertices or more)
		if(cface.polygon.size() >= 3){

			// according to: https://titan.csit.rmit.edu.au/~e20068/teaching/i3dg&a/2012/normals/normals.xhtml

			Vector3 a, b;

			a = Vector3Subtract(cface.polygon[1], cface.polygon[0]);

			b = Vector3Subtract(cface.polygon[2], cface.polygon[0]);
			cface.normal = Vector3Normalize(Vector3CrossProduct(a,b)); // does that actually work? I can't believe it would
			cface.normalCalculated = true;

		}


		pStructs[cFCount] = cface;
		
	}
	
	Box mapBoundingBox = GenerateBoundingBoxPolygon(totalVertices);

	Vector3 sizeOfMap = mapBoundingBox.max - mapBoundingBox.min;
	Vector3 sizeOfVoxel = sizeOfMap / cmgp.voxelLateralCount;

	CollisionMap retval;
	int vCount = 0;
	for (int z = 0; z < cmgp.voxelLateralCount; z++) {
		for (int y = 0; y < cmgp.voxelLateralCount; y++) {
			for (int x = 0; x < cmgp.voxelLateralCount; x++) {
				CollisionSet cs;

				cs.size.min =
				{
					mapBoundingBox.min.x + sizeOfVoxel.x * x,
					mapBoundingBox.min.y + sizeOfVoxel.y * y,
					mapBoundingBox.min.z + sizeOfVoxel.z * z
				};

				cs.size.max =
				{
					mapBoundingBox.min.x + sizeOfVoxel.x * (x + 1),
					mapBoundingBox.min.y + sizeOfVoxel.y * (y + 1),
					mapBoundingBox.min.z + sizeOfVoxel.z * (z + 1)
				};
				cs.collisionFaces = std::vector<CollisionFace>(0);
				retval.collisionSets.push_back(cs);
				// polygonCalculationsThreads.push_back(std::thread(PolygonCalcThread, &retval.collisionSets[vCount], pStructs, vCount));
				
				for(int p = 0; p < pStructs.size(); p++){
					if (__collides(MPFW::Physics::GenerateBoundingBoxPolygon(pStructs[p].polygon), retval.collisionSets[vCount].size)) {
						// std::lock_guard<std::mutex> guard(collSetMutex);
						retval.collisionSets[vCount].collisionFaces.push_back(pStructs[p]);
					}
					// std::print("v:{},p:{}\n", vCount, p);
				}
				vCount++;


			}
		}
	}
	

	/*for (int i = 0; i < polygonCalculationsThreads.size(); i++) {
		if(polygonCalculationsThreads[i].joinable())polygonCalculationsThreads[i].join();
	}*/

	/*
	std::ofstream cacheFile(std::string("data/cache/collisiondata/" + cacheName), std::ios::binary);


	CachedCollisionMap cache;
	cache.cachedCollisionMapVersion = 0x01;
	cache.collisionSetCount = retval.collisionSets.size();
	cacheFile.write((char*)&cache.cachedCollisionMapVersion, 4);
	cacheFile.write((char*)&cache.collisionSetCount, 4);


	int headerSize = retval.collisionSets.size() * 36 + 8;

	int currentFileSize = 0;


	std::string subfile = "";


	for (int i = 0; i < retval.collisionSets.size(); i++) {


		std::string collsetsubfile = "";

		for (int j = 0; j < retval.collisionSets[i].collisionFaces.size(); j++) {
			using namespace std::string_literals;
			collsetsubfile += retval.collisionSets[i].collisionFaces[j].
		}



		unsigned long fileOffset = headerSize + currentFileSize;
		unsigned long size = retval.

		cacheFile.write((char*)& n, 4);
		cacheFile.write((char*)& n, 4);
		int collsize = retval.collisionSets[i].collisionFaces.size();
		cacheFile.write((char*)&collsize, 4);
		cacheFile.write((char*)&retval.collisionSets[i].size.min.x, 4);
		cacheFile.write((char*)&retval.collisionSets[i].size.min.y, 4);
		cacheFile.write((char*)&retval.collisionSets[i].size.min.z, 4);
		cacheFile.write((char*)&retval.collisionSets[i].size.max.x, 4);
		cacheFile.write((char*)&retval.collisionSets[i].size.max.y, 4);
		cacheFile.write((char*)&retval.collisionSets[i].size.max.z, 4);

	}


	cacheFile.close();

	*/
	return retval;



}
