// mesh.h
#ifndef __mesh_h__
#define __mesh_h__

#ifndef __tags_h__
#include "tags.h"
#endif

#ifndef __BYTE_SWAPPING_H__
#include "byte_swapping.h"
#endif

#ifndef __myth_defs_h__
#include "myth_defs.h"
#endif


/* ---------- constants */

// the mesh is an array of submeshes
enum
{
	MAXIMUM_MESH_WIDTH= 256,
	MAXIMUM_MESH_HEIGHT= 256,

	// submeshes are 32x32 cells
 	SUBMESH_WIDTH_BITS= 5,
	SUBMESH_WIDTH= 1<<SUBMESH_WIDTH_BITS,
	MAXIMUM_SUBMESH_WIDTH= MAXIMUM_MESH_WIDTH>>SUBMESH_WIDTH_BITS,
	MAXIMUM_SUBMESH_HEIGHT= MAXIMUM_MESH_HEIGHT>>SUBMESH_WIDTH_BITS,

	// submesh textures are 256x256 pixels
	SUBMESH_TEXTURE_WIDTH_BITS= 8,
	SUBMESH_TEXTURE_WIDTH= 1<<SUBMESH_TEXTURE_WIDTH_BITS,
	
	// cells on the submesh are 8x8 pixels
	SUBMESH_CELL_TEXTURE_WIDTH_BITS= SUBMESH_TEXTURE_WIDTH_BITS-SUBMESH_WIDTH_BITS,
	SUBMESH_CELL_TEXTURE_WIDTH= 1<<SUBMESH_CELL_TEXTURE_WIDTH_BITS,
	
	// submeshes are 32x32 cells
	SUBMESH_CELL_WIDTH= SUBMESH_TEXTURE_WIDTH/SUBMESH_CELL_TEXTURE_WIDTH,

	// Somebody change this comment... PLEASE!
	// we'd rather the volatile mesh only be 26x26 but we also need to shift to read addresses
	VOLATILE_MESH_WIDTH_BITS= 6,
	VOLATILE_MESH_WIDTH= 1<<VOLATILE_MESH_WIDTH_BITS,
	VOLATILE_MESH_HEIGHT= 64,
	
	// the volatile texture maps are 512x512
	VOLATILE_TEXTURE_WIDTH_BITS= 9,
	VOLATILE_TEXTURE_WIDTH= VOLATILE_MESH_WIDTH*SUBMESH_CELL_TEXTURE_WIDTH,
	VOLATILE_TEXTURE_HEIGHT= VOLATILE_MESH_HEIGHT*SUBMESH_CELL_TEXTURE_WIDTH,

	VOLATILE_TEXTURE_WIDTH_UPSHIFT= LONG_BITS-2-VOLATILE_MESH_WIDTH_BITS,
	VOLATILE_TEXTURE_HEIGHT_UPSHIFT= LONG_BITS-2-VOLATILE_MESH_WIDTH_BITS,
	VOLATILE_TEXTURE_WIDTH_DOWNSHIFT= LONG_BITS-2-VOLATILE_TEXTURE_WIDTH_BITS,
	VOLATILE_TEXTURE_HEIGHT_DOWNSHIFT= LONG_BITS-2-2*VOLATILE_TEXTURE_WIDTH_BITS,
	VOLATILE_TEXTURE_WIDTH_MASK= VOLATILE_TEXTURE_WIDTH-1,
	VOLATILE_TEXTURE_HEIGHT_MASK= (VOLATILE_TEXTURE_WIDTH-1)<<VOLATILE_TEXTURE_WIDTH_BITS,

	// was 7, 4
	VOLATILE_SUBTEXTURE_CELL_WIDTH= 6,
	VOLATILE_SUBTEXTURE_SLOP_PIXELS= 4,
	
	VOLATILE_SUBTEXTURE_WIDTH_BITS= 6,
	VOLATILE_SUBTEXTURE_WIDTH= 1<<VOLATILE_SUBTEXTURE_WIDTH_BITS,

	// media textures are 128x128
	MEDIA_TEXTURE_WIDTH_BITS= 7,
	MEDIA_TEXTURE_HEIGHT_BITS= MEDIA_TEXTURE_WIDTH_BITS,
	MEDIA_TEXTURE_WIDTH= 1<<MEDIA_TEXTURE_WIDTH_BITS,
	MEDIA_TEXTURE_HEIGHT= 1<<MEDIA_TEXTURE_HEIGHT_BITS,
	MEDIA_MESH_WIDTH= MEDIA_TEXTURE_WIDTH/(2*SUBMESH_CELL_TEXTURE_WIDTH),

