#ifndef ZF_H
#define ZF_H

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

#include <assert.h>
#include <time.h>
#include <dirent.h>

#include <math.h>

#include <glib.h>

#include <SDL/SDL.h>

#ifdef __linux__
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#endif

#ifdef __APPLE__
#include <float.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#endif

#include <fmod.h>
//#include <fmod_errors.h>

#include <cl.h>
#include <clu.h>
#include <clio.h>

#define ZF_TIME_STEP 10
#define ZF_TIME_STEP_SECS 0.001f * ZF_TIME_STEP /* time slice in seconds */

#define ZF_SHIP             (1<<0)
#define ZF_GROUND           (1<<1)

#define ZF_HIVE             (1<<2)
#define ZF_LEECH            (1<<3)
#define ZF_WASP             (1<<4)

#define ZF_LASER            (1<<5)
#define ZF_BEAM             (1<<6)
#define ZF_MISSILE          (1<<7)
#define ZF_SUPER_MISSILE    (1<<8)
#define ZF_TRITOR           (1<<9)
#define ZF_BOMB             (1<<10)
#define ZF_DECOY_EXPLODED   (1<<11)

#define ZF_DECOY            (1<<12)
#define ZF_DEBRIS           (1<<13)
#define ZF_HEXFIELD         (1<<14)
#define ZF_FLUX_RING        (1<<15)
#define ZF_DRONE            (1<<16)
#define ZF_DRONE_PICKUP     (1<<17)
#define ZF_BATTERY_RING     (1<<18)
#define ZF_TIER_RING        (1<<19)

#define ZF_TURRET           (1<<20)
#define ZF_ENEMY_MISSILE    (1<<21)
#define ZF_DROID_MISSILE    (1<<22)

#define ZF_EEL_SEGMENT      (1<<23)
#define ZF_LANDSCAPE_OBJECT (1<<24)

#define ZFT_REMOVE_RAY      (1<<25)
#define ZF_DROID            (1<<26)

#define ZF_TIER_1_RING      (1<<27)
#define ZF_TIER_2_RING      (1<<28)
#define ZF_TIER_3_RING      (1<<29)

#define ZF_BOSS_TARGET      (1<<30)
#define ZF_BOSS_BOMB        (1<<31)

/*including enemy missile so that it can be locked on */
#define ZF_ENEMY   (ZF_LEECH | ZF_WASP | ZF_TURRET | ZF_EEL_SEGMENT | ZF_DROID | ZF_BOSS_TARGET | ZF_ENEMY_MISSILE)
#define ZF_WEAPON  (ZF_LASER | ZF_BEAM | ZF_MISSILE | ZF_SUPER_MISSILE  | ZF_TRITOR)
#define ZF_EXPLOSIVE (ZF_BOMB | ZF_DECOY_EXPLODED)

#define FILENAME_STRING_LENGTH 80 /* used in zf_menu_loop.c and zf_level.c */

typedef unsigned int ZfType;

typedef struct ZfHeightmap ZfHeightmap;
typedef struct ZfHive ZfHive;
typedef struct ZfLeech ZfLeech;
typedef struct ZfWasp ZfWasp;
typedef struct ZfDroid ZfDroid;
typedef struct ZfDrone ZfDrone;  /* acutally the pickup things */
typedef struct ZfHudMessage ZfHudMessage;
typedef struct ZfEelSegment ZfEelSegment;
typedef struct ZfLandscapeObject ZfLandscapeObject;
typedef struct ZfFluxRing ZfFluxRing;
typedef struct ZfBatteryRing ZfBatteryRing;
typedef struct ZfTierRing ZfTierRing;
typedef struct ZfHexFluxField ZfHexFluxField;
typedef struct ZfLevel ZfLevel;
typedef struct ZfBossTarget ZfBossTarget;
typedef struct ZfBoss ZfBoss;
typedef struct ZfBossBomb ZfBossBomb;


/* audio mod file (.xm, .it, .s3m etc) */
typedef struct ZfAudioMod ZfAudioMod;
/* audio sample (for often played sounds like effects) */
typedef struct ZfAudioSample ZfAudioSample;
/* audio stream (for background music)  */
typedef struct ZfAudioStream ZfAudioStream;


/*
 * List management and reference counting
 */

/*
  This function returns whether of not an object is valid. This must
  be provided by an object that will be added to a Zf system that uses
  managed linked lists (er... I think that's *every* system). When an
  object is invalid, it will be removed from each system
  automagickally.

  There is one important constraint of this sort of function: once it
  returns false, it must always return false. Basically, something
  that has been invalidated can *never* be valid again!
*/
typedef bool ZfIsValid(const void* data);

