#pragma once
#include "../types.h"

#include <glm/glm.hpp>

#include <vector>
#include <string>
#include <map>


#define MAX_TEXTURE_NAME 16
#define MIP_LEVELS 4

#define MAX_MAP_HULLS 4
/* contains loading functions for Half Life BSPs */
namespace HLBSP {

	struct Lump {
		uint32_t offset;
		uint32_t len;
	};
	using rgb = glm::uvec3;
	using rgba = glm::uvec4;

	using vec3 = glm::vec3;
	using vec2 = glm::vec2;
	using ivec3 = glm::vec<3, int16_t>;

	struct Header {
		uint32_t version;

		union {
			Lump lumps[15];
			struct {
				Lump	entities,
						planes,
						textures,
						vertices,
						visibility,
						nodes,
						texinfo,
						faces,
						lighting,
						clip_nodes,
						leaves,
						mark_surfaces,
						edges,
						surf_edges,
						models;
			};
		};
	};

	struct Plane {
		vec3 norm;
		float dist;
		/* exists for certain optimizations (swaped y and z) */
		enum PlaneType {
			eX,
			eZ,
			eY,
			eAnyX,
			eAnyZ,
			eAnyY,
		} type;
	};

	struct TextureLump {
		uint32_t n_mip_textures;
		int32_t offsets[];
	};

	struct MipTexture {
		char name[MAX_TEXTURE_NAME];
		uint32_t width, height;
		/* is 0 if stored in WAD, otherwise, offset is from beginning of this struct */
		uint32_t mip_offsets[MIP_LEVELS];
	};

	struct Vertex {
		vec3 pos;
		vec2 uv;
		vec3 norm;
	};

	struct Vis {};

	struct Node {
		int32_t plane;
		/* negative numbers are leaf indices */
		int16_t children[2];

		/* bounding box coords (integer) */
		ivec3 bb_mins;
		ivec3 bb_maxes;

		int16_t first_face_idx;
		int16_t n_faces;
	};

	struct TexInfo {
		vec3 shift_s_dir;
		float shift_s;
		vec3 shift_t_dir;
		float shift_t;
		uint32_t mip_tex_idx;
		/* seems to always be 0 */
		uint32_t flags;
		
	};

	struct Face {
		uint16_t plane_idx;
		/* set if different normals orientation */
		uint16_t plane_side;
		uint32_t first_surf_edge_idx;
		int16_t n_surf_edges;
		int16_t tex_info_idx;
		uint8_t lighting_styles[4];
		uint32_t lightmap_offset;
	};


	struct Lightmap {
		rgb* lights;
	};

	struct ClipNode {
		int32_t plane_idx;
		/* negative numbers are contents */
		int16_t children[2];
	};

	struct Leaf {
		enum {
			eEmpty = -1,
			eSolid = -2,
			eWater = -3,
			eSlime = -4,
			eLava = -5,
			eSky = -6,
			eOrigin = -7,
			eClip = -8,
			eCurrent0 = -9,
			eCurrent90 = -10,
			eCurrent180 = -11,
			eCurrent270 = -12,
			eCurrentUp = -13,
			eCurrentDown = -14,
			eTranslucent = -15,
		} contents;
		/* if this is -1, no VIS data */
		int32_t vis_offset;
		ivec3 bb_mins;
		ivec3 bb_maxes;
		uint16_t first_mark_surface_idx;
		uint16_t n_mark_surfaces;
		uint8_t ambient_sound_levels[4];
	};

	typedef uint16_t MarkSurface;

	struct Edge {
		uint16_t vertex_indices[2];
	};

	typedef int32_t Surfedge;

	struct Model {
		vec3 bb_mins;
		vec3 bb_maxes;
		vec3 origin;
		int32_t head_node_indices[MAX_MAP_HULLS];
		int32_t vis_leafs;
		int32_t first_face_idx;
		int32_t n_faces;
	};

	struct BSP {
		BSP(const std::string& fname);
		void load_vertices();
		int determine_leaf(vec3 cam_pos);
		bool determine_visibility(const Leaf& cam_leaf, const Leaf& leaf, const std::array<glm::vec4, 6>& frustum, const vec3 box_verts[8]);
		int get_index_from_surfedge(int surfedge);

		Header* header;
		std::string filename;
		std::vector<uint8_t> file_data;

		std::vector<::std::map<::std::string, std::string>> entities;
		std::vector<Plane> planes;
		std::vector<MipTexture> textures;
		std::vector<glm::vec3> vertices;
		std::vector<glm::vec3> processed_vertices;
		/* skipping vis for now */
		std::vector<Node> nodes;
		std::vector<TexInfo> tex_infos;
		std::vector<Face> faces;
		Lightmap lightmap;
		std::vector<ClipNode> clip_nodes;
		std::vector<Leaf> leaves;
		std::vector<MarkSurface> mark_surfaces;
		std::vector<Edge> edges;
		std::vector<Surfedge> surfedges;
		std::vector<Model> models;

		std::vector<::Vertex> get_vertices();
		std::vector<Vertex> textured_vertices;
		/* to eliminate needless re-loading*/
		int last_leaf = -0x1337;
	};
}