	// upshifts are identical to SUBMESH_TEXTURE_WIDTH_UPSHIFT, SUBMESH_TEXTURE_HEIGHT_UPSHIFT
	MEDIA_TEXTURE_WIDTH_DOWNSHIFT= LONG_BITS-3-VOLATILE_TEXTURE_WIDTH_BITS,
	MEDIA_TEXTURE_HEIGHT_DOWNSHIFT= LONG_BITS-3-MEDIA_TEXTURE_WIDTH_BITS-VOLATILE_TEXTURE_WIDTH_BITS,
	MEDIA_TEXTURE_WIDTH_MASK= MEDIA_TEXTURE_WIDTH-1,
	MEDIA_TEXTURE_HEIGHT_MASK= (MEDIA_TEXTURE_HEIGHT-1)<<MEDIA_TEXTURE_WIDTH_BITS,

	VERTICES_PER_MESH_CELL= 4,
	
//	MESH_MEDIA_HEIGHT= 0
	NONRENDERED_CELL_HEIGHT= -8*WORLD_ONE
};

// Possible parameters for get_mesh_height
enum {
	_true_height,
	_land_height,
	_media_height
};

#define GET_TRUE_CELL_HEIGHT(cell) (AT_LEAST_PARTIALLY_MEDIA(cell) ? (cell)->media_height : (cell)->height)
#define SUBMESH_INDEX(x, y) (((y)>>SUBMESH_WIDTH_BITS)*submesh_width + ((x)>>SUBMESH_WIDTH_BITS))

/* ---------- macros */

enum {
	TEXEL_TO_WORLD_BITS= WORLD_FRACTIONAL_BITS-SUBMESH_CELL_TEXTURE_WIDTH_BITS,
	WORLD_TO_SUBMESH_BITS= WORLD_FRACTIONAL_BITS+SUBMESH_WIDTH_BITS
};

#define TEXEL_TO_WORLD(x) ((x)<<TEXEL_TO_WORLD_BITS)
#define WORLD_TO_TEXEL(x) ((x)>>TEXEL_TO_WORLD_BITS)
#define WORLD_TO_SUBMESH(x) ((x)>>WORLD_TO_SUBMESH_BITS)
#define SUBMESH_TO_WORLD(x) ((x)<<WORLD_TO_SUBMESH_BITS)

/* ---------- structures */

// itŐd be nice to steal some bits for light intensity; sprites would interpolate these numbers

// we need bits for impassable-to-walking and impassable-to-flying; is it possible to walk
// under stuff you can't fly over?  it is certainly possible to fly over stuff you can't walk
// over; these are usually the results of polygonal scenery but could be precomputed from heights

// media height is not user-editable, but is calculated based on the deepest vertex

enum // terrain type (5 bits)
{
	_terrain_media_dwarf_depth, // dwarf-sized waist height
	_terrain_media_human_depth, // man-sized waist height
	_terrain_media_giant_depth, // giant-sized waist height
	_terrain_media_deep, // deeper than giant height
	_terrain_sloped,
	_terrain_steep,
	_terrain_grass,
	_terrain_desert,
	_terrain_rocky,
	_terrain_marsh,
	_terrain_snow,
	_terrain_forest, // identical to grass but prevents long-range sighting
	_terrain_loathing_special,
	_terrain_unused,
	_terrain_walking_impassable,
	_terrain_flying_impassable,
	NUMBER_OF_TERRAIN_TYPES, // <=MAXIMUM_NUMBER_OF_TERRAIN_TYPES
	
	DEFAULT_TERRAIN_TYPE= _terrain_grass,
	FIRST_MEDIA_TERRAIN_TYPE= _terrain_media_dwarf_depth,
	NUMBER_OF_MEDIA_TERRAIN_TYPES= _terrain_media_deep-FIRST_MEDIA_TERRAIN_TYPE+1,

	FIRST_ELEVATION_TERRAIN_TYPE= _terrain_sloped,
	LAST_ELEVATION_TERRAIN_TYPE= _terrain_steep,
	NUMBER_OF_ELEVATION_TERRAIN_TYPES= LAST_ELEVATION_TERRAIN_TYPE-FIRST_ELEVATION_TERRAIN_TYPE,