/*
  This function notifies an object that it has been referenced by a
  system (or something else). The object should probably keep a
  reference count or something...
*/
typedef void ZfReference(void* data);

/*
  This function notifies an object that it has been released from a
  system (or is no longer referenced by something else). This will
  usually occur when a system realises that the object is invalid. The
  object should probably keep a reference count so that it can destroy
  itself when it is no longer referenced by anything. (It should never
  destroy itself otherwise!)
*/
typedef void ZfRelease(void* data);

/*
  Interface for data to be referenced, dereferenced etc.
*/
typedef struct ZfSmartPointer ZfSmartPointer;

struct ZfSmartPointer
{
    ZfIsValid* is_valid;
    ZfReference* reference;
    ZfRelease* release; 
};

/*
  Add an item to a list.

*/
GList*
zf_list_add_data(GList* list,
		 void* data,
		 ZfSmartPointer* smart_pointer);

/*
  Perform a function for every valid item object

*/
void
zf_list_foreach_valid(GList* list,
		      ZfSmartPointer* smart_pointer,
		      void (* func)(void* data));

/*
  Perform a function for every valid pair of item objects

*/
void
zf_list_foreach_valid_pair(GList* list,
			   ZfSmartPointer* smart_pointer,
			   void (* func)(void* data0, void* data1));

/*
  Perform a function for every valid pair of item objects in TWO LISTS
  (OMG!)

*/
void
zf_list_foreach_valid_twisted_pair(GList* list0,
				   ZfSmartPointer* smart_pointer0,
				   GList* list1,
				   ZfSmartPointer* smart_pointer1,
				   void (* func)(void* data0, void* data1));

/*
  Remove invalid items from a list.

*/
GList*
zf_list_remove_invalids(GList* list,
			ZfSmartPointer* smart_pointer);

/*
  Destroy list

*/
void
zf_list_destroy(GList* list,
		ZfSmartPointer* smart_pointer);
       
/*
 * Animation System
 */

/*
  This function is very basic. It simply makes an object animate
  itself. The term "animate" is used pretty loosely here. It just
  means that the object does something for this timestep.
*/
typedef void ZfAnimate(void* data);

/*
  Initialise animation system.
  
  Create an empty list of animators.
*/
void
zf_animation_system_init(void);

/*
  Step animation system.

  Go through all animators. Check if they are valid. If so, animate
  them. Perform garbage collection to remove and release the invalids.
*/
void
zf_animation_system_step(void);

/*
  Close animation system.

  Go through all animators and release them. Finally, free the list.
*/
void
zf_animation_system_close(void);

/*
  Add an animator to the animation system.

  The animator can be any object that provides functions required for
  list management and an animation function. The animator will be
  animated at each timestep by means of the animate function.
*/
void
zf_animation_system_add(void* data,
			ZfSmartPointer* smart_pointer,
			ZfAnimate* animate);

/* Note: There is no function to remove an animation. Since the
   animation system uses managed lists, data will be removed from the
   list when invalid. Furthermore, the data will be released (and this
   *should* result in the data being destroyed at some point when it
   is no longer referenced by anything) */

unsigned int
zf_animation_system_get_animator_count(void);

void
zf_animation_system_print(void);

/*
 * Collision System
 */

/*
  Unlike static objects, dynamic objects move around at the collision
  system needs to be able to query the current location. Each dynamic
  object needs to provide this function.
*/
typedef void ZfQueryPosition(void* data,
			     CLvertex* position);

/*
  Static objects need to provide a means of checking if dynamic
  objects collide with them. Since all dynamic objects provide a
  sphere function, static objects need to provide this function which
  checks for a collision with a sphere and returns true if a collision
  occurs (false otherwise). If a collision occurs, it sets the
  provided sphere and force. The sphere should be the same radius as
  the collider sphere, but placed just outside of the static
  object. The force should be the reflective force (calculated from
  the collider_force and some friction/restitution calculation.
*/
typedef bool ZfQueryCollision(void* data,
			      const CLUsphere* sphere,
			      const CLnormal* force,
			      ZfType collider_type,
			      CLvertex* collision_position,
			      CLnormal* collision_force_perp,
			      CLnormal* collision_force_tan);

/* Note: There is no dynamic collision function, because these are
   handled automatically by the collision system. (All dynamic objects
   are spheres after all so this will always be the same anyhow) */

