/*
 * 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.
 *
*/





#include "bmpman/bmpman.h"
#include "freespace.h"
#include "gamesnd/gamesnd.h"
#include "globalincs/linklist.h"
#include "graphics/font.h"
#include "graphics/matrix.h"
#include "iff_defs/iff_defs.h"
#include "io/timer.h"
#include "jumpnode/jumpnode.h"
#include "localization/localize.h"
#include "network/multi.h"
#include "object/object.h"
#include "playerman/player.h"
#include "radar/radar.h"
#include "ship/awacs.h"
#include "ship/ship.h"
#include "ship/subsysdamage.h"
#include "weapon/emp.h"
#include "weapon/weapon.h"

extern int radar_target_id_flags;

HudGaugeRadarStd::HudGaugeRadarStd():
HudGaugeRadar(HUD_OBJECT_RADAR_STD, 255, 255, 255)
{
	gr_init_alphacolor( &radar_crosshairs, 255, 255, 255, 196);
}

void HudGaugeRadarStd::initCenterOffsets(float x, float y)
{
	Radar_center_offsets[0] = x;
	Radar_center_offsets[1] = y;
}

void HudGaugeRadarStd::initBitmaps(char *fname)
{
	Radar_gauge.first_frame = bm_load_animation(fname, &Radar_gauge.num_frames);
	if ( Radar_gauge.first_frame < 0 ) {
		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
	}
}