	TERRAIN_TYPE_BITS= 4,
	MAXIMUM_NUMBER_OF_TERRAIN_TYPES= 1<<TERRAIN_TYPE_BITS,
	TERRAIN_TYPE_MASK= MAXIMUM_NUMBER_OF_TERRAIN_TYPES-1,
	
	CELL_TERRAIN_MASK= (TERRAIN_TYPE_MASK<<TERRAIN_TYPE_BITS) | TERRAIN_TYPE_MASK
};
#define GET_CELL_TERRAIN_TYPE(c,w) ((w) ? (((c)->flags)&TERRAIN_TYPE_MASK) : (((c)->flags>>TERRAIN_TYPE_BITS)&TERRAIN_TYPE_MASK))
#define GET_TERRAIN_TYPE(cell,x,y) GET_CELL_TERRAIN_TYPE(cell, (((x)^(y))&WORLD_ONE) ? ((y)&(WORLD_ONE-1))<(WORLD_ONE-((x)&(WORLD_ONE-1)))) : ((y)&(WORLD_ONE-1))>((x)&(WORLD_ONE-1));

#define TERRAIN_TYPE_IS_MEDIA(t) ((t)<FIRST_MEDIA_TERRAIN_TYPE+NUMBER_OF_MEDIA_TERRAIN_TYPES)

enum // mesh flags
{
	_mesh_vertex_is_media_bit= 2*TERRAIN_TYPE_BITS,
	_mesh_cell_was_or_is_on_fire_bit,
	_mesh_vertex_is_animated_media_bit,
	_mesh_cell_triangle0_is_media_bit,
	_mesh_cell_triangle1_is_media_bit,
	_mesh_cell_has_reflection_bit, // build a reflection for this cell and it's objects
	_mesh_cell_is_not_rendered_bit,
#ifdef LOATHING
	_mesh_cell_will_be_marked_impassable_bit,
#endif
	
	NUMBER_OF_MESH_FLAGS, // <=16
	
	_mesh_vertex_is_media_flag= FLAG(_mesh_vertex_is_media_bit),
	_mesh_cell_was_or_is_on_fire_flag= FLAG(_mesh_cell_was_or_is_on_fire_bit),
	_mesh_vertex_is_animated_media_flag= FLAG(_mesh_vertex_is_animated_media_bit),
	_mesh_cell_triangle0_is_media_flag= FLAG(_mesh_cell_triangle0_is_media_bit),
	_mesh_cell_triangle1_is_media_flag= FLAG(_mesh_cell_triangle1_is_media_bit),
	_mesh_cell_has_reflection_flag= FLAG(_mesh_cell_has_reflection_bit),
	_mesh_cell_is_not_rendered_flag= FLAG(_mesh_cell_is_not_rendered_bit)
#ifdef LOATHING
	,_mesh_cell_will_be_marked_impassable_flag= FLAG(_mesh_cell_will_be_marked_impassable_bit)
#endif
};

#define AT_LEAST_PARTIALLY_MEDIA(c) ((c)->flags&(_mesh_cell_triangle0_is_media_flag|_mesh_cell_triangle1_is_media_flag))
#define IS_PARTIALLY_MEDIA(c) (((((c)->flags)>>1)^((c)->flags))&_mesh_cell_triangle0_is_media_flag)
#define IS_WHOLLY_MEDIA(c) (((c)->flags&_mesh_cell_triangle0_is_media_flag) && ((c)->flags&_mesh_cell_triangle1_is_media_flag))

#define SIZEOF_STRUCT_MESH_CELL 12
struct mesh_cell
{
	short_world_distance physical_height; // (might have a few free bits here)
	word normal; // two 8-bit indexes into the precalculated normal table
	word flags; // includes terrain type of each triangle

	short first_object_index; // postprocessed
	
	short_world_distance media_height;
	short_world_distance render_height;
};

// ============================================================================================
/* ---------- constants */

enum
{
	MESH_GROUP_TAG= 0x6d657368, // 'mesh' (looks like text)
	MESH_VERSION_NUMBER= 11,
	
	MESH_DEFINITION_EDITOR_DATA_SIZE= 16
};

/* ---------- mesh header */