/*
  Every dynamic object added to the collision system needs to perform
  collision response when it collides with a static object. This
  function provides the required information such as a pointer to the
  static object, its type, and point and force of impact (see static
  collision function for more information). It is up to the dynamic
  collider to ensure that it is moved to the new position and that it
  is affected by the collision force.
*/
/*
  Each dynamic object also needs to perform some response when it
  collides with another dynamic object. This function provides a
  pointer to the other object, its type, and the collision force. The
  collision force is calculated by the collision system from the mass
  and velocity of the collider. (The velocity is calculated by the
  difference in the sphere origins and the mass).
*/
typedef void ZfCollisionResponse(void* data,
				 const void* collider_data,
				 ZfType collider_type,
				 const CLvertex* collision_position,
				 const CLnormal* collision_force_perp,
				 const CLnormal* collision_force_tan);

/*
  Interface for dynamic colliders.
*/
typedef struct ZfDynamicCollider ZfDynamicCollider;

struct ZfDynamicCollider
{
    ZfQueryPosition* query_position;
    ZfCollisionResponse* collision_response;
};

void
zf_collision_system_init(void);

void
zf_collision_system_close(void);

/*
  Step collision system.
*/
void
zf_collision_system_step(void);


/*
  Add a static collider to the collision system.
  
  The static collider needs to provide list management functions, a
  type (so that dynamic colliders know what they have hit) and a
  collide function (to check when collisions with dynamic colliders
  occur).
*/
void
zf_collision_system_add_static(void* data,
			       ZfSmartPointer* smart_pointer,
			       ZfQueryCollision* query_collision,
			       ZfType type);

/*
  Add a dynamic collider to the collision system.

  The dynamic collider needs to provide list management functions, a
  type (so that other colliders know what they have hit), physical
  details (mass, radius, and queriable position), and collision
  response functions (to both dynamic and static colliders).
*/
void
zf_collision_system_add_dynamic(void* data,
				ZfSmartPointer* smart_pointer,
				ZfDynamicCollider* dynamic_collider,
				ZfType type,
				float mass,
				float radius);

bool
zf_collision_system_query_dynamic_target(const CLUray* ray,
					 ZfType type,
					 void** collider_data,
					 ZfSmartPointer** smart_pointer,
					 ZfDynamicCollider** dynamic_collider);

bool
zf_collision_system_query_static_target(const CLUray* ray,
					CLvertex** position);

bool
zf_collision_system_nearest_target(const CLvertex* vertex,
				   ZfType type,
				   void** collider_data,
				   ZfSmartPointer** smart_pointer,
				   ZfDynamicCollider** dynamic_collider);

unsigned int
zf_collision_system_get_collider_count(void);



/*
void
zf_target_direction(const ZfTarget* target,
		    const CLvertex* position,
		    CLnormal* direction);
*/

/*
 * Render System
 */

typedef void ZfRender(void* data);

void
zf_render_system_init(void);

void
zf_render_system_close(void);

void
zf_render_system_step(void); /* probably should be _render(void)!!! */

void
zf_render_system_step_opaque(void);

void
zf_render_system_step_translucent(void);

void
zf_render_system_add_opaque(void* data,
			    ZfSmartPointer* smart_pointer,
			    ZfRender* render);

void
zf_render_system_add_translucent(void* data,
				 ZfSmartPointer* smart_pointer,
				 ZfRender* render);

void
zf_render_system_print(void);

unsigned int
zf_render_system_get_renderable_count(void);

void
zf_render_system_set_tool_mode(bool is_tool);

bool
zf_render_system_get_tool_mode(void);

/*
 * Trigger/Spawnable System
 */
typedef void ZfSpawn(void* data);

void
zf_trigger_system_init(void);

void
zf_trigger_system_close(void);

void
zf_trigger_system_add_trigger(float flux_t,
			      void* data,
			      ZfSmartPointer* smart_pointer,
			      ZfSpawn* spawn);

/*
 * Particle System
 */
void
zf_particle_system_close(void);

void
zf_particle_system_init(void);

void
zf_particle_system_add_particle(CLvertex* position,
				CLnormal* velocity, /* per game step */
				float density,
				CLcolour* colour,
				unsigned int lifespan,
				float size);

void
zf_particle_system_print(void);

unsigned int
zf_particle_system_get_particle_count(void);



/*
 * Debris
 */
void
zf_debris_close(void);

void
zf_debris_init(void);

void
zf_debris_new(const CLmatrix* frame,
	      const CLnormal* velocity,
	      const CLnormal* axis,
	      float angular_speed,
	      float mass,
	      float radius,
	      int lifespan,
	      const CLmodel* model);

/*
 * Explosion
 */
