const float MAGNUM_ROTATION_POSITION_M1 = -0.167;
const float MAGNUM_ROTATION_POSITION_0 = 0.0;
const float MAGNUM_ROTATION_POSITION_1 = 0.167;
const float MAGNUM_ROTATION_POSITION_2 = 0.334;
const float MAGNUM_ROTATION_POSITION_3 = 0.500;
const float MAGNUM_ROTATION_POSITION_4 = 0.668;
const float MAGNUM_ROTATION_POSITION_5 = 0.835;
const float MAGNUM_ROTATION_POSITION_6 = 1.0;


enum MagnumAnimState
{
	DEFAULT 			= 0, 	///< default weapon state
};

enum MagnumStableStateID
{
	UNKNOWN				=  0,
	DEFAULT				=  1,
}

class Magnum_Static_State extends WeaponStableState
{
	bool init = false;
	override void OnEntry(WeaponEventBase e) 
	{		
		if (LogManager.IsWeaponLogEnable()) { wpnPrint("[wpnstate] { Magnum stable state"); } 
		super.OnEntry(e);
		if (init)
		{
			Magnum_Base magnum;
			if (CastTo(magnum, m_weapon))
			{
				magnum.SyncCylinderRotation();
				//magnum.SyncSelectionStateFromState();
			}
		}
		init = true;
	}
	override void OnExit (WeaponEventBase e) { super.OnExit(e); if (LogManager.IsWeaponLogEnable()) { wpnPrint("[wpnstate] }  Magnum stable state"); } }
	override int GetCurrentStateID () { return MagnumStableStateID.DEFAULT; }
	override bool HasBullet() { return m_weapon.IsChamberFull(m_weapon.GetCurrentMuzzle()); }
	override bool HasMagazine() { return false; }
	override bool IsJammed() { return m_weapon.IsJammed(); }
	override bool IsSingleState() { return true; }
};



class Magnum_Base extends Weapon_Base
{
	ref WeaponStableState C;
	int m_LastMuzzleloaded;
	int m_ActiveMuzzle;
	const string ATT_SLOT_CYLINDER = "RevolverCylinder";
	const string ATT_SLOT_EJECTOR = "RevolverEjector";
	
	override void SetActions()
	{
		AddAction(FirearmActionLoadMultiBulletQuick);
		super.SetActions();
		AddAction(FirearmActionLoadMultiBullet);
	}
	
	override RecoilBase SpawnRecoilObject()
	{
		return new MagnumRecoil(this);
	}
	
	void Magnum_Base ()
	{
		m_LastMuzzleloaded = 0;
		m_ActiveMuzzle = 0;
		
		/*m_abilities.Insert(new AbilityRecord(WeaponActions.CHAMBERING, WeaponActionTypes.CHAMBERING_STARTLOOPABLE_CLOSED));
		m_abilities.Insert(new AbilityRecord(WeaponActions.CHAMBERING, WeaponActionTypes.CHAMBERING_ENDLOOPABLE));
		m_abilities.Insert(new AbilityRecord(WeaponActions.MECHANISM, WeaponActionTypes.MECHANISM_CLOSED));
		m_abilities.Insert(new AbilityRecord(WeaponActions.MECHANISM, WeaponActionTypes.MECHANISM_SPECIAL));
		m_abilities.Insert(new AbilityRecord(WeaponActions.UNJAMMING, WeaponActionTypes.UNJAMMING_START));
		m_abilities.Insert(new AbilityRecord(WeaponActions.UNJAMMING, WeaponActionTypes.UNJAMMING_END));
		m_abilities.Insert(new AbilityRecord(WeaponActions.FIRE, WeaponActionTypes.FIRE_NORMAL));
		m_abilities.Insert(new AbilityRecord(WeaponActions.FIRE, WeaponActionTypes.FIRE_COCKED));*/
	}
	
	override void InitStateMachine ()
	{
		m_abilities.Insert(new AbilityRecord(WeaponActions.CHAMBERING, WeaponActionChamberingTypes.CHAMBERING_STARTLOOPABLE_CLOSED));
		m_abilities.Insert(new AbilityRecord(WeaponActions.CHAMBERING, WeaponActionChamberingTypes.CHAMBERING_STARTLOOPABLE_CLOSED_KEEP));
		m_abilities.Insert(new AbilityRecord(WeaponActions.CHAMBERING, WeaponActionChamberingTypes.CHAMBERING_ENDLOOPABLE));
		m_abilities.Insert(new AbilityRecord(WeaponActions.MECHANISM, WeaponActionMechanismTypes.MECHANISM_CLOSED));
		m_abilities.Insert(new AbilityRecord(WeaponActions.MECHANISM, WeaponActionMechanismTypes.MECHANISM_SPECIAL));
		m_abilities.Insert(new AbilityRecord(WeaponActions.FIRE, WeaponActionFireTypes.FIRE_NORMAL));
		m_abilities.Insert(new AbilityRecord(WeaponActions.FIRE, WeaponActionFireTypes.FIRE_COCKED));
		m_abilities.Insert(new AbilityRecord(WeaponActions.FIRE, WeaponActionFireTypes.FIRE_UNCOCKED));


		// setup state machine
		// basic weapon state
		C = new Magnum_Static_State(this, NULL, MagnumAnimState.DEFAULT);


		WeaponMagnumChambering Chamber = new WeaponMagnumChambering(this, NULL, WeaponActions.CHAMBERING, WeaponActionChamberingTypes.CHAMBERING_STARTLOOPABLE_CLOSED_KEEP,WeaponActionChamberingTypes.CHAMBERING_ENDLOOPABLE);
		WeaponMagnumChambering Chamber_E = new WeaponMagnumChambering(this, NULL, WeaponActions.CHAMBERING, WeaponActionChamberingTypes.CHAMBERING_STARTLOOPABLE_CLOSED,WeaponActionChamberingTypes.CHAMBERING_ENDLOOPABLE);
		//WeaponMagnumChambering Chamber_CC00 = new WeaponMagnumChambering(this, NULL, WeaponActions.CHAMBERING, WeaponActionChamberingTypes.CHAMBERING_END);

		WeaponCharging Mech = new WeaponCharging(this, NULL, WeaponActions.MECHANISM, WeaponActionMechanismTypes.MECHANISM_CLOSED);
		
		WeaponStateBase	Trigger_normal = new WeaponFireMagnum(this, NULL, WeaponActions.FIRE, WeaponActionFireTypes.FIRE_NORMAL);
		WeaponStateBase	Trigger_dry = new WeaponFireMagnum(this, NULL, WeaponActions.FIRE, WeaponActionFireTypes.FIRE_COCKED);
		
		// events

		WeaponEventBase __L__ = new WeaponEventLoad1Bullet;
		WeaponEventBase _fin_ = new WeaponEventHumanCommandActionFinished;
		WeaponEventBase _abt_ = new WeaponEventHumanCommandActionAborted;
		WeaponEventBase __M__ = new WeaponEventMechanism;
		WeaponEventBase __T__ = new WeaponEventTrigger;
		

		WeaponEventBase _rto_ = new WeaponEventReloadTimeout;
		WeaponEventBase _dto_ = new WeaponEventDryFireTimeout;

	
		m_fsm = new WeaponFSM();



		m_fsm.AddTransition(new WeaponTransition( C,				__L__,	Chamber, null, new WeaponGuardAnyChamberFiredOut(this))); // chamber from closed charged
		m_fsm.AddTransition(new WeaponTransition(  Chamber,	_fin_,	C));
		m_fsm.AddTransition(new WeaponTransition(  Chamber,	_abt_,	C));

		
		m_fsm.AddTransition(new WeaponTransition( C,				__L__,	Chamber_E)); // chamber from closed charged
		m_fsm.AddTransition(new WeaponTransition(  Chamber_E,	_fin_,	C));
		m_fsm.AddTransition(new WeaponTransition(  Chamber_E,	_abt_,	C));

		
		m_fsm.AddTransition(new WeaponTransition( C,				__M__,	Mech)); // charge from closed
		m_fsm.AddTransition(new WeaponTransition(  Mech,		_fin_,	C));
		m_fsm.AddTransition(new WeaponTransition(  Mech,		_abt_,	C));

		
		
		m_fsm.AddTransition(new WeaponTransition( C,				__T__,	Trigger_normal, null, new GuardAnd (new WeaponGuardCurrentChamberFull(this), new GuardNot(new WeaponGuardCurrentChamberFiredOut(this))) )); // fire.cocked
		m_fsm.AddTransition(new WeaponTransition(  Trigger_normal,	_fin_,	C));
		m_fsm.AddTransition(new WeaponTransition(  Trigger_normal, 	_dto_,	C));
		m_fsm.AddTransition(new WeaponTransition(  Trigger_normal, 	_abt_,	C));
		
		m_fsm.AddTransition(new WeaponTransition( C,				__T__,	Trigger_dry)); // fire.cocked
		m_fsm.AddTransition(new WeaponTransition(  Trigger_dry,	_fin_,	C));
		m_fsm.AddTransition(new WeaponTransition(  Trigger_dry, 	_dto_,	C));
		m_fsm.AddTransition(new WeaponTransition(  Trigger_dry, 	_abt_,	C));


		SetInitialState(C);
		SelectionBulletHide();
		SetCurrentMuzzle(0);
		//HideMagazine();
		m_fsm.Start();
	}
	
	override bool CanChamberBullet(int muzzleIndex, Magazine mag)
	{
		if ( CanChamberFromMag(muzzleIndex, mag) )
		{
			if (IsChamberEmpty(muzzleIndex))
				return true;
				
			if (IsChamberFiredOut(muzzleIndex))
				return true;
		}
		return false;
	}
	
	override void AssembleGun()
	{
		super.AssembleGun();
		
		if ( !FindAttachmentBySlotName(ATT_SLOT_EJECTOR) )
		{
			GetInventory().CreateAttachment("Magnum_Ejector");
		}
		
		if ( !FindAttachmentBySlotName(ATT_SLOT_CYLINDER) )
		{
			GetInventory().CreateAttachment("Magnum_Cylinder");
		}

		ForceSyncSelectionState();
		SyncCylinderRotation();
	}
	
	static float GetCylinderRotation(int muzzleIndex)
	{
		switch (muzzleIndex)
		{
			case 0:
				return MAGNUM_ROTATION_POSITION_0;
			case 1:
				return MAGNUM_ROTATION_POSITION_5;
			case 2:
				return MAGNUM_ROTATION_POSITION_4;
			case 3:
				return MAGNUM_ROTATION_POSITION_3;
			case 4:
				return MAGNUM_ROTATION_POSITION_2;
			case 5:
				return MAGNUM_ROTATION_POSITION_1;
		}
		
		ErrorEx(string.Format("Invalid muzzle index: %1", muzzleIndex));
		
		return MAGNUM_ROTATION_POSITION_0;
	}
	
	void SetCylinderRotationAnimationPhase(float rot, bool reset = false)
	{
		Magnum_Cylinder cylinder = Magnum_Cylinder.Cast(GetAttachmentByType(Magnum_Cylinder));
		Magnum_Ejector ejector = Magnum_Ejector.Cast(GetAttachmentByType(Magnum_Ejector));
		if (cylinder && ejector)
		{
			float anim_phase = cylinder.GetAnimationPhase("Rotate_Cylinder");
			if ( Math.AbsFloat(anim_phase - rot) > 0.1 )
			{
				if (reset)
				{
					cylinder.ResetAnimationPhase("Rotate_Cylinder", rot );
					ejector.ResetAnimationPhase("Rotate_Ejector", rot );				
				}				
				else if (rot == MAGNUM_ROTATION_POSITION_0)
				{
					cylinder.ResetAnimationPhase("Rotate_Cylinder", MAGNUM_ROTATION_POSITION_M1 );
					ejector.ResetAnimationPhase("Rotate_Ejector", MAGNUM_ROTATION_POSITION_M1 );
				}
				
				cylinder.SetAnimationPhase("Rotate_Cylinder", rot );
				ejector.SetAnimationPhase("Rotate_Ejector", rot );
			}
		}
	}
	
	void SyncCylinderRotation(bool reset = true)
	{
		SetCylinderRotationAnimationPhase(GetCylinderRotation(GetCurrentMuzzle()), reset);
	}
	
	override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
	{
		super.EEHealthLevelChanged(oldLevel,newLevel,zone);
		
		if (GetGame().IsClient())
			return;
		
		SetAttachmentsHealth();
	}
	
	//sets health of cylinder and ejector objects
	void SetAttachmentsHealth()
	{
		EntityAI entity;
		ItemBase attachment;
		for (int i = 0; i < GetInventory().AttachmentCount(); i++)
		{
			entity = GetInventory().GetAttachmentFromIndex(i);
			if ( Class.CastTo(attachment,entity) )
			{
				attachment.SetHealth01("","Health",GetHealth01());
			}
		}
	}
	
	override void OnFire(int muzzle_index)
	{
		super.OnFire(muzzle_index);
		Magnum_Cylinder cylinder = Magnum_Cylinder.Cast(GetAttachmentByType(Magnum_Cylinder));
		if (cylinder)
		{
			string bullet_nose = "bullet_nose";
			if (muzzle_index > 0)
				bullet_nose = string.Format("bullet_nose_" + (muzzle_index + 1));
			cylinder.HideSelection(bullet_nose);
		}
	}
	
	override bool IsShowingChamberedBullet()
	{
		return false;
	}
	
	override bool CanEjectBullet() 
	{
		for (int i = 0; i < GetMuzzleCount(); i++)
		{
			if (IsChamberFull(i))
				return true;
		}
		return false;
	}
	
	override void ShowBullet(int muzzleIndex)
	{
		super.ShowBullet(muzzleIndex);
		
		Magnum_Cylinder cylinder = Magnum_Cylinder.Cast(GetAttachmentByType(Magnum_Cylinder));
		if (cylinder)
		{		
			string bullet = "bullet";
			if (muzzleIndex > 0)
				bullet = string.Format("bullet_" + (muzzleIndex + 1));
			
			cylinder.ShowSelection(bullet);
			
			if (!IsChamberFiredOut(muzzleIndex))
			{
				string bullet_nose = "bullet_nose";
				if (muzzleIndex > 0)
					bullet_nose = string.Format("bullet_nose_" + (muzzleIndex + 1));
				cylinder.ShowSelection(bullet_nose);
			}
		}
	}
	
	override void HideBullet(int muzzleIndex)
	{
		super.HideBullet(muzzleIndex);
		
		Magnum_Cylinder cylinder = Magnum_Cylinder.Cast(GetAttachmentByType(Magnum_Cylinder));
		if (cylinder)
		{		
			string bullet = "bullet";
			if (muzzleIndex > 0)
				bullet = string.Format("bullet_" + (muzzleIndex + 1));
			
			cylinder.HideSelection(bullet);
			
			string bullet_nose = "bullet_nose";
			if (muzzleIndex > 0)
				bullet_nose = string.Format("bullet_nose_" + (muzzleIndex + 1));
			cylinder.HideSelection(bullet_nose);
		}
	}
	
	//Debug menu Spawn Ground Special
	override void OnDebugSpawn()
	{
		super.OnDebugSpawn();
		
		EntityAI entity;
		if ( Class.CastTo(entity, this) )
		{
			entity.SpawnEntityOnGroundPos("Ammo_357", entity.GetPosition());
		}
	}
};

class Magnum extends Magnum_Base
{
	override void GetDebugActions(out TSelectableActionInfoArrayEx outputList)
	{
		outputList.Insert(new TSelectableActionInfoWithColor(SAT_DEBUG_ACTION, EActions.SEPARATOR, "", FadeColors.LIGHT_GREY));
		outputList.Insert(new TSelectableActionInfoWithColor(SAT_DEBUG_ACTION, EActions.SPIN, "Spin", FadeColors.LIGHT_GREY));
		outputList.Insert(new TSelectableActionInfoWithColor(SAT_DEBUG_ACTION, EActions.SEPARATOR, "___________________________", FadeColors.RED));

		super.GetDebugActions(outputList);
	}
	
	override bool OnAction(int action_id, Man player, ParamsReadContext ctx)
	{
		if (GetGame().IsServer())
		{
			if (action_id == EActions.SPIN)
			{
				const float animPhaseOffset = 0.167;
	
				Magnum_Cylinder cylinder = Magnum_Cylinder.Cast(GetAttachmentByType(Magnum_Cylinder));
				Magnum_Ejector ejector = Magnum_Ejector.Cast(GetAttachmentByType(Magnum_Ejector));
		
				if (cylinder)
				{
					float animPhase = cylinder.GetAnimationPhase("Rotate_Cylinder");
					if (animPhase + animPhaseOffset > 1.0)
					{
						animPhase -= 1.0;
						cylinder.ResetAnimationPhase("Rotate_Cylinder", animPhase);
						ejector.ResetAnimationPhase("Rotate_Ejector", animPhase);
					}
	
					cylinder.SetAnimationPhase("Rotate_Cylinder", animPhase);
					ejector.ResetAnimationPhase("Rotate_Ejector", animPhase);
				}
			}
		}
		
		return super.OnAction(action_id, player, ctx);
	}
}

class SawedoffMagnum extends Magnum_Base {};
class Magnum_Cylinder extends DummyItem {};
class Magnum_Ejector extends DummyItem {};