void HudGaugeRadarStd::blipDrawDistorted(blip *b, int x, int y)
{
	int xdiff, ydiff;
	float scale;
	xdiff =  Random::next(-10, 9);
	ydiff =  Random::next(-10, 9);

	// maybe scale the effect if EMP is active
	if(emp_active_local()){
		scale = emp_current_intensity();

		xdiff = (int)((float)xdiff * scale);
		ydiff = (int)((float)ydiff * scale);
	}

	drawContactCircle(x + xdiff, y + ydiff, b->rad);
}
void HudGaugeRadarStd::blipDrawFlicker(blip *b, int x, int y)
{
	int xdiff=0, ydiff=0, flicker_index;

	if ( (b-Blips) & 1 ) {
		flicker_index=0;
	} else {
		flicker_index=1;
	}

	if ( timestamp_elapsed(Radar_flicker_timer[flicker_index]) ) {
		Radar_flicker_timer[flicker_index] = _timestamp_rand(50,1000);
		Radar_flicker_on[flicker_index] = !Radar_flicker_on[flicker_index];
	}

	if ( !Radar_flicker_on[flicker_index] ) {
		return;
	}

	if (Random::flip_coin()) {
		xdiff = Random::next(-2, 1);
		ydiff = Random::next(-2, 1);
	}

	drawContactCircle(x + xdiff, y + ydiff, b->rad);
}
void HudGaugeRadarStd::blitGauge(bool config)
{
	int x = position[0];
	int y = position[1];
	float scale = 1.0;

	if (config) {
		std::tie(x, y, scale) = hud_config_convert_coord_sys(position[0], position[1], base_w, base_h);
		int bmw, bmh;
		bm_get_info(Radar_gauge.first_frame + 1, &bmw, &bmh);
		hud_config_set_mouse_coords(gauge_config_id, x, x + fl2i(bmw * scale), y, y + fl2i(bmh * scale));
	}
	
	if (Radar_gauge.first_frame + 1 >= 0)
		renderBitmap(Radar_gauge.first_frame+1, x, y, scale, config);
}
void HudGaugeRadarStd::drawBlips(int blip_type, int bright, int distort)
{
	blip *b = NULL;
	blip *blip_head = NULL;
	int x, y;

	Assert((blip_type >= 0) && (blip_type < MAX_BLIP_TYPES));


	// Need to set font.
	font::set_font(font::FONT1);


	// get the appropriate blip list
	if (bright)
		blip_head = &Blip_bright_list[blip_type];
	else
		blip_head = &Blip_dim_list[blip_type];


	// draw all blips of this type
	for (b = GET_FIRST(blip_head); b != END_OF_LIST(blip_head); b = GET_NEXT(b))
	{
		gr_set_color_fast(b->blip_color);
		plotBlip(b, &x, &y);

		// maybe draw cool blip to indicate current target
		if (b->flags & BLIP_CURRENT_TARGET)
		{
			b->rad = Radar_blip_radius_target;
			current_target_x = x;
			current_target_y = y;
		}
		else
		{
			b->rad = Radar_blip_radius_normal;
		}

		// maybe distort blip
		if (distort)
		{
			blipDrawDistorted(b, x, y);
		}
		else if (b->flags & BLIP_DRAW_DISTORTED)
		{
			blipDrawFlicker(b, x, y);
		}
		else
		{
			bool show_icon = (Radar_2d_icon_mode == RadarIconMode::On ||
			                  (Radar_2d_icon_mode == RadarIconMode::TargetOnly && (b->flags & BLIP_CURRENT_TARGET)));
			if (!show_icon || (b->radar_image_2d == -1 && b->radar_color_image_2d == -1))
				drawContactCircle(x, y, b->rad);
			else
				drawContactImage(x, y, b->rad, b->radar_image_2d, b->radar_color_image_2d, b->radar_image_size);
		}
	}
}
void HudGaugeRadarStd::drawBlipsSorted(int distort)
{
	current_target_x = 0;
	current_target_y = 0;
	// draw dim blips first, then bright blips
	for (int is_bright = 0; is_bright < 2; is_bright++)
	{
		drawBlips(BLIP_TYPE_JUMP_NODE, is_bright, distort);
		drawBlips(BLIP_TYPE_WARPING_SHIP, is_bright, distort);
		drawBlips(BLIP_TYPE_NAVBUOY_CARGO, is_bright, distort);
		drawBlips(BLIP_TYPE_NORMAL_SHIP, is_bright, distort);
		drawBlips(BLIP_TYPE_BOMB, is_bright, distort);
		drawBlips(BLIP_TYPE_TAGGED_SHIP, is_bright, distort);
	}
	// draw crosshairs last - if at all.
	if(radar_target_id_flags & RTIF_CROSSHAIRS) {
		drawCrosshairs(current_target_x, current_target_y);
	}
}
void HudGaugeRadarStd::drawContactCircle( int x, int y, int rad )
{
	if ( rad == Radar_blip_radius_target )	{
		if (radar_target_id_flags & RTIF_BLINK) {
			if (Missiontime & 8192)
				return;
		}
		renderCircle(x, y, 6);
	} else {
		// rad = RADAR_BLIP_RADIUS_NORMAL;
		renderCircle(x, y, 4);
	}
}
void HudGaugeRadarStd::drawContactImage( int x, int y, int rad, int idx, int clr_idx, int size )
{
	// this we will move as ships.tbl option (or use for radar scaling etc etc)
	//int size = 24; 
	
	int w, h, old_bottom, old_bottom_unscaled, old_right, old_right_unscaled;
	float scalef, wf, hf, xf, yf;
	vec3d blip_scaler;

	if(bm_get_info(idx, &w, &h) < 0)
	{
		// Just if something goes terribly wrong
		drawContactCircle(x, y, rad);
		return;
	}

	// just to make sure the missing casts wont screw the math
	wf = (float) w;
	hf = (float) h;
	xf = (float) x;
	yf = (float) y;

	// make sure we use the larger dimension for the scaling
	// lets go case by case to make sure there are no probs
	if (size == -1)
		scalef = 1.0f;
	else if ((h == w) && (size == h))
		scalef = 1.0f;
	else if ( h > w)
		scalef = ((float) size) / hf;
	else
		scalef = ((float) size) / wf;

	Assert(scalef != 0);

	// animate the targeted icon - option 1 of highlighting the targets
	if ( rad == Radar_blip_radius_target ) {
		if (radar_target_id_flags & RTIF_PULSATE) {
			scalef *= 1.3f + (sinf(10 * f2fl(Missiontime)) * 0.3f);
		}
		if (radar_target_id_flags & RTIF_BLINK) {
			if (Missiontime & 8192)
				return;
		}
		if (radar_target_id_flags & RTIF_ENLARGE) {
			scalef *= 1.3f;
		}
	}

	// setup the scaler
	blip_scaler.xyz.x = scalef;
	blip_scaler.xyz.y = scalef;
	blip_scaler.xyz.z = 1.0f;
	
	old_bottom = gr_screen.clip_bottom;
	old_bottom_unscaled = gr_screen.clip_bottom_unscaled;
	gr_screen.clip_bottom = (int) (old_bottom/scalef);
	gr_screen.clip_bottom_unscaled = (int) (old_bottom_unscaled/scalef);

	old_right = gr_screen.clip_right;
	old_right_unscaled = gr_screen.clip_right_unscaled;
	gr_screen.clip_right = (int) (old_right/scalef);
	gr_screen.clip_right_unscaled = (int) (old_right_unscaled/scalef);

	// scale the drawing coordinates
	x = (int) ((xf / scalef) - wf/2.0f);
	y = (int) ((yf / scalef) - hf/2.0f);

	gr_push_scale_matrix(&blip_scaler);

	if ( idx >= 0 ) {
		gr_set_bitmap(idx,GR_ALPHABLEND_NONE,GR_BITBLT_MODE_NORMAL,1.0f);
		renderBitmap( x, y );
	}

	if ( clr_idx >= 0 ) {
		gr_set_screen_scale(base_w, base_h);
		gr_bitmap(x, y);
		gr_reset_screen_scale();
	}

	gr_pop_scale_matrix();

	gr_screen.clip_bottom = old_bottom;
	gr_screen.clip_bottom_unscaled = old_bottom_unscaled;

	gr_screen.clip_right = old_right;
	gr_screen.clip_right_unscaled = old_right_unscaled;
}

void HudGaugeRadarStd::render(float  /*frametime*/, bool config)
{
	//WMC - This strikes me as a bit hackish
	bool g3_yourself = !g3_in_frame();
	if(g3_yourself)
		g3_start_frame(1);

	int ok_to_blit_radar = 1;

	if (!config) {
		float sensors_str = ship_get_subsystem_strength(Player_ship, SUBSYSTEM_SENSORS);

		if (ship_subsys_disrupted(Player_ship, SUBSYSTEM_SENSORS)) {
			sensors_str = MIN_SENSOR_STR_TO_RADAR - 1;
		}

		// note that on lowest skill level, there is no radar effects due to sensors damage
		if (((Game_skill_level == 0) || (sensors_str > SENSOR_STR_RADAR_NO_EFFECTS)) && !Sensor_static_forced) {
			Radar_static_playing = false;
			Radar_static_next = TIMESTAMP::never();
			Radar_death_timer = TIMESTAMP::never();
			Radar_avail_prev_frame = true;
		} else if (sensors_str < MIN_SENSOR_STR_TO_RADAR) {
			if (Radar_avail_prev_frame) {
				Radar_death_timer = _timestamp(2000);
				Radar_static_next = TIMESTAMP::immediate();
			}
			Radar_avail_prev_frame = false;
		} else {
			Radar_death_timer = TIMESTAMP::never();
			if (Radar_static_next.isNever())
				Radar_static_next = TIMESTAMP::immediate();
		}

		if (timestamp_elapsed(Radar_death_timer)) {
			ok_to_blit_radar = 0;
		}
	}

	setGaugeColor(HUD_C_NONE, config);
	blitGauge(config);
	drawRange(config);

	if ( timestamp_elapsed(Radar_static_next) ) {
		Radar_static_playing = !Radar_static_playing;
		Radar_static_next = _timestamp_rand(50, 750);
	}

	if (!config) {
		// if the emp effect is active, always draw the radar wackily
		if (emp_active_local()) {
			Radar_static_playing = true;
		}

		if (ok_to_blit_radar) {
			if (Radar_static_playing) {
				drawBlipsSorted(1); // passing 1 means to draw distorted
				if (!Radar_static_looping.isValid()) {
					Radar_static_looping = snd_play_looping(gamesnd_get_game_sound(GameSounds::STATIC));
				}
			} else {
				drawBlipsSorted(0);
				if (Radar_static_looping.isValid()) {
					snd_stop(Radar_static_looping);
					Radar_static_looping = sound_handle::invalid();
				}
			}
		} else {
			if (Radar_static_looping.isValid()) {
				snd_stop(Radar_static_looping);
				Radar_static_looping = sound_handle::invalid();
			}
		}
	}

	if(g3_yourself)
		g3_end_frame();
}
void HudGaugeRadarStd::pageIn()
{
	bm_page_in_aabitmap( Radar_gauge.first_frame, Radar_gauge.num_frames );
}

void HudGaugeRadarStd::clampBlip(vec3d* b)
{
	float max_radius;
	float hypotenuse;

	max_radius = i2fl(Radar_radius[0] - 5);

	// Scale blip to radar size
	vm_vec_scale(b, i2fl(Radar_radius[0])/2.0f);

	hypotenuse = hypotf(b->xyz.x, b->xyz.y);

	// Clamp to inside of plot area, if needed
	if (hypotenuse > max_radius) {
		vm_vec_scale2(b, max_radius, hypotenuse);
	}

	// Scale Y to respect aspect ratio
	b->xyz.y *= (i2fl(Radar_radius[1]) / i2fl(Radar_radius[0]));
}

void HudGaugeRadarStd::plotBlip(blip *b, int *x, int *y)
{
	float zdist, rscale;
	vec3d *pos = &b->position;

	if (b->dist < pos->xyz.z) {
		rscale = 0.0f;
	} else {
		rscale = (float) acosf_safe(pos->xyz.z / b->dist) / PI;
	}

	zdist = hypotf(pos->xyz.x, pos->xyz.y);

	vec3d new_pos = {};

	if (zdist >= 0.01f)
	{
		new_pos = *pos;
		new_pos.xyz.z = 0.0f;
		vm_vec_scale(&new_pos, rscale / zdist);		// Values are within +/- 1.0f
		clampBlip(&new_pos);
	}

	*x = fl2i(position[0] + Radar_center_offsets[0] + new_pos.xyz.x);
	*y = fl2i(position[1] + Radar_center_offsets[1] - new_pos.xyz.y);
}

void HudGaugeRadarStd::drawCrosshairs(int x, int y)
{
	int i,j,m;

	gr_set_color_fast(&radar_crosshairs);

	for(i = 0; i < 2; i++) {
		m = (i * 2) - 1;
		renderGradientLine(x + m*4,y,x + m*8,y);
	}
	for(j = 0; j < 2; j++) {
		m = (j * 2) - 1;
		renderGradientLine(x,y + m*4,x,y + m*8);
	}
}
