//#define ENTS_NYI

#ifdef ENTS_NYI
void() editor_ents_add =
{
};

void(vector mousepos) editor_ents_overlay =
{
	drawrawstring('0 32 0', "Not Yet Implemented", '8 8 0', '1 1 1', 1);
};
#else
static int selectedent;
static entity tempent;

typedef struct
{
	//quick access for rendering.
	float modelindex;
	float isbsp;
	float alpha;
	vector colourmod;
	vector org;
	vector ang;
	vector mins;
	vector maxs;
	float scale;

	//full data
	hashtable fields;
} entedit_t;

static entedit_t *editents;
static int numents;

struct
{	//FIXME: should probably parse quakeed comments or something.
	//FIXME: bbox colours.
	string classn;
	string model;
	vector colour;
	vector mins, maxs;
	float spawnflags_match;
	float spawnflags_mask;
} entclasses[] =
{
	{"info_player_start", 				"progs/player.mdl",		'0 1 0',	'-16 -16 -24',	'16 16 32'},
	{"info_player_start", 				"progs/player.mdl",		'1 0 0',	'-16 -16 -24',	'16 16 32'},
	{"info_player_start2",				"progs/player.mdl",		'1 0 0',	'-16 -16 -24',	'16 16 32'},
	{"info_player_deathmatch",		"progs/player.mdl",		'1 0 1',	'-16 -16 -24',	'16 16 32'},
	{"info_player_coop", 				"progs/player.mdl",		'1 0 1',	'-16 -16 -24',	'16 16 32'},

	{"item_armor1", 				"progs/armor.mdl",		'0 0.5 0.8',	'-16 -16 0',	'16 16 56'},
	{"item_armor2", 				"progs/armor.mdl",		'0 0.5 0.8',	'-16 -16 0',	'16 16 56'},
	{"item_armorInv", 				"progs/armor.mdl",		'0 0.5 0.8',	'-16 -16 0',	'16 16 56'},

	{"weapon_supershotgun", 			"progs/g_shot.mdl",		'0 0.5 0.8',	'-16 -16 0',	'16 16 56'},
	{"weapon_nailgun", 				"progs/g_nail.mdl",		'0 0.5 0.8',	'-16 -16 0',	'16 16 56'},
	{"weapon_supernailgun", 			"progs/g_nail2.mdl",		'0 0.5 0.8',	'-16 -16 0',	'16 16 56'},
	{"weapon_grenadelauncher",		"progs/g_rock.mdl",		'0 0.5 0.8',	'-16 -16 0',	'16 16 56'},
	{"weapon_rocketlauncher", 		"progs/g_rock2.mdl",		'0 0.5 0.8',	'-16 -16 0',	'16 16 56'},
	{"weapon_lightning", 				"progs/g_light.mdl",		'0 0.5 0.8',	'-16 -16 0',	'16 16 56'},

	{"item_key1", 					"progs/w_s_key.mdl",	'0 0.5 0.8',	'-16 -16 -24',	'16 16 32'},
	{"item_key2", 					"progs/w_g_key.mdl",	'0 0.5 0.8',	'-16 -16 -24',	'16 16 32'},
	{"item_sigil", 					"progs/end1.mdl",		'0 0.5 0.8',	'-16 -16 -24',	'16 16 32'},

	{"item_artifact_invulnerability", 		"progs/invulner.mdl",		'0 0.5 0.8',	'-16 -16 -24',	'16 16 32'},
	{"item_artifact_envirosuit", 		"progs/suit.mdl",		'0 0.5 0.8',	'-16 -16 -24',	'16 16 32'},
	{"item_artifact_invisibility", 		"progs/invisibl.mdl",		'0 0.5 0.8',	'-16 -16 -24',	'16 16 32'},
	{"item_artifact_super_damage", 		"progs/quaddama.mdl",	'0 0.5 0.8',	'-16 -16 -24',	'16 16 32'},



//	{"func_button",					0,				'0 .5 .8', 	?
//	{"trigger_changelevel",			0,				'0.5 0.5 0.5'	? //NO_INTERMISSION
//	{"func_door",					0,				'0 .5 .8,'		? //START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
//	{"func_door_secret",				0,				'0 .5 .8',		? //open_once 1st_left 1st_down no_shoot always_shoot
//	{"trigger_portaldespawn",			0,				'.5 .5 .5',	?
//	{"func_wall",					0,				'0 .5 .8', 	?
//	{"func_illusionary",				0,				'0 .5 .8',		?
//	{"func_episodegate",				0,				'0 .5 .8',		? //E1 E2 E3 E4
//	{"func_bossgate",				0,				'0 .5 .8',		?
//	{"func_plat",					0,				'0 .5 .8'		? PLAT_LOW_TRIGGER
//	{"func_train",					0,				'0 .5 .8'		?
//	{"trigger_multiple",				0,				'.5 .5 .5' 	? notouch
//	{"trigger_once",					0,				'.5 .5 .5' 	? notouch
//	{"trigger_secret",				0,				'.5 .5 .5' 	?
//	{"trigger_counter",				0,				'.5 .5 .5' 	? nomessage
//	{"trigger_teleport",				0,				'.5 .5 .5' 	? PLAYER_ONLY SILENT
//	{"trigger_setskill",				0,				'.5 .5 .5' 	?
//	{"trigger_onlyregistered",			0,				'.5 .5 .5' 	?
//	{"trigger_hurt",					0,				'.5 .5 .5' 	?
//	{"trigger_push",					0,				'.5 .5 .5' 	? PUSH_ONCE
//	{"trigger_monsterjump",			0,				'.5 .5 .5', 	?
//	{"trigger_motionsickness",			0,				'.5 .5 .5', 	?
//	{"worldspawn",					0,				'0 0 0) ?

	{"path_corner",					0,				'0.5 0.3 0', 	'-8 -8 -8',	'8 8 8'},
	{"event_lightning",				0,				'0 1 1', 		'-16 -16 -16',	'16 16 16'},
	{"info_intermission",				0,				'1 0.5 0.5', 	'-16 -16 -16',	'16 16 16'},
	{"misc_portalspawn",				0,				'.5 .5 .5', 		'-8 -8 -8',	'8 8 8'},
	{"noclass",					0,				'0 0 0', 		'-8 -8 -8',	'8 8 8'},
	{"item_health",					"maps/b_bh25.bsp",	'.3 .3 1', 		'0 0 0',		'32 32 32'}, //rotten megahealth
	{"item_shells",					"maps/b_shell0.bsp",	'0 .5 .8', 		'0 0 0',		'32 32 32'}, //big
	{"item_spikes",					"maps/b_nail0.bsp",	'0 .5 .8', 		'0 0 0',		'32 32 32'}, //big
	{"item_rockets",				"maps/b_rock0.bsp",	'0 .5 .8', 		'0 0 0',		'32 32 32'}, //big
	{"item_cells",					"maps/b_batt0.bsp",	'0 .5 .8', 		'0 0 0',		'32 32 32'}, //big
	{"item_weapon",				0,				'0 .5 .8', 		'0 0 0',		'32 32 32'}, //shotgun rocket spikes big
	{"info_null",					0,				'0 0.5 0', 		'-4 -4 -4',	'4 4 4'},
	{"info_notnull",					0,				'0 0.5 0', 		'-4 -4 -4',	'4 4 4'},
	{"light",						0,				'0 1 0', 		'-8 -8 -8',	'8 8 8'}, //START_OFF
	{"light_fluoro",					0,				'0 1 0', 		'-8 -8 -8',	'8 8 8'},//START_OFF
	{"light_fluorospark",				0,				'0 1 0', 		'-8 -8 -8',	'8 8 8'},
	{"light_globe",					0,				'0 1 0', 		'-8 -8 -8',	'8 8 8'},
	{"light_torch_small_walltorch",		0,				'0 .5 0', 		'-10 -10 -20',	'10 10 20'},
	{"light_flame_large_yellow",		0,				'0 1 0', 		'-10 -10 -12',	'12 12 18'},
	{"light_flame_small_yellow",		0,				'0 1 0', 		'-8 -8 -8',	'8 8 8'}, //START_OFF
	{"light_flame_small_white",		0,				'0 1 0', 		'-10 -10 -40',	'10 10 40'}, //START_OFF
	{"misc_fireball",					0,				'0 .5 .8', 		'-8 -8 -8',	'8 8 8'},
	{"misc_explobox",				0,				'0 .5 .8', 		'0 0 0',		'32 32 64'},
	{"misc_explobox2",				0,				'0 .5 .8', 		'0 0 0',		'32 32 64'},
	{"trap_spikeshooter",				0,				'0 .5 .8', 		'-8 -8 -8',	'8 8 8'}, //superspike laser
	{"trap_shooter",				0,				'0 .5 .8', 		'-8 -8 -8',	'8 8 8'}, //superspike laser
	{"air_bubbles",					0,				'0 .5 .8', 		'-8 -8 -8',	'8 8 8'},
	{"viewthing",					0,				'0 .5 .8', 		'-8 -8 -8',	'8 8 8'},
	{"ambient_suck_wind",			0,				'0.3 0.1 0.6', 	'-10 -10 -8',	'10 10 8'},
	{"ambient_drone",				0,				'0.3 0.1 0.6', 	'-10 -10 -8',	'10 10 8'},
	{"ambient_flouro_buzz",			0,				'0.3 0.1 0.6', 	'-10 -10 -8',	'10 10 8'},
	{"ambient_drip",				0,				'0.3 0.1 0.6', 	'-10 -10 -8',	'10 10 8'},
	{"ambient_comp_hum",			0,				'0.3 0.1 0.6', 	'-10 -10 -8',	'10 10 8'},
	{"ambient_thunder",				0,				'0.3 0.1 0.6', 	'-10 -10 -8',	'10 10 8'},
	{"ambient_light_buzz",			0,				'0.3 0.1 0.6', 	'-10 -10 -8',	'10 10 8'},
	{"ambient_swamp1",				0,				'0.3 0.1 0.6', 	'-10 -10 -8',	'10 10 8'},
	{"ambient_swamp2",				0,				'0.3 0.1 0.6', 	'-10 -10 -8',	'10 10 8'},
	{"misc_noisemaker",				0,				'1 0.5 0', 		'-10 -10 -10',	'10 10 10'},
	{"misc_teleporttrain",				0,				'0 .5 .8', 		'-8 -8 -8',	'8 8 8'},
	{"trigger_relay",				0,				'.5 .5 .5', 		'-8 -8 -8', 	'8 8 8'},
	{"info_teleport_destination",		0,				'.5 .5 .5', 		'-8 -8 -8', 	'8 8 32'},

	{"monster_boss",				"progs/boss.mdl",	'1 0 0', 		'-128 -128 -24','128 128 256'},
	{"monster_hell_knight",			"progs/hknight.mdl",	'1 0 0', 		'-16 -16 -24',	'16 16 40'}, //Ambush
	{"monster_demon1",				"progs/demon.mdl",	'1 0 0', 		'-32 -32 -24',	'32 32 64'}, //Ambush
	{"monster_dog",				"progs/dog.mdl",	'1 0 0', 		'-32 -32 -24',	'32 32 40'}, //Ambush
	{"monster_enforcer",				"progs/enforcer.mdl",	'1 0 0', 		'-16 -16 -24',	'16 16 40'}, //Ambush
	{"monster_fish",				"progs/fish.mdl",	'1 0 0', 		'-16 -16 -24',	'16 16 24'}, //Ambush
	{"monster_ogre",				"progs/ogre.mdl",	'1 0 0', 		'-32 -32 -24', 	'32 32 64'}, //Ambush
	{"monster_oldone",				"progs/oldone.mdl",	'1 0 0', 		'-16 -16 -24', 	'16 16 32'},
	{"monster_knight",				"progs/knight.mdl",	'1 0 0', 		'-16 -16 -24',	'16 16 40'}, //Ambush
	{"monster_shalrath",				"progs/shalrath.mdl",	'1 0 0', 		'-32 -32 -24',	'32 32 48'}, //Ambush
	{"monster_shambler",			"progs/shambler.mdl",'1 0 0', 		'-32 -32 -24',	'32 32 64'}, //Ambush
	{"monster_army",				"progs/soldier.mdl",	'1 0 0', 		'-16 -16 -24',	'16 16 40'}, //Ambush
	{"monster_tarbaby",				"progs/tarbaby.mdl",	'1 0 0', 		'-16 -16 -24',	'16 16 24'}, //Ambush
	{"monster_wizard",				"progs/wizard.mdl",	'1 0 0', 		'-16 -16 -24', 	'16 16 40'}, // Ambush
	{"monster_zombie",				"progs/zombie.mdl",	'1 0 0', 		'-16 -16 -24', 	'16 16 32'} //Crucified ambush
};

