IqmTypeMap getShader(XMLElement*elShade, ShaderPtr& sPtr, uint32_t nMat, uint32_t nLights){
	// this will store all vertex attributes
	IqmTypeMap ret;

	// Check to see if all variables described in XML are present
	string v(check("Vertex", elShade)->Attribute("src"));
	string f(check("Fragment", elShade)->Attribute("src"));

	ifstream vIn("../Resources/Shaders/" + v), fIn("../Resources/Shaders/" + f);
	string vSrc((istreambuf_iterator<char>(vIn)), istreambuf_iterator<char>());
	string fSrc((istreambuf_iterator<char>(fIn)), istreambuf_iterator<char>());

	// Set the light and geom (material) count in the shaders
	auto setNum = [nMat, nLights](string& shdrSrc){
		auto pos = shdrSrc.find("\n");
		shdrSrc.insert(pos + 1, "#define NUM_MATS " + std::to_string(nMat) + "\n");
		shdrSrc.insert(pos + 1, "#define NUM_LIGHTS " + std::to_string(nLights) + "\n");
	};
	setNum(vSrc);
	setNum(fSrc);

	sPtr = Shader::FromSource(vSrc, fSrc);

	auto sBind = sPtr->Bind();

	// Declared vertex attributes
	XMLElement * attrs = check("Attributes", elShade);

	// Make sure those variables exist in the shader
	for (auto el = attrs->FirstChildElement(); el; el = el->NextSiblingElement()){
		string type(el->Value());
		string var(el->GetText());
		GLint handle = sPtr->getHandle(var); // Should I have the shader ensure it's an attribute?
		// A negative handle means the query was unsuccessful
		if (handle < 0){
			cout << "Invalid variable queried in shader " << var << endl;
			continue; //exit(6);
		}
		// Populate returned map
		if (type.compare("Position") == 0)
			ret[IqmFile::IQM_T::POSITION] = handle;
		else if (type.compare("TexCoord") == 0)
			ret[IqmFile::IQM_T::TEXCOORD] = handle;
		else if (type.compare("Normal") == 0)
			ret[IqmFile::IQM_T::NORMAL] = handle;
		else if (type.compare("Tangent") == 0)
			ret[IqmFile::IQM_T::TANGENT] = handle;
	}

	return ret;
}
void setupMiscUniforms(const ShaderPtr& sPtr){
	// Uniform Handles (really shouldn't be hard coded like this)
	Camera::SetProjHandle(sPtr->getHandle("PV")); // Projection Matrix
	Camera::SetPosHandle(sPtr->getHandle("u_wCameraPos")); // World Space cam pos
	//Camera::SetCHandle(shader["C"]); // Camera Matrix

	Geometry::setMHandle(sPtr->getHandle("M")); // World transform Matrix
	Geometry::setNHandle(sPtr->getHandle("N")); // Normal Matrix
	Geometry::setMatIdxHandle(sPtr->getHandle("mIdx"));

	// If these end up negative, so be it
	Material::SetTexMapHandle(sPtr->getHandle("u_TextureMap")); // Texture Map Sampler
	Material::SetNrmMapHandle(sPtr->getHandle("u_NormalMap")); // Normal Map Sample;

	// This causes GL_TEXTUREi to be associated with an int
	// Make a manager for this inside Textures namespace
	glUniform1i(sPtr->getHandle("u_TextureMap"), COLOR_TEX_UNIT);
	glUniform1i(sPtr->getHandle("u_NormalMap"), NORMAL_TEX_UNIT);
	glUniform1i(sPtr->getHandle("u_EnvMap"), CUBE_TEX_UNIT);
}
Scene::Scene(string XmlSrc, ShaderPtr& sPtr, Camera& cam){
	XMLDocument doc;
	doc.LoadFile(XmlSrc.c_str());

	// Get the root (Scene) element
	XMLElement * elScene = doc.FirstChildElement("Scene");
	if (!elScene){
		cout << "XML Root not found. " << endl;
		exit(5);
	}
	// Get and verify that important things are there
	XMLElement * elCam = check("Camera", elScene);
	XMLElement * elShade = check("Shader", elScene);
	XMLElement * elGeom = check("Geom", elScene);
	XMLElement * elLight = check("Light", elScene);

	// First create host assets
	// Init Camera
	Camera::Type camType = getCamera(*elCam, cam);
	if (camType == Camera::Type::NIL){
		cout << "Error creating Camera" << endl;
		exit(7);
	}

	// Init Geometry
	for (XMLElement * el = elGeom->FirstChildElement(); el; el = el->NextSiblingElement())
		m_vGeometry.push_back(getGeom(el));

	// Set up the lights, arbitrarily assigning pre-existing geometry as its representation
	for (auto el = elLight->FirstChildElement(); el; el = el->NextSiblingElement())
		m_vLights.push_back(getLight(el));
	
	// Init shader
	// The material count == #geom + # of point lights
	int nMaterials = m_vGeometry.size() + std::count_if(
		m_vLights.begin(), m_vLights.end(), [](const Light& l){
		return l.getType() == Light::Type::POINT;
	});
	IqmTypeMap iqmTypes = getShader(elShade, sPtr, nMaterials, m_vLights.size());

	// Sort so overlapping geometry is adjacent
	std::sort(m_vGeometry.begin(), m_vGeometry.end());

	// Create all GPU assets, starting by binding the shader
	auto sBind = sPtr->ScopeBind();

	// Create Geometry
	for (auto it = m_vGeometry.begin(); it != m_vGeometry.end();){
		// For the first instance, create the assets
		createGPUAssets(iqmTypes, *it);
		// For all remaining instances, just copy VAO and nIdx
		int count = std::count(m_vGeometry.begin(), m_vGeometry.end(), *it);
		auto cloneIt = it;
		for (int i = 1; i < count; i++){
			(it + i)->setNumIndices(it->getNumIdx());
			(it + i)->setVAO(it->getVAO());
		}
		it += count;
	}

	// Set up material shader access
	Shader * s = sPtr.get();
	auto fixMaterial = [s](Geometry& G, int i){
		string m = "MatArr[i].";
		m[m.length() - 3] = '0' + i;

		Material M = G.getMaterial();

		// Material handles aren't static
		M.SetReflectHandle(s->getHandle(m + "Reflectivity"));
		M.setShinyHandle(s->getHandle(m + "Shininess"));
		M.setDiffHandle(s->getHandle(m + "Diffuse"));
		M.setSpecHandle(s->getHandle(m + "Specular"));
		createGPUAssets(M);

		G.setMaterial(M);
	};

	// Upload all geometry materials
	for (int i = 0; i < m_vGeometry.size(); i++)
		fixMaterial(m_vGeometry[i], i);

	// Create lights
	for (int i = 0; i < m_vLights.size(); i++){
		// Upload to shader (Must be accessed like "TheLights[i].Type, GL3)
		string s = "LightArr[i].";
		s[s.length() - 3] = '0' + i;

		// Store handles per light, since they could move
		m_vLights[i].SetTypeHandle(sPtr->getHandle(s + "Type"));
		m_vLights[i].SetPosOrHalfHandle(sPtr->getHandle(s + "PosOrHalf"));
		m_vLights[i].SetDirOrAttenHandle(sPtr->getHandle(s + "DirOrAtten"));
		m_vLights[i].SetIntensityHandle(sPtr->getHandle(s + "Intensity"));

		// Put light data on GPU
		createGPUAssets(m_vLights[i]);

		// Give point lights some geometry
		if (m_vLights[i].getType() == Light::Type::POINT){
			Geometry lightGeom = m_vGeometry[0];
			lightGeom.identity(); // Maybe scale it somehow?
			mat4 light_T = glm::translate(m_vLights[i].getPos());
			lightGeom.leftMultM(light_T);

			Material lightMat(10.f, 0.f, vec4(glm::linearRand(vec3(0), vec3(1)), 0.5f), vec4(1));

			lightGeom.setMaterial(lightMat);
			fixMaterial(lightGeom, i + m_vGeometry.size() );
			m_vLights[i].SetGeometry(lightGeom);
		}

		//// Set up point light materials
		//if (m_vLights[i].getType() == Light::Type::POINT){
		//	Geometry g = m_vLights[i].GetGeometry(); // I wish we didn't have to go back and forth like this
		//	fixMaterial(g, i + m_vGeometry.size() - 1);  // but returning a reference seems like overkill
		//	m_vLights[i].SetGeometry(g);
		//}
	}

	setupMiscUniforms(sPtr);

	Scene::s_EnvMapHandle = sPtr->getHandle("u_EnvMap");

	//// Find a better place for this, do XML
	std::string cubeFaces[6] = { "posX.png", "negX.png", "posY.png", "negY.png", "posZ.png", "negZ.png" };
	m_EnvMap = Textures::CubeMap(cubeFaces);
}