void
zf_explosion_init(void);

void
zf_explosion_close(void);

void
zf_explosion_new(const CLvertex* position,
		 float size);

/*
 * Shields - for the ship
 */
void
zf_shield_init(void);

void
zf_shield_close(void);

bool
zf_shield_activate(int lifespan);

void
zf_shield_deactivate(void);

void
zf_shield_render(void);

/*
 * Hive
 */
void
zf_hive_init(char* filename);

void
zf_hive_close(void);

void
zf_hive_new(const CLvertex* position);

bool
zf_hive_nearest_hive(const CLvertex* vertex,
		     void** hive_data,
		     ZfSmartPointer** hive_smart_pointer);

void
zf_hive_query_position(const ZfHive* hive, 
		       CLvertex* position);

void
zf_hive_write(char* filename);


/*
 * Leech
 */
void
zf_leech_init(char* filename);

void
zf_leech_close(void);

ZfLeech*
zf_leech_new(const CLvertex* position);

void
zf_leech_write(char* filename, GList* leech_list);



/*
 * Wasp
 */
void
zf_wasp_init(char* filename);

void
zf_wasp_closer(void);

void
zf_wasp_tool_init(void);

void
zf_wasp_new(const CLvertex* position);

void
zf_wasp_save(void);



/*
 * Droid
 */
void
zf_droid_init(char* filename);

void
zf_droid_write(char* filename, GList* droid_list);

void
zf_droid_close(void);

ZfDroid*
zf_droid_new(const CLvertex* position,
	     const int droid_type,
	     const float start_t,
	     const float end_t);


/*
 * HexFluxField
 */
void
zf_hexfluxfield_init(char* filename);

void
zf_hexfluxfield_write(char* filename, GList* hexfluxfield_list);

void
zf_hexfluxfield_close(void);

ZfHexFluxField*
zf_hexfluxfield_new(const double flux_t);

bool
zf_hexfluxfield_query_ray_intercept(const CLUray* ray, 
				    void* data,
				    CLvertex** position);

/*
 * Flux Ring
 */
void
zf_flux_ring_init(char* filename);

void
zf_flux_ring_close(void);

ZfFluxRing*
zf_flux_ring_new(bool half_ring,
		 const double flux_t,
		 const float roll);

void
zf_flux_ring_write(char* filename, GList* flux_ring_list);
	     

/*
 * Battery Ring
 */
void
zf_battery_ring_init(char* filename);

void
zf_battery_ring_close(void);

ZfBatteryRing*
zf_battery_ring_new(const double flux_t);

void
zf_battery_ring_write(char* filename, GList* battery_ring_list);


/*
 * Tier Ring Obstacle
 */
void
zf_tier_ring_init(char* filename);

void
zf_tier_ring_close(void);

ZfTierRing*
zf_tier_ring_new(const double flux_t, const int tier_level);

void
zf_tier_ring_write(char* filename, GList* tier_ring_list);


/*
 * Drone
 */
void
zf_drone_init(char* filename);

void
zf_drone_close(void);

ZfDrone*
zf_drone_new(unsigned int type, const CLvertex* position);

void
zf_drone_write(char* filename, GList* drone_list);

bool
zf_drone_query_ray_intercept(const CLUray* ray, 
			     void* data,
			     CLvertex** position);

/*
 * Drone Pickup
 */
void
zf_drone_pickup_init(void);

void
zf_drone_pickup_close(void);

void
zf_drone_pickup_new(int type, const CLvertex* position);


/*
 * Eel
 */
void
zf_eel_init(void);

void
zf_eel_close(void);

void
zf_eel_segment_init(char* filename);

void
zf_eel_segment_write(char* filename, GList* eel_segment_list);

ZfEelSegment*
zf_eel_segment_new(float start_flux_t,
		   float end_flux_t,
		   float roll, 
                   int num_segments,
                   ZfEelSegment* parent);



/*
 * Turret
 */
void
zf_turret_init(char* filename);

void
zf_turret_close(void);

void
zf_turret_new(const CLvertex* position);

/*
 * Enemy Missile
 */
void
zf_enemy_missile_init(void);

void
zf_enemy_missile_close(void);

void
zf_enemy_missile_new(const CLvertex* launch_position,
		     const int type,
		     const CLvertex* hover_position,
		     ZfBoss* boss,
		     ZfSmartPointer* boss_smarter_pointer);

/*
 * Droid Missile
 */
void
zf_droid_missile_init(void);

void
zf_droid_missile_close(void);

void
zf_droid_missile_new(const CLvertex* position,
		     const int droid_type);

