/*
 * Copyright (C) Volition, Inc. 1999.  All rights reserved.
 *
 * All source code herein is the property of Volition, Inc. You may not sell 
 * or otherwise commercially exploit the source or things you created based on the 
 * source.
 *
*/


#ifndef _3D_H
#define _3D_H

#include "camera/camera.h"
#include "graphics/2d.h"
#include "graphics/grbatch.h"
#include "graphics/tmapper.h"
#include "math/vecmat.h"

//flags for point structure
#define PF_PROJECTED 	 1	//has been projected, so sx,sy valid
#define PF_OVERFLOW		 2	//can't project
#define PF_TEMP_POINT	 4	//created during clip

//clipping codes flags
#define CC_OFF_LEFT	1
#define CC_OFF_RIGHT	2
#define CC_OFF_BOT	4
#define CC_OFF_TOP	8
#define CC_OFF_USER	16
#define CC_BEHIND		0x80

// Combo!
#define CC_OFF			(CC_OFF_LEFT | CC_OFF_RIGHT | CC_OFF_BOT | CC_OFF_TOP | CC_OFF_USER | CC_BEHIND)

/**
 * Start the frame. Pass non-zero to enable zbuffering
 */
#define g3_start_frame(zbuffer_flag) g3_start_frame_func(zbuffer_flag, __FILE__, __LINE__ )

/**
 * Use the g3_start_frame macro instead of calling this directly.
 */
extern void g3_start_frame_func(int zbuffer_flag, const char *filename, int lineno);

/**
 * End the frame
 */
#define g3_end_frame() g3_end_frame_func( __FILE__, __LINE__ )
extern void g3_end_frame_func(const char *filename, int lineno);

/**
 * Currently in frame?
 */
extern int g3_in_frame();

constexpr float PROJ_FOV_FACTOR = 1.39626348f;
extern fov_t Proj_fov;					// Projection matrix fov (for HT&L)

/**
 * Sets the Proj_fov from the specified zoom value
 */
void g3_set_fov(fov_t zoom);

float g3_get_hfov(const fov_t& fov, bool visible_fov = true);

/**
 * Set view from camera
 */
void g3_set_view(camera *cam);

/**
 * Set view from x,y,z, viewer matrix, and zoom.  Must call one of g3_set_view_*()
 */
void g3_set_view_matrix(const vec3d *view_pos, const matrix *view_matrix, fov_t zoom);

// Never set these!
extern matrix		View_matrix;		// The matrix to convert local coordinates to screen
extern vec3d		View_position;		// The offset to convert local coordinates to screen

extern matrix		Light_matrix;		// Used to rotate world points into current local coordinates
extern vec3d		Light_base;			// Used to rotate world points into current local coordinates

extern matrix		Eye_matrix;			// Where the viewer's eye is pointing in World coordinates
extern vec3d		Eye_position;		// Where the viewer's eye is at in World coordinates
extern fov_t		Eye_fov;			// What the viewer's FOV is

extern vec3d Object_position;
extern matrix	Object_matrix;			// Where the opject is pointing in World coordinates


/**
 * Draws a line representing the horizon
 */
void g3_draw_horizon_line();


/**
 * Instance at specified point with specified orientation
 */
void g3_start_instance_matrix(const vec3d *pos, const matrix *orient, bool set_api = true);

/**
 * Instance at specified point with specified transform.
 */
void g3_start_instance_matrix(const matrix4 *transform, bool set_api = true);

/**
 * Instance at specified point with specified orientation
 */
void g3_start_instance_angles(const vec3d *pos, const angles *orient);

/**
 * Pops the old context
 */
void g3_done_instance(bool set_api = false);

/**
 * Returns true if a plane is facing the viewer. 
 *
 * Takes the unrotated surface normal of the plane, and a point on it.  The normal need not be normalized
 */
int g3_check_normal_facing(const vec3d *v, const vec3d *norm);

/**
 * Rotates a point. returns codes.  does not check if already rotated
 */
ubyte g3_rotate_vertex(vertex *dest, const vec3d *src);

/**
 * Use this for stars, etc
 */
ubyte g3_rotate_faraway_vertex(vertex *dest, const vec3d *src);

/**
 * Projects a point
 */
int g3_project_vertex(vertex *point);

/**
 * Projects a vector
 */
ubyte g3_project_vector(const vec3d *p, float *sx, float *sy );

/**
 * Rotates a point.  
 * @return Returns codes.
 */
ubyte g3_rotate_vector(vec3d *dest, const vec3d *src);

/**
 * Codes a vector.  
 * @return Returns the codes of a point.
 */
ubyte g3_code_vector(const vec3d * p);

/**
 * Calculate the depth of a point - returns the z coord of the rotated point
 */
float g3_calc_point_depth(const vec3d *pnt);

/**
 * From a 2d point, compute the vector through that point
 */
void g3_point_to_vec(vec3d *v,int sx,int sy);

/**
 * From a 2d point, compute the vector through that point.
 *
 * This can be called outside of a g3_start_frame/g3_end_frame
 * pair as long g3_start_frame was previously called.
 */
void g3_point_to_vec_delayed(vec3d *v,int sx,int sy);

/*
 * Code a point. Fills in the p3_codes field of the point, and returns the codes
 */
ubyte g3_code_vertex(vertex *point);

vec3d *g3_rotate_delta_vec(vec3d *dest,vec3d *src);


/**
 * Draws a line.
 *
 * @param p0 First point
 * @param p1 Second point
 */
int g3_draw_line(vertex *p0, vertex *p1);


/**
 * Get bitmap dims onscreen as if g3_draw_bitmap() had been called
 */
int g3_get_bitmap_dims(int bitmap, vertex *pos, float radius, int *x, int *y, int *w, int *h, int *size);

/**
 * Draw a sortof sphere - i.e., the 2d radius is proportional to the 3d
 * radius, but not to the distance from the eye.  Uses the current 2d color.
 */
int g3_draw_sphere(vertex *pnt, float rad);

/**
 * Same as g3_draw_sphere, but you pass a vector and this rotates
 * and projects it and then call g3_draw_sphere.
 */
int g3_draw_sphere_ez(const vec3d *pnt, float rad);

ubyte g3_transfer_vertex(vertex *dest, const vec3d *src);

/**
 * Draw a line in HTL mode without having to go through the rotate/project stuff
 */
void g3_draw_htl_line(const vec3d *start, const vec3d *end);

/**
 * Draw a sphere mode without having to go through the rotate/project stuff
 */
void g3_draw_htl_sphere(color* clr,
	const vec3d* position,
	float radius,
	gr_alpha_blend alpha_blend_mode,
	gr_zbuffer_type zbuffer_mode);
void g3_draw_htl_sphere(color* clr, const vec3d* position, float radius);
void g3_draw_htl_sphere(const vec3d* position, float radius);

void g3_render_primitives(material* mat, vertex* verts, int n_verts, primitive_type prim_type, bool orthographic = false);
void g3_render_primitives_textured(material* mat, vertex* verts, int n_verts, primitive_type prim_type, bool orthographic = false);
void g3_render_primitives_colored(material* mat, vertex* verts, int n_verts, primitive_type prim_type, bool orthographic = false);
void g3_render_primitives_colored_textured(material* mat, vertex* verts, int n_verts, primitive_type prim_type, bool orthographic = false);

void g3_render_rod(const color *clr, int num_points, const vec3d *pvecs, float width);

void g3_render_laser_2d(material *mat_params, vec3d *headp, float head_width, vec3d *tailp, float tail_width, float max_len);

void g3_render_rect_screen_aligned_rotated(material *mat_params, vertex *pnt, float angle, float rad);

void g3_render_rect_screen_aligned(material *mat_params, vertex *pnt, int orient, float rad, float depth = 0.0f);
void g3_render_rect_screen_aligned_2d(material *mat_params, vertex *pnt, int orient, float rad, bool isFaraway = false);

void g3_render_rect_oriented(material* mat_info, vec3d *pos, matrix *ori, float width, float height);
void g3_render_rect_oriented(material* mat_info, vec3d *pos, vec3d *norm, float width, float height);

void g3_render_line_3d(color *clr, bool depth_testing, const vec3d *start, const vec3d *end);
void g3_render_line_3d(bool depth_testing, const vec3d *start, const vec3d *end);

void g3_render_sphere(color *clr, vec3d* position, float radius);
void g3_render_sphere(vec3d* position, float radius);

void g3_render_shield_icon(color *clr, coord2d coords[6], int resize_mode = GR_RESIZE_FULL);
void g3_render_shield_icon(coord2d coords[6], int resize_mode = GR_RESIZE_FULL);

typedef struct horz_pt {
	float x, y;
	int edge;
} horz_pt;

/**
 * Flash ball
 *
 * A neat looking ball of rays that move around and look all energetic and stuff
 */
struct flash_beam{
	vertex start;
	vertex end;
	float width;
};

class flash_ball{
	flash_beam *ray;
	vec3d center;
	uint n_rays;
	void parse_bsp(int offset, ubyte *bsp_data);
	void defpoint(int off, ubyte *bsp_data);

public:
	flash_ball(int number, float min_ray_width, float max_ray_width = 0, const vec3d* dir = &vmd_zero_vector, const vec3d* pcenter = &vmd_zero_vector, float outer = PI2, float inner = 0.0f, ubyte max_r = 255, ubyte max_g = 255, ubyte max_b = 255, ubyte min_r = 255, ubyte min_g = 255, ubyte min_b = 255)
		:ray(NULL),n_rays(0)
	{
			initialize(number, min_ray_width, max_ray_width, dir, pcenter, outer, inner, max_r, max_g, max_b, min_r, min_g, min_b);
	}

	~flash_ball()
	{
		if (ray)
			vm_free(ray);
	}

	void initialize(uint number, float min_ray_width, float max_ray_width = 0, const vec3d* dir = &vmd_zero_vector, const vec3d* pcenter = &vmd_zero_vector, float outer = PI2, float inner = 0.0f, ubyte max_r = 255, ubyte max_g = 255, ubyte max_b = 255, ubyte min_r = 255, ubyte min_g = 255, ubyte min_b = 255);
	void initialize(ubyte *bsp_data, int bsp_data_size, float min_ray_width, float max_ray_width = 0, const vec3d* dir = &vmd_zero_vector, const vec3d* pcenter = &vmd_zero_vector, float outer = PI2, float inner = 0.0f, ubyte max_r = 255, ubyte max_g = 255, ubyte max_b = 255, ubyte min_r = 255, ubyte min_g = 255, ubyte min_b = 255);
	void render(int texture, float rad, float intinsity, float life);
};
#endif
