enum eDebugMode
{
    NORMAL,
	MUZZLE_FIRE,
	CAMERA_MUZZLE_HYBRID,
	COUNT
};



class WeaponDebug
{
	const int BUFFER_SIZE = 1000;
	const float COLLISIONS_DISTANCE_TOLERANCE = 0.01;
	const float MAX_MUZZLE_DISTANCE_TOLERANCE = 20;
	const float CAMERA_BULLET_ORIGIN_OFFSET = 1;
	const float CAMERA_TRACE_MIN_DISTANCE_TOLERANCE = 0.3;
	
	Weapon m_WeaponInHands;
	int m_BufferIndex;
	bool m_IsDrawKeyHeldDown;
	bool m_IsLMBPressed;
	bool m_IsToggleKeyPressed;
	bool m_IsFireKeyPressed;
	float m_TargetDistance;
	eDebugMode m_CurrentMode;
	vector m_AimTrailCyclic[BUFFER_SIZE];	
	vector m_AimTrailOrdered[BUFFER_SIZE];
	
	ref map<int, string> m_DebugModesNames = new map<int, string>;
	
	//ref array<Selection> m_Selections = new array<Selection>();
	
	Shape m_Shape_usti;
	Shape m_Shape_konec;
	Shape m_ShapeFireDirection1;
	Shape m_ShapeFireDirection2;
	Shape m_HitShape;
	Shape m_ShapeEye;
	Shape m_ShapeTrailLines;
	Shape m_ShapeFireDirCamera;
	Shape m_HitShape2;
	Shape m_HitShape3;
	Shape m_HitShape4;
	//Shape temp_shape;
	Shape m_PermanentLine1;
	Shape m_PermanentLine2;
	
	Shape m_PermanentShape1;
	Shape m_PermanentShape2;
	
	Weapon GetWeaponInHands()
	{
		Weapon weapon_in_hands;
		PlayerBase player = PlayerBase.Cast(GetGame().GetPlayer());
		if( player && player.GetItemInHands() ) Class.CastTo(weapon_in_hands,  player.GetItemInHands());
		
		return weapon_in_hands;
	}
	
	void WeaponDebug()
	{
		m_DebugModesNames.Insert(eDebugMode.NORMAL, "Mode "+eDebugMode.NORMAL.ToString()+": shoot along the weapon barrel");
		m_DebugModesNames.Insert(eDebugMode.MUZZLE_FIRE,"Mode "+eDebugMode.MUZZLE_FIRE.ToString()+": shoot from muzzle end to camera trace hit point");
		m_DebugModesNames.Insert(eDebugMode.CAMERA_MUZZLE_HYBRID, "Mode "+eDebugMode.CAMERA_MUZZLE_HYBRID.ToString()+": shoot either from camera lens or from muzzle \n depending on the situation");
	}
	
	void ~WeaponDebug()
	{
		RemoveAllShapes(true);
		
	}

	void RemoveAllShapes(bool is_exit = false)
	{
		Debug.RemoveShape(m_Shape_usti);
		Debug.RemoveShape(m_Shape_konec);
		Debug.RemoveShape(m_ShapeFireDirection1);
		Debug.RemoveShape(m_ShapeFireDirection2);
		Debug.RemoveShape(m_ShapeEye);
		Debug.RemoveShape(m_ShapeTrailLines);
		Debug.RemoveShape(m_ShapeFireDirCamera);
		Debug.RemoveShape(m_HitShape);
		Debug.RemoveShape(m_HitShape2);
		Debug.RemoveShape(m_HitShape3);
		Debug.RemoveShape(m_HitShape4);
		
		if ( is_exit )
		{
			Debug.RemoveShape(m_PermanentLine1);
			Debug.RemoveShape(m_PermanentLine2);
			Debug.RemoveShape(m_PermanentShape1);
			Debug.RemoveShape(m_PermanentShape2);
		}
	}


	void OnCommandHandlerUpdate()
	{
			
		m_IsLMBPressed = false;
		m_IsFireKeyPressed = false;
		
		if (KeyState(KeyCode.KC_LWIN) == 1)
		{
			if (!m_IsDrawKeyHeldDown)
			{
				OnKeyDown(KeyCode.KC_LWIN);
			}
			m_IsDrawKeyHeldDown = true;
		}
		else
		{
			if ( m_IsDrawKeyHeldDown )
			{
				//OnKeyUp();
			}
			m_IsDrawKeyHeldDown = false;
		}
		
		if (KeyState(KeyCode.KC_Z) == 1)
		{
			ClearKey(KeyCode.KC_Z);
			CycleDebugMode();
		}
		
		if (KeyState(KeyCode.KC_X) == 1)
		{
			m_IsFireKeyPressed = true;
			ClearKey(KeyCode.KC_X);
		}

		if (GetMouseState(MouseState.LEFT) & MB_PRESSED_MASK)
		{
			m_IsLMBPressed = true;
		}
	}

	void OnKeyDown(KeyCode key)
	{
		if (key == KeyCode.KC_X)
		{
		}
	}
	
	void CycleDebugMode()
	{
		m_CurrentMode++;
		if ( m_CurrentMode == eDebugMode.COUNT )
		{
			m_CurrentMode = 0;
		}
	
	}
	
	void OnPostFrameUpdate()
	{
		if ( GetWeaponInHands() )
		{
			RemoveAllShapes();
			Weapon weapon = GetWeaponInHands();
			vector cameraDirection = GetGame().GetCurrentCameraDirection();
			vector cameraPosition = GetGame().GetCurrentCameraPosition();
			vector usti_hlavne_position = weapon.GetSelectionPositionMS( "usti hlavne" );//usti hlavne
			vector konec_hlavne_position = weapon.GetSelectionPositionMS( "konec hlavne" );//konec hlavne
			usti_hlavne_position = weapon.ModelToWorld(usti_hlavne_position);
			konec_hlavne_position = weapon.ModelToWorld(konec_hlavne_position);
			
			if ( m_CurrentMode == eDebugMode.NORMAL ) 
			{
				DrawLineOfFire(konec_hlavne_position,usti_hlavne_position );
			}
			
			if ( m_CurrentMode == eDebugMode.MUZZLE_FIRE ) 
			{
				DrawLineOfFireMuzzleToHit(usti_hlavne_position, cameraDirection, cameraPosition);
			}
			
			if ( m_CurrentMode == eDebugMode.CAMERA_MUZZLE_HYBRID ) 
			{
				DrawLineOfFireCameraHybrid(usti_hlavne_position, cameraDirection, cameraPosition, konec_hlavne_position);
			}
			
			if (m_IsDrawKeyHeldDown)
			{
				AddPosToCyclicBuffer(usti_hlavne_position);
				OrderTrailArray();
			}

			m_ShapeTrailLines = Debug.DrawLines( m_AimTrailOrdered,BUFFER_SIZE, COLOR_YELLOW, ShapeFlags.NOZBUFFER );
			
		
			DrawEyePoint(weapon);
			DrawBarrelMemoryPoints(konec_hlavne_position,usti_hlavne_position );
			DisplayGeneralInfo();
			DisplayTargetInfo();
			/*
			vector pos;
			weapon.GetProjectedCursorPos3d(pos);
			Debug.RemoveShape(temp_shape);
			temp_shape = Debug.DrawSphere(pos, 0.1, Colors.GREEN);
			*/
		}
	}

	void DrawBarrelMemoryPoints(vector begin_point, vector end_point)
	{
		if (!m_IsDrawKeyHeldDown)
		{
			m_Shape_usti = Debug.DrawSphere(end_point, 0.011, Colors.GREEN, ShapeFlags.TRANSP|ShapeFlags.NOOUTLINE|ShapeFlags.NOZBUFFER);
			m_Shape_konec = Debug.DrawSphere(begin_point, 0.011, Colors.GREEN, ShapeFlags.TRANSP|ShapeFlags.NOOUTLINE|ShapeFlags.NOZBUFFER);
		}
	}


	void DrawLineOfFire(vector begin_point, vector end_point)
	{
		vector contact_point;
		vector contact_dir;
		vector aim_point = end_point - begin_point;
		int contact_component;
		
		aim_point = aim_point.Normalized() * 100;
		aim_point = aim_point + end_point;
		
		m_ShapeFireDirection1 = Debug.DrawLine(end_point, aim_point);
		m_ShapeFireDirection2 = Debug.DrawLine(begin_point, end_point, ShapeFlags.NOZBUFFER );
		
		if ( DayZPhysics.RaycastRV(end_point, aim_point, contact_point, contact_dir, contact_component, null, null, null, false, false, ObjIntersectFire) )
		{	
			m_HitShape = Debug.DrawSphere(contact_point, 0.04, COLOR_RED);
		}
		
		if ( m_IsFireKeyPressed )
		{
			Debug.RemoveShape(m_PermanentLine1);
			m_PermanentLine1 = Debug.DrawLine(end_point, contact_point, Colors.RED, ShapeFlags.NOZBUFFER );
		}
	}

	void AddPosToCyclicBuffer(vector pos)
	{
		m_AimTrailCyclic[m_BufferIndex] = pos;
		m_BufferIndex++;
		if (m_BufferIndex == BUFFER_SIZE)
		{
			m_BufferIndex = 0;			
		}
	}	
	
	void OrderTrailArray()
	{
		int unordered_index;
		
		for (int i = 0; i < BUFFER_SIZE; i++)
		{
			unordered_index = m_BufferIndex + i;
			if ( unordered_index >= BUFFER_SIZE )
			{
				unordered_index = unordered_index - BUFFER_SIZE;
			}
			m_AimTrailOrdered[i] = m_AimTrailCyclic[unordered_index];
		}
	}	
	
	void DrawEyePoint(Weapon weapon)
	{
		vector position = GetEyePointPosition(weapon);
		m_ShapeEye = Debug.DrawSphere(position, 0.009, COLOR_BLUE, ShapeFlags.TRANSP|ShapeFlags.NOOUTLINE|ShapeFlags.NOZBUFFER);
	}

	vector GetEyePointPosition(Weapon weapon)
	{
		string memory_point_name = weapon.ConfigGetString("memoryPointCamera");
		ItemBase optics = weapon.GetAttachedOptics();
		if (optics)
		{
			memory_point_name = optics.ConfigGetString("memoryPointCamera");
			return optics.ModelToWorld(optics.GetSelectionPositionLS( memory_point_name ));
		}
		return weapon.ModelToWorld(weapon.GetSelectionPositionLS( memory_point_name ));
		
	}

	void DisplayGeneralInfo()
	{
		DbgUI.Begin("sway weight", 50, 50);
		DayZPlayerImplement player = DayZPlayerImplement.Cast(GetGame().GetPlayer());
		float sway_weight = player.GetAimingModel().GetSwayWeight();
		DbgUI.Text("value: " + sway_weight.ToString());
		DbgUI.Text("Hold LWIN to draw debug line");
		DbgUI.Text("Press X to simulate fire");
		DbgUI.Text("Press Z to cycle debug modes");
		DbgUI.Text("Debug " +m_DebugModesNames.Get(m_CurrentMode) );
		DbgUI.Text("");
		DbgUI.End();
	}

	void DisplayTargetInfo()
	{
		DbgUI.Begin("target distance", 50,200);
		DbgUI.Text("value: " + m_TargetDistance.ToString());
		DbgUI.End();
	}

		
	void DrawLineOfFireMuzzleToHit(vector begin_point, vector camera_dir, vector camera_pos)
	{
		vector contact_point_cam_trace;
		vector contact_point_muzzle_trace;
		
		vector contact_dir_muzzle;
		vector contact_dir_camera;
		
		vector contact_point;
		vector contact_dir;
		
		int contact_component;
		int contact_component_muzzle;
		
		vector end_point = camera_pos + camera_dir * 1000;
		Man player = GetGame().GetPlayer();
		Object player_o;
		Class.CastTo(player_o, player);
		
		if ( DayZPhysics.RaycastRV(camera_pos, end_point, contact_point_cam_trace, contact_dir_camera, contact_component,null, null, player_o , false, false, ObjIntersectFire, 0.1) )
		{
			m_ShapeFireDirCamera = Debug.DrawLine(begin_point, contact_point_cam_trace, Colors.RED, ShapeFlags.NOZBUFFER );
			m_HitShape2 = Debug.DrawSphere(contact_point_cam_trace, 0.03, Colors.GREEN);
			m_TargetDistance =  vector.Distance( player.GetPosition(), contact_point_cam_trace);
			
			if ( m_IsFireKeyPressed )
			{
				Debug.RemoveShape(m_PermanentLine1);
				Debug.RemoveShape(m_PermanentLine2);
				m_PermanentLine1 = Debug.DrawLine(begin_point, contact_point_cam_trace, Colors.RED, ShapeFlags.NOZBUFFER );
				m_PermanentLine2 = Debug.DrawLine(camera_pos, contact_point_cam_trace, Colors.GREEN, ShapeFlags.NOZBUFFER );
			}
			
		}
		
		else
		{
			m_ShapeFireDirCamera = Debug.DrawLine(begin_point, end_point, Colors.GREEN, ShapeFlags.NOZBUFFER );
			m_TargetDistance = -1;
		}
		
		if ( DayZPhysics.RaycastRV(begin_point, contact_point_cam_trace, contact_point_muzzle_trace, contact_dir_muzzle, contact_component_muzzle, null, null, null, false, false, ObjIntersectFire, 0.0) )
		{
			m_HitShape3 = Debug.DrawSphere(contact_point_muzzle_trace, 0.03, COLOR_RED);
		}
	}

	void DrawLineOfFireCameraHybrid(vector usti_hlavne_position, vector camera_dir, vector camera_pos, vector konec_hlavne_position)
	{
		bool muzzle_shot;
		vector contact_point_cam_trace;
		vector contact_point_muzzle_trace;
		
		vector aim_at_position;
		
		vector contact_dir_muzzle;
		vector contact_dir;
		
		vector contact_point;
		
		int contact_component;
		int contact_component_muzzle;
		
		float distance_to_aim_at;
		vector end_point = camera_pos + camera_dir * 100;
		vector start_point = camera_pos + (CAMERA_BULLET_ORIGIN_OFFSET * camera_dir);
		vector weapon_aim_direction = usti_hlavne_position - konec_hlavne_position;
		weapon_aim_direction.Normalize();
		
		Man player = GetGame().GetPlayer();
		Object player_o;
		Class.CastTo(player_o, player);

		if ( DayZPhysics.RaycastRV(start_point, end_point, contact_point_cam_trace, contact_dir, contact_component,null, null, player_o , false, false, ObjIntersectFire) )
		{
			m_TargetDistance = vector.Distance(start_point, contact_point_cam_trace);
			aim_at_position = contact_point_cam_trace;
			
			if ( DayZPhysics.RaycastRV(usti_hlavne_position, contact_point_cam_trace, contact_point_muzzle_trace, contact_dir, contact_component,null, null, player_o , false, false, ObjIntersectFire, 0.05) )
			{
				float collision_distance = vector.Distance(contact_point_cam_trace, contact_point_muzzle_trace);
				float muzzle_collision_distance = vector.Distance(usti_hlavne_position, contact_point_muzzle_trace);
				
				if ((collision_distance > 2 && muzzle_collision_distance < MAX_MUZZLE_DISTANCE_TOLERANCE) || m_TargetDistance < CAMERA_TRACE_MIN_DISTANCE_TOLERANCE)
				{
					muzzle_shot = true;
					aim_at_position = contact_point_muzzle_trace;
				}
			}
		}
		
		
		
		distance_to_aim_at = vector.Distance(camera_pos, aim_at_position);
		
		if ( m_IsFireKeyPressed )
		{
			Debug.RemoveShape(m_PermanentLine1);
			Debug.RemoveShape(m_PermanentLine2);
			Debug.RemoveShape(m_PermanentShape1);
			
			vector contact_point_temp;
			
			if (muzzle_shot)
			{
				m_PermanentLine1 = Debug.DrawLine(usti_hlavne_position, usti_hlavne_position + weapon_aim_direction * 5, Colors.RED, ShapeFlags.NOZBUFFER );
				m_PermanentLine2 = Debug.DrawLine(camera_pos, contact_point_cam_trace, Colors.GREEN, ShapeFlags.NOZBUFFER );
				PrintString("muzle shot");
			}
			else
			{
				vector dir = contact_point_cam_trace - camera_pos;
				dir.Normalize();
				dir = dir * 100;
				float dst = vector.Distance(camera_pos, contact_point_cam_trace);
				float dist2 = vector.Distance(camera_pos, contact_point_cam_trace);
				
				//m_PermanentShape1 = Debug.DrawSphere(start_point, 0.015,Colors.GREEN);
				m_PermanentLine1 = Debug.DrawLine(start_point, contact_point_cam_trace + dir, Colors.RED, ShapeFlags.NOZBUFFER );
				PrintString("camera shot");
			}
		}
		
		float clamped_distance = Math.Clamp(distance_to_aim_at, 0 , 100);
		float distance_normalized = Math.InverseLerp(0, 100, clamped_distance);
		float hit_sphere_size = Math.Lerp(0.025, 0.75, distance_normalized);
		
		Debug.RemoveShape(m_HitShape4);
		Debug.RemoveShape(m_HitShape);
		m_HitShape = Debug.DrawSphere(contact_point_cam_trace, 0.20,Colors.GREEN, ShapeFlags.TRANSP);
		m_HitShape4 = Debug.DrawSphere(aim_at_position, hit_sphere_size);
	
	}
	
}