/*
 * Missile
 */
void
zf_missile_init(void);

void
zf_missile_close(void);

void
zf_missile_new(const CLvertex* position,
	       float speed,
	       void* target_data,
	       ZfSmartPointer* smart_pointer,
	       ZfDynamicCollider* dynamic_collider);

void
zf_super_missile_new(const CLvertex* position,
		     float speed,
		     void* target_data,
		     ZfSmartPointer* smart_pointer,
		     ZfDynamicCollider* dynamic_collider);

/*
 * Laser
 */
void
zf_laser_init(void);

void
zf_laser_close(void);

void
zf_laser_new(const CLvertex* origin,
	     const CLvertex* destination);

void
zf_beam_new(const CLvertex* origin,
	    const CLvertex* destination);

#if 0
/*
 * Torpedo - Runs along the flux.
 */
void
zf_torpedo_init(void);

void
zf_torpedo_close(void);

void
zf_torpedo_new(const CLmatrix* flux_frame,
	       float flux_t,
	       float offset_height,
	       float roll);
#endif
/*
 * TriTor - Runs along the flux.
 */
void
zf_tritor_init(void);

void
zf_tritor_close(void);

void
zf_tritor_new(const CLmatrix* flux_frame,
	      float flux_t,
	      float roll);

/*
 * Decoy
 */
void
zf_decoy_init(void);

void
zf_decoy_close(void);

/* Expects normalised direction */
void
zf_decoy_new(CLvertex* ship_pos,
	     CLnormal* direction);


/*
 * Bomb
 */
void
zf_bomb_init(void);

void
zf_bomb_close(void);

void
zf_bomb_new(CLvertex* ship_pos);


/*
 * Skybox
 */
void
zf_skybox_init(char* filename);

void
zf_skybox_render(void);

void
zf_skybox_close(void);


/*
 * Water
 */
void
zf_water_init(float water_level, float width, float length);

void
zf_water_render(void);

void
zf_water_render_quad(void);

void 
zf_water_load_clip_plane(void);

void
zf_water_close(void);

float
zf_water_query_level(void);


/*
 * Heightmap
 */
void
zf_heightmap_init(char* filename);

void
zf_heightmap_close(void);

float
zf_heightmap_query_height(float x, float z);

void
zf_heightmap_query_normal(CLnormal* result, float x, float z);

void
zf_heightmap_set_wire_mode(bool wire);

void
zf_heightmap_write_file(char* heights_filename,
			char* colours_filename,
			char* detail_texture_filename,
			char* output_filename,
			CLUalignedbox* box);

bool
zf_heightmap_loaded(void);

/*
 * Camera
 */
void
zf_camera_init(void);

void
zf_camera_step(void);

void
zf_camera_load(void);

void
zf_camera_set_lookat_relative(int x, int y);

void
zf_camera_activate_boss_mode(const CLvertex* pos);

void
zf_camera_deactivate_boss_mode(void);

bool
zf_camera_query_boss_mode(void);


/* recovery should be BETWEEN 0.0f - 1.0f, indicating the scale of recovery time between max_min*/ 
void
zf_camera_shake_start(float recovery);


/*
 * Flux 
 */
void
zf_flux_init(char* filename);

void
zf_flux_close(void);

void
zf_flux_increase_power(float amount);

void 
zf_flux_decrease_power(float amount);

/*
  double
  zf_flux_query_power(void);
*/

void
zf_flux_query_start_platform_frame(CLmatrix* frame);

void
zf_flux_query_end_platform_frame(CLmatrix* frame);

/* returns t value at end of flux */
double
zf_flux_query_max_t(void);

/* given a flux t, returns global coordinates of flux */
void
zf_flux_query_position(CLvertex* position,
		       double t);

/* Given a distance, this methods approximates what the next t should be*/
double
zf_flux_query_next_t_given_distance(double t,
				    float distance);

/* Given a distance, this methods approximates what the previous t should be - For going backwards on the flux.*/
double
zf_flux_query_previous_t_given_distance(double t,
					float distance);

void
zf_flux_update_frame(CLmatrix* frame,
		     double t);

/* Assumes repeaters cannot be destroyed */
void
zf_flux_nearest_repeater(const CLvertex* vertex,
			 CLvertex* repeater_position);

float
zf_flux_nearest_repeater_t(const CLvertex* vertex);

void
zf_flux_add_repeater(const CLvertex* vertex);

/* z axis is aligned to normal, normal is normalised in this function */
void
zf_align_frame_z_vertex_normal(CLmatrix* frame,
			     CLvertex* vertex,
			     CLnormal* normal);

void
zf_align_frame_y_vertex_normal(CLmatrix* frame,
			       CLvertex* vertex,
			       CLnormal* normal);

void
zf_flux_write(char* filename);



/*
 * Landscape Objects
 */
void
zf_landscape_object_init(char* filename);

void
zf_landscape_object_close(void);

ZfLandscapeObject*
zf_landscape_object_new(const CLvertex* position, 
			const int type);

void
zf_landscape_object_write(char* filename);



/*
 * Boss
 */
void
zf_boss_init(char* filename);

void
zf_boss_close(void);

void
zf_boss_decrement_target(ZfBoss* boss);

void
zf_boss_new(const CLvertex* position, 
	    const float flux_t,
	    const int num_targets, 
	    const CLvertex* target_positions); /* an array */

CLmatrix*
zf_boss_query_frame(ZfBoss* boss);

bool
zf_boss_query_dying(ZfBoss* boss);

bool
zf_boss_query_battle(ZfBoss* boss);

void
zf_boss_launch_mega_attack(void);

/*
 * Boss Bomb
 */
void
zf_boss_bomb_init(void);

void
zf_boss_bomb_close(void);

void
zf_boss_bomb_new(CLvertex* ship_pos);



/*
 * Boss Target
 */
void
zf_boss_target_init(void);

void
zf_boss_target_close(void);

void
zf_boss_target_destroy(ZfBossTarget* target);

void
zf_boss_target_activate(ZfBossTarget* target);

void
zf_boss_target_reset(ZfBossTarget* target);

/* Assumes local coords for position relative to boss */
ZfBossTarget* 
zf_boss_target_new(const CLvertex* position, 
		   const float flux_t,
		   const int index,
		   ZfBoss* boss,
		   ZfSmartPointer* boss_smart_pointer);
/*
  bool
  zf_target_query_ray_intercept(const CLUray* ray, 
  void* data,
  CLvertex** position);
*/

/*
 * GameLoop
 */
void
zf_game_loop_init(char* level_filename);

void
zf_game_loop_close(void);

int
zf_game_loop_run(void); /* returns the result of the game. 0 for
			   lose. 1 for win */
void
zf_game_loop_reset_time(void);

void
zf_game_loop_set_super_blur(bool mode);

void
zf_game_loop_set_blur(bool mode);

void
zf_game_loop_set_cheat_mode(bool mode);

void
zf_game_loop_set_water(bool mode);



/*
 * Ship
 */
void
zf_ship_init(void);

void
zf_ship_restart(void);  /* for when die and next life */

void
zf_ship_close(void);

void
zf_ship_query_frame(CLmatrix* frame);

double
zf_ship_query_power(void);

void
zf_ship_query_position(CLvertex* position);

void
zf_ship_query_flux_frame(CLmatrix* matrix);

float
zf_ship_query_flux_t(void);

float
zf_ship_query_hover_height(void);

int
zf_ship_query_tier_level(void);

float
zf_ship_query_thrust(void);

float
zf_ship_query_current_roll(void);

bool
zf_ship_query_super_mode(void);

bool
zf_ship_query_dead(void);

bool
zf_ship_query_defeat_boss(void);

/* Hack - only boss should call this */
void
zf_ship_set_defeat_boss(bool defeat);

bool
zf_ship_query_end_state(void);

bool
zf_ship_query_landed(void);

int
zf_ship_query_lives(void);

void
zf_ship_super_battery_mode(void);

void
zf_ship_increment_life(void);

void
zf_ship_increase_thrust(void);

void
zf_ship_decrease_thrust(void);

void
zf_ship_move_up_tier(void);

void
zf_ship_move_down_tier(void);

void
zf_ship_jump_flux(float value);

void
zf_ship_hit_tier(const int tier);

void
zf_ship_animate_hit_tier(const int tier);

void
zf_ship_increase_roll(void);

void
zf_ship_decrease_roll(void);

void
zf_ship_increase_power(float amount);

/* Returns true if it can decrease it, otherwise false - for the weapons mainly */
bool
zf_ship_decrease_power(float amount);

bool
zf_ship_start(void);


/*
 * WeaponControl
*/
void
zf_weapon_control_shoot_laser(void);

void
zf_weapon_control_shoot_beam(void);

void
zf_weapon_control_shoot_missile(void);

void
zf_weapon_control_shoot_super_missile(void);

void
zf_weapon_control_shoot_torpedo(void);

void
zf_weapon_control_shoot_tritor(void);

void
zf_weapon_control_launch_decoy(void);