enum // flags (mirrored is user data field of tag)
{
	// first 16 flags show valid netgame scoring types
	_mesh_is_single_player_entry_point_bit= MAXIMUM_NUMBER_OF_GAME_SCORING_TYPES,
	_mesh_supports_unit_trading_bit,
	_mesh_supports_veterans_bit,
	_mesh_has_limited_terrain_visibility_bit,
	_mesh_is_complete_bit,
	_mesh_can_be_used_by_demo_bit,
	_mesh_leaves_overhead_map_closed_bit,
	_mesh_is_training_map_bit,
	_mesh_can_catch_fire_bit,
	_mesh_is_indoor_map_bit,
	_mesh_has_terrain_following_camera_bit,
	_mesh_overhead_map_doesnt_scroll_bit,
	
	NUMBER_OF_MESH_DEFINITION_FLAGS,
	
	_mesh_is_single_player_entry_point_flag= FLAG(_mesh_is_single_player_entry_point_bit),
	_mesh_supports_unit_trading_flag= FLAG(_mesh_supports_unit_trading_bit),
	_mesh_supports_veterans_flag= FLAG(_mesh_supports_veterans_bit),
	_mesh_has_limited_terrain_visibility_flag= FLAG(_mesh_has_limited_terrain_visibility_bit),
	_mesh_is_complete_flag= FLAG(_mesh_is_complete_bit),
	_mesh_can_be_used_by_demo_flag= FLAG(_mesh_can_be_used_by_demo_bit),
	_mesh_leaves_overhead_map_closed_flag= FLAG(_mesh_leaves_overhead_map_closed_bit),
	_mesh_is_training_map_flag= FLAG(_mesh_is_training_map_bit),
	_mesh_can_catch_fire_flag= FLAG(_mesh_can_catch_fire_bit),
	_mesh_is_indoor_map_flag= FLAG(_mesh_is_indoor_map_bit),
	_mesh_has_terrain_following_camera_flag= FLAG(_mesh_has_terrain_following_camera_bit),
	_mesh_overhead_map_doesnt_scroll_flag= FLAG(_mesh_overhead_map_doesnt_scroll_bit)
};

enum // next mesh array indexes
{
	_next_mesh_success,
	_next_mesh_failure,
	NUMBER_OF_NEXT_MESH_TAGS
};

enum // cutscenes array indexes
{
	_cutscene_pregame,
	_cutscene_postgame_success,
	_cutscene_postgame_failure,
	NUMBER_OF_CUTSCENE_MOVIE_TAGS
};

enum // storyline array indexes
{
	_storyline_pregame,
	_storyline_unused0,
	_storyline_unused1,
	_storyline_unused2,
	NUMBER_OF_STORYLINE_STRING_TAGS
};

enum // screens array indexes (these are just collections)
{
	_screen_loss,
	_screen_success,
	_screen_failure,
	NUMBER_OF_SCREEN_COLLECTION_TAGS
};

enum // map description string list indexes
{
	_map_name_string_index,
	_custom_rules_string_index,
	NUMBER_OF_DESCRIPTION_STRING_INDEXES
};

enum // sequence indexes
{
	// overhead map collection
	_overhead_map_sequence_map= 0,
	
	// pregame collection
	_pregame_sequence_story_background= 0,
	_pregame_sequence_netgame_preview,
	_pregame_sequence_story_detail_map,
	_pregame_sequence_colors,
	_pregame_sequence_story_map,
	
	// pregame colors
	_pregame_header_text_color_index= 0,
	_pregame_body_text_color_index,
	_pregame_caption_text_color_index,
	
	// postgame collection
	_postgame_sequence_win= 0,
	_postgame_sequence_loss,
	_postgame_sequence_colors,

		_postgame_color_header= 0,
		_postgame_color_column_header,
		_postgame_color_body
};

#define SIZEOF_STRUCT_MESH_DEFINITION 1024
struct mesh_definition
{
	file_tag landscape_texturestack_tag;
	file_tag media_tag;
	
	short submesh_width, submesh_height;
	long mesh_offset, mesh_size;
	void *mesh;

	long data_offset, data_size;
	void *data;
	
	long marker_palette_entries, marker_palette_offset, marker_palette_size; // array of marker_data structures
	void *marker_palette; // combined with marker list in scenario_header
	
	long marker_count, markers_offset, markers_size; // array of marker_data structures
	void *markers;
	
	file_tag mesh_lighting_tag; // shadow intensity, ambient light, etc.
	file_tag connector_tag; // for fences
	
	unsigned long flags;
	
	file_tag particle_system_tag;

	long team_count;
	
	short_fixed dark_fraction;
	short_fixed light_fraction;
	struct rgb_color dark_color;
	struct rgb_color light_color;
	fixed transition_point;

	short_world_distance ceiling_height;
	short unused1;

	// this is not a rectangle; these are the buffers on each edge of the map, in cells
	rectangle2d edge_of_mesh_buffer_zones;
	
	file_tag global_ambient_sound_tag;

	long map_action_count, map_actions_offset, map_action_buffer_size;

	file_tag map_description_string_list_tag;
	file_tag postgame_collection_tag;
	file_tag pregame_collection_tag;
	file_tag overhead_map_collection_tag;
	file_tag next_mesh_tags[NUMBER_OF_NEXT_MESH_TAGS];
	file_tag cutscene_movie_tags[NUMBER_OF_CUTSCENE_MOVIE_TAGS];
	file_tag storyline_string_tags[NUMBER_OF_STORYLINE_STRING_TAGS];
	
	long media_coverage_region_offset, media_coverage_region_size;
	short *media_coverage_region;
	
	long mesh_LOD_data_offset, mesh_LOD_data_size;
	word *mesh_LOD_data;

	struct rgb_color global_tint_color;
	short_fixed global_tint_fraction;
	word pad;
	
	file_tag wind_tag;

	file_tag screen_collection_tags[NUMBER_OF_SCREEN_COLLECTION_TAGS];
	
	struct rgb_color blood_color;

	file_tag picture_caption_string_list_tag;
	file_tag narration_sound_tag;
	file_tag win_ambient_sound_tag;
	file_tag loss_ambient_sound_tag;

	// For Creative's Environmental Audio Extensions
	unsigned long reverb_environment;
	float reverb_volume;
	float reverb_decay_time;
	float reverb_damping;

	long connector_count, connectors_offset, connectors_size; // array of connectors (i.e. fences)
	struct saved_connector_data *connectors;

	char cutscene_names[NUMBER_OF_CUTSCENE_MOVIE_TAGS][64];

	file_tag hints_string_list_tag;

	struct rgb_color fog_color;
	float fog_density;

	file_tag difficulty_level_override_string_list_tag;

	// joe added these fields:
	unsigned short new_field_block_valid_code; // must == 0xFEDC
	short padd;
	file_tag detail_stack_tag;
	file_tag environment_texture_tag;
	file_tag ground_sprites_tag;
	file_tag level_picture_tag;
	file_tag collapsed_detail_stack_tag;
	
	int prologue_parms[4];	// x/y/zoom_speed/scroll_speed.

	file_tag postgame_pics[2];	// jpg's.
	file_tag postgame_text[2];	// text.
	file_tag postgame_mp3[2];	// 'quik' type.
	short postgame_speed[2]; 	// in ticks of some kind... look at the epilogue stuff.
	file_tag prologue_quik[4];  // last one's not used.
	
	short unused[196];
	
	short connector_type;
	short map_description_string_index;
	short overhead_map_collection_index;
	short landscape_textures_index;
	short detail_textures_index;
	short environment_texture_index;
	short global_ambient_sound_index;
	short media_type;
	short hints_string_list_index;
	short padding;
	byte editor_data[MESH_DEFINITION_EDITOR_DATA_SIZE];
};

extern byte_swap_code mesh_definition_byte_swapping_data[];
extern byte_swap_code mesh_cell_bs_data[];

// ============================================================================================
bool read_mesh_definition( mesh_definition* def, tag id );
bool save_mesh_definition( mesh_definition* def, tag id );

bool read_mesh_definition( mesh_definition* def, tag_entry* e );
bool save_mesh_definition( mesh_definition* def, tag_entry* e );

#include "TTagEditForm.h"

class TmeshEditForm: public TTagEditForm {
	public:
							TmeshEditForm( struct tag_entry *e );
				
		DECLAREDISPATCH
		virtual long		DoControlClick( long arg, void *ptr );
		
		virtual void		CommitToTag();
		virtual void		ReloadTag();
		virtual bool		NeedCommit();
		
	protected:
		void				SetValues();
		void				GetValues();
		mesh_definition	def;
};

#endif