entedit_t*() editor_ents_new =
{
	local float nent;
	local entedit_t *newents;
	nent = numents;
	numents += 1;

	//extend the list
	newents = memalloc(sizeof(entedit_t)*numents);
	memcpy((__variant*)newents, (__variant*)editents, sizeof(entedit_t)*nent);
	memfree((__variant*)editents);
	editents = newents;

	editents[nent].fields = hash_createtab(12, EV_STRING);

	return &editents[nent];
};

void(float num) editor_ents_delete =
{
};

inline float(string model) modelindexforname =
{
	print("precaching \"");
	print(model);
	print("\"\n");
	precache_model(model);
	setmodel(tempent, model);
	return tempent.modelindex;
};
void(entedit_t *nent) editor_ents_updated =
{
	float ang;

	nent->alpha = stof(nent->fields["alpha"]);
	nent->scale = stof(nent->fields["scale"]);
	nent->modelindex = modelindexforname(nent->fields["model"]);
	nent->mins = tempent.mins;
	nent->maxs = tempent.maxs;
	nent->org = stov(nent->fields["origin"]);
	nent->isbsp = FALSE;

	ang = stof(nent->fields["angle"]);
	if (ang == -1)
		nent->ang = [90, 0, 0];
	else if (ang == -2)
		nent->ang = [-90, 0, 0];
	else
		nent->ang = [0, ang, 0];

	if (!nent->modelindex)
	{
		int i;
		string classn;
		classn = nent->fields["classname"];
		for (i = 0; i < entclasses.length; i++)
		{
			if (classn == entclasses[i].classn)
			{
				nent->modelindex = modelindexforname(entclasses[i].model);
				nent->alpha = 0.3;
				nent->colourmod = entclasses[i].colour;
				nent->mins = entclasses[i].mins;
				nent->maxs = entclasses[i].maxs;
				break;
			}
		}
	}
	else
		nent->isbsp = TRUE;

	if (nent->isbsp)
		nent->ang = '0 0 0';	//bsp entities should not be displayed rotated, because that just messes everything up.
};

void() editor_ents_reload =
{
	local string field, value;

	//reset ent state
	getentitytoken(__NULL__);

	for(;;)
	{
		field = getentitytoken();
		if not (field)
		{
			break;
		}
		if (field == "{")
		{
			local entedit_t *nent;
			nent = editor_ents_new();

			for(;;)
			{
				field = getentitytoken();
				if not (field)
				{
					print("Truncated ent lump\n");
					return;
				}
				if (field == "}")
					break;
				value = getentitytoken();

				nent.fields[field] = value;
				print(sprintf("%s: %s\n", field, value));
			}

			editor_ents_updated(nent);
		}
		else
		{
			print(sprintf("Corrupt ent lump, found \"%s\"\n", field));
			return;
		}
	}
};

void(string shadername, vector min, vector max, vector col) editor_ents_drawbbox =
{
	if (min == max)
		return;
	R_BeginPolygon(shadername);
#define line(x,y) 			R_PolygonVertex(x, '0 0', col, 1);	R_PolygonVertex(y, '0 0', col, 1);	R_EndPolygon()
	line(min, ([max[0], min[1], min[2]]));
	line(min, ([min[0], max[1], min[2]]));
	line(min, ([min[0], min[1], max[2]]));
	line(max, ([min[0], max[1], max[2]]));
	line(max, ([max[0], min[1], max[2]]));
	line(max, ([max[0], max[1], min[2]]));

	line(([max[0], min[1], min[2]]), ([max[0], max[1], min[2]]));
	line(([max[0], min[1], min[2]]), ([max[0], min[1], max[2]]));
	line(([min[0], max[1], min[2]]), ([min[0], max[1], max[2]]));
	line(([min[0], max[1], min[2]]), ([max[0], max[1], min[2]]));
	line(([min[0], min[1], max[2]]), ([min[0], max[1], max[2]]));
	line(([min[0], min[1], max[2]]), ([max[0], min[1], max[2]]));
#undef line
};

void() editor_ents_add =
{
	float e;
	if (!tempent)
	{
		tempent = spawn();
		editor_ents_reload();
	}

	//make sure this shader was generated already.
	shaderforname("entbox",
		"{"
			"cull disable\n"
			"{\n"
				"map $whiteimage\n"
				"blendfunc add\n"
				"rgbgen vertex\n"
				"alphagen vertex\n"
			"}\n"
		"}");
	//make sure this shader was generated already.
	shaderforname("entboxsel",
		"{"
			"cull disable\n"
			"{\n"
				"map $whiteimage\n"
				"blendfunc add\n"
				"rgbgen vertex\n"
				"alphagen vertex\n"
				"sort nearest\n"
				"nodepthtest\n"
			"}\n"
		"}");

	for (e = 1; e < numents; e++)
	{
		if (e == selectedent)
		{
			if (gettime(0)*5f & 1)
				continue;
			tempent.effects |= 8192f;
			editor_ents_drawbbox("entboxsel", editents[e].org+editents[e].mins, editents[e].org+editents[e].maxs, editents[e].colourmod);
		}
		else
		{
			tempent.effects &~= 8192f;
			editor_ents_drawbbox("entbox", editents[e].org+editents[e].mins, editents[e].org+editents[e].maxs, editents[e].colourmod);
		}

		if (!editents[e].modelindex)
			continue;

		tempent.modelindex = editents[e].modelindex;
		tempent.alpha = editents[e].alpha;
		tempent.scale = editents[e].scale;
		tempent.angles = editents[e].ang;
		tempent.colormod = editents[e].colourmod;

		setorigin(tempent, editents[e].org);
		addentity(tempent);
	}
};

float(float key, float unic, vector mousepos) editor_ents_key =
{
	if (key != K_MOUSE1)
		return FALSE;
	float bestfrac = 1;
	int bestent = selectedent, e;

	vector t = mousefar;
	vector o = mousenear;
	if (vlen(o - t) > 8192)
		t = o + normalize(t)*8192;

	for (e = 1; e < numents; e++)
	{
		if (editents[e].isbsp)
			tempent.solid = SOLID_BSP;
		else
			tempent.solid = SOLID_BBOX;
		tempent.modelindex = editents[e].modelindex;
		tempent.alpha = editents[e].alpha;
		tempent.scale = editents[e].scale;
		tempent.angles = editents[e].ang;
		tempent.colormod = editents[e].colourmod;
		tempent.mins = editents[e].mins;
		tempent.maxs = editents[e].maxs;
		setorigin(tempent, editents[e].org);

		traceline(o, t, 256, tempent);

		if (bestfrac > trace_fraction)
		{
			bestfrac = trace_fraction;
			bestent = e;
		}
	}
	tempent.modelindex = SOLID_NOT;

	selectedent = bestent;
	return TRUE;
};

void(vector mousepos) editor_ents_overlay =
{
	int i;
	vector pos = '0 32 0';
	if (selectedent >= numents)
		return;
	entedit_t *ent = &editents[selectedent];

	for (i = 0; ; i++)
	{
		string key, value;
		key = hash_getkey(ent->fields, i);
		if not (key)
			return;
		value = ent->fields[key];
		drawrawstring(pos, sprintf("%s: %s", key, value), '8 8 0', '1 1 1', 1);
		pos_y += 8;
	}
};
#endif