void
zf_weapon_control_launch_bomb(void);

/* Assumes the weapon state is valid */
void
zf_weapon_control_set_weapon(ZfType state);
  
ZfType
zf_weapon_control_query_weapon(void);
 
void
zf_weapon_control_start_lock_mode(void);
  
void
zf_weapon_control_stop_lock_mode(void);

void
zf_weapon_control_start_super_lock_mode(void);
  
void
zf_weapon_control_stop_super_lock_mode(void);

/*
bool
zf_weapon_control_query_lock_mode(void);
*/
void
zf_weapon_control_start_beam_mode(void);
  
void
zf_weapon_control_stop_beam_mode(void);
/*
bool
zf_weapon_control_query_beam_mode(void);
*/
int
zf_weapon_control_query_missile_stock(void);

int
zf_weapon_control_query_torpedo_stock(void);

int
zf_weapon_control_query_decoy_stock(void);

void
zf_weapon_control_add_to_missile_stock(int number);

void
zf_weapon_control_add_to_torpedo_stock(int number);

void
zf_weapon_control_add_to_decoy_stock(int number);

void
zf_weapon_control_add_to_bomb_tritor_stock(int number);

void
zf_weapon_control_render_locked(void);
/* Update cross hair position and update lock_list */
void
zf_weapon_control_step(void); 

/* for the hud */
float
zf_weapon_control_query_tritor_countdown(void);

/* for the hud */
float
zf_weapon_control_query_bomb_countdown(void);

/* for the hud */
int
zf_weapon_control_query_bomb_tritor_stock(void);



/*
 * HUD
 */
void
zf_hud_init(char* filename); /* file for the hud messages */

void
zf_hud_close(void);

void
zf_hud_render(void);

void 
zf_hud_increase_score(int increment);

unsigned int
zf_hud_query_score(void);

ZfHudMessage*
zf_hud_new_message(float flux_t, int type, CLvertex *position, char *message);

void
zf_hud_message_kill(ZfHudMessage* msg);

void
zf_hud_message_write(char* filename, GList* hud_message_list);

void
zf_hud_set_boss_mode(int mode);


/*
 * Score indicators - that show up when an enemy is taken out.
 */
void
zf_score_indicator_init(void);

void
zf_score_indicator_close(void);

void	
zf_score_indicator_new(const CLvertex* position, 
		       const int score_value);


/*
 * CrossHair
 */
void
zf_cross_hair_init(void);

void
zf_cross_hair_close(void);

void
zf_cross_hair_render(void);

void
zf_cross_hair_set_position(int u, int v);

void
zf_cross_hair_query_position_distance(CLvertex* position);


/* audio */
void
zf_audio_init(void);

void
zf_audio_close(void);

void
zf_audio_print(void);

float*
zf_audio_get_spectrum(void);

float
zf_audio_get_spectrum_average(void);

/* audio mod engine */
ZfAudioMod*
zf_audio_mod_new(void);

ZfAudioMod*
zf_audio_mod_load(const char* filename);

void
zf_audio_mod_play(ZfAudioMod* mod);

void
zf_audio_mod_set_looping(ZfAudioMod* mod, bool looping);

void
zf_audio_mod_set_speed(ZfAudioMod* mod, float speed);

void
zf_audio_mod_print(ZfAudioMod* mod);

void
zf_audio_mod_destroy(ZfAudioMod* mod);

/* audio sample engine */
ZfAudioSample*
zf_audio_sample_new(void);

ZfAudioSample*
zf_audio_sample_load(const char* filename);

void
zf_audio_sample_play(ZfAudioSample* sample);

void
zf_audio_sample_stop(ZfAudioSample* sample);

void
zf_audio_sample_set_looping(ZfAudioSample* sample, bool looping);

void
zf_audio_sample_set_volume(ZfAudioSample* sample, int volume);

void
zf_audio_sample_print(ZfAudioSample* sample);

void
zf_audio_sample_destroy(ZfAudioSample* sample);

FSOUND_SAMPLE*
zf_audio_sample_get_sample(ZfAudioSample* sample);

/* audio stream engine */
ZfAudioStream*
zf_audio_stream_new(void);

ZfAudioStream*
zf_audio_stream_load(const char* filename);

ZfAudioStream*
zf_audio_stream_read(FILE* stream);

void
zf_audio_stream_play(ZfAudioStream* stream);

void
zf_audio_stream_stop(ZfAudioStream* stream);

void
zf_audio_stream_pause(ZfAudioStream* stream);

void
zf_audio_stream_set_loop_count(ZfAudioStream* stream, int count);

void
zf_audio_stream_print(ZfAudioStream* stream);

void
zf_audio_stream_destroy(ZfAudioStream* stream);

void
zf_audio_disable(void);


/*
  menu
*/

/* nick - removed since these don't need to be public, moved to game_loop */
/*
void 
zf_menu_update_progress_bar(float progress);

void 
zf_menu_loading_complete(void);
*/

/* TEXT */
void
zf_text_draw(const char* text);


/* ZfLevel */

ZfLevel*
zf_level_new(void);

void
zf_level_close(ZfLevel* level);

void
zf_level_write_file(ZfLevel* level, char* output_filename);

ZfLevel*
zf_level_read_file(char* input_filename);

char**
zf_level_search_directory(unsigned int* num_filenames, char* directory);

void
zf_level_load(ZfLevel* level);

void
zf_level_print(ZfLevel* level);

#if 1
void zf_level_set_skybox_file       (ZfLevel* level, char* filename);
void zf_level_set_heightmap_file    (ZfLevel* level, char* filename);
void zf_level_set_flux_file         (ZfLevel* level, char* filename);
void zf_level_set_hives_file        (ZfLevel* level, char* filename);
void zf_level_set_wasps_file        (ZfLevel* level, char* filename);
void zf_level_set_hexfluxfields_file(ZfLevel* level, char* filename);
void zf_level_set_fluxrings_file    (ZfLevel* level, char* filename);
void zf_level_set_batteryrings_file (ZfLevel* level, char* filename);
void zf_level_set_tierrings_file    (ZfLevel* level, char* filename);
void zf_level_set_drones_file       (ZfLevel* level, char* filename);
void zf_level_set_droids_file       (ZfLevel* level, char* filename);
void zf_level_set_eels_file         (ZfLevel* level, char* filename);
void zf_level_set_hudmessages_file  (ZfLevel* level, char* filename);
void zf_level_set_landscape_file    (ZfLevel* level, char* filename);
void zf_level_set_boss_file         (ZfLevel* level, char* filename);
void zf_level_set_leeches_file         (ZfLevel* level, char* filename);

char* zf_level_get_skybox_file       (ZfLevel* level);
char* zf_level_get_heightmap_file    (ZfLevel* level);
char* zf_level_get_flux_file         (ZfLevel* level);
char* zf_level_get_hives_file        (ZfLevel* level);
char* zf_level_get_wasps_file        (ZfLevel* level);
char* zf_level_get_hexfluxfields_file(ZfLevel* level);
char* zf_level_get_fluxrings_file    (ZfLevel* level);
char* zf_level_get_batteryrings_file (ZfLevel* level);
char* zf_level_get_tierrings_file    (ZfLevel* level);
char* zf_level_get_drones_file       (ZfLevel* level);
char* zf_level_get_droids_file       (ZfLevel* level);
char* zf_level_get_eels_file         (ZfLevel* level);
char* zf_level_get_hudmessages_file  (ZfLevel* level);
char* zf_level_get_landscape_file    (ZfLevel* level);
char* zf_level_get_boss_file         (ZfLevel* level);
char* zf_level_get_leeches_file         (ZfLevel* level);
#endif

void zf_level_get_level_name_from_file(char* name, char* filename);

/* !!!IMPORTANT!!! THINKING ABOUT SINGLETONS

   we have a singleton called something like game_state
   
   other singletons use the game_state in their "proxy/pseudo" smart
   pointers.

   so, each singleton is_valid returns true if the game is
   running. otherwise, it returns false and will make the systems that
   it is referenced by call cleanup. when the first system calls
   cleanup (and we assume all will at almost the same time), the
   "close" function for the singleton will be called by the release
   function. there is no risk of null pointer exception
   because... well... the data is null anyway.

*/
/*
  render system

  set alpha test to discard non-opaque
  render opaque

  set alpha test to discard non-translucent
  enable blending
  render translucent
  disable blending

  want callbacks for

  in frustum
  distance hint

  want data for

  opaque/translucent (if both, add twice with different render funcs)
  texture/not textured (all textured?)

  want to be able to add

  lights... or just loadables?... probably lights
  renderables

  animation system
  (add scene collide to animation system)

  render system
  (add scene to render_system)

  scene
  - add_light(CLlight* light);

  - add_static(valid(), reference(), release(),
                type, collide_sphere(), in_box(), collide_ray(),
                opaque, translucent, render());

  - add_dynamic(valid(), reference(), release(),
                type, radius, mass, position(),
                opaque, translucent, render());
*/

#endif /* ZF_H */
