
// detach magazine composite state
class WeaponDetachingMag_1 extends WeaponStartAction
{ };

class WeaponDetachingMag_Store extends WeaponStateBase
{
	Magazine m_magazine; /// magazine that will be detached
	ref InventoryLocation m_dst;

	override void OnEntry (WeaponEventBase e)
	{
		//if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(m_weapon) + "  WeaponDetachingMag_Store, Detaching mag=" + m_magazine.ToString() +  "to loc=" + InventoryLocation.DumpToStringNullSafe(m_dst)); }
		super.OnEntry(e);
		if (e)
		{
			if (!m_magazine || !m_dst)
			{
				Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store, error - no magazine to load from (m_magazine=NULL)");
			}
		}
	}

	override void OnAbort (WeaponEventBase e)
	{
		m_magazine = NULL;
		m_dst = NULL;

		super.OnAbort(e);
	}

	override void OnExit (WeaponEventBase e)
	{
		InventoryLocation il = new InventoryLocation;
		if (m_magazine.GetInventory().GetCurrentInventoryLocation(il))
		{
			if (GameInventory.LocationSyncMoveEntity(il, m_dst))
			{
				m_weapon.HideMagazine();
				if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store, ok - magazine removed from inv (inv->dst)"); }
			}
			else
			{
				// @TODO: drop on gnd
				Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store, error - cannot store detached magazine!");
			}
		}
		else
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store, error - cannot get curr location");
		}

		m_magazine = NULL;
		m_dst = NULL;
		super.OnExit(e);
	}

	override bool SaveCurrentFSMState (ParamsWriteContext ctx)
	{
		if (!super.SaveCurrentFSMState(ctx))
			return false;

		if (!ctx.Write(m_magazine))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store.SaveCurrentFSMState: cannot write m_magazine for weapon=" + m_weapon);
			return false;
		}

		if (!OptionalLocationWriteToContext(m_dst, ctx))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store.SaveCurrentFSMState: cannot write m_st for weapon=" + m_weapon);
			return false;
		}
		return true;
	}

	override bool LoadCurrentFSMState (ParamsReadContext ctx, int version)
	{
		if (!super.LoadCurrentFSMState(ctx, version))
			return false;

		if (!ctx.Read(m_magazine))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store.LoadCurrentFSMState: cannot read m_magazine for weapon=" + m_weapon);
			return false;
		}
		if (!OptionalLocationReadFromContext(m_dst, ctx))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store.LoadCurrentFSMState: cannot read m_dst for weapon=" + m_weapon);
			return false;
		}
		return true;
	}
};

class WeaponDetachingMag_StoreRemoveChamberBullet extends WeaponStateBase
{
	Magazine m_magazine; /// magazine that will be detached
	ref InventoryLocation m_dst;
	bool m_bulletEjected;

	override void OnEntry (WeaponEventBase e)
	{
		//if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(m_weapon) + "  WeaponDetachingMag_Store, Detaching mag=" + m_magazine.ToString() +  "to loc=" + InventoryLocation.DumpToStringNullSafe(m_dst)); }
		super.OnEntry(e);
		m_bulletEjected = false;
		if (e)
		{
			if (!m_magazine || !m_dst)
			{
				Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store, error - no magazine to load from (m_magazine=NULL)");
			}
			else
			{
				int mi = m_weapon.GetCurrentMuzzle();
				if(!m_weapon.IsChamberFiredOut(mi))
				{
					if(!m_weapon.IsChamberEmpty(mi))
					{
						m_bulletEjected = true;
						DayZPlayer p = e.m_player;
						ejectBulletAndStoreInMagazine(m_weapon,mi,m_magazine,p);
						m_weapon.EffectBulletHide(mi);
						m_weapon.HideBullet(mi);
					}
				}
			}
		}
	}

	override void OnAbort (WeaponEventBase e)
	{
		if(m_bulletEjected)
		{
			int mi = m_weapon.GetCurrentMuzzle();
			pushToChamberFromAttachedMagazine(m_weapon,mi);
			
			m_weapon.EffectBulletHide(0);
			m_weapon.HideBullet(0);
		}
		
		m_magazine = NULL;
		m_dst = NULL;

		super.OnAbort(e);
	}

	override void OnExit (WeaponEventBase e)
	{
		InventoryLocation il = new InventoryLocation;
		if (m_magazine.GetInventory().GetCurrentInventoryLocation(il))
		{
			if (GameInventory.LocationSyncMoveEntity(il, m_dst))
			{
				m_weapon.HideMagazine();
				if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store, ok - magazine removed from inv (inv->dst)"); }
			}
			else
			{
				// @TODO: drop on gnd
				Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store, error - cannot store detached magazine!");
			}
		}
		else
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store, error - cannot get curr location");
		}

		m_magazine = NULL;
		m_dst = NULL;
		super.OnExit(e);
	}

	override bool SaveCurrentFSMState (ParamsWriteContext ctx)
	{
		if (!super.SaveCurrentFSMState(ctx))
			return false;

		if (!ctx.Write(m_magazine))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store.SaveCurrentFSMState: cannot write m_magazine for weapon=" + m_weapon);
			return false;
		}

		if (!OptionalLocationWriteToContext(m_dst, ctx))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store.SaveCurrentFSMState: cannot write m_st for weapon=" + m_weapon);
			return false;
		}
		return true;
	}

	override bool LoadCurrentFSMState (ParamsReadContext ctx, int version)
	{
		if (!super.LoadCurrentFSMState(ctx, version))
			return false;

		if (!ctx.Read(m_magazine))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store.LoadCurrentFSMState: cannot read m_magazine for weapon=" + m_weapon);
			return false;
		}
		if (!OptionalLocationReadFromContext(m_dst, ctx))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag_Store.LoadCurrentFSMState: cannot read m_dst for weapon=" + m_weapon);
			return false;
		}
		return true;
	}
};

/*class WeaponDetachingMag_Store_W4T extends WeaponDetachingMag_Store
{
	override bool IsWaitingForActionFinish () { return true; }
};*/

class WeaponDetachingMag extends WeaponStateBase
{
	WeaponActions m_action;
	int m_actionType;
	Magazine m_magazine; /// detached magazine
	ref InventoryLocation m_dst; /// detached magazine destination

	ref WeaponStateBase m_start;
	ref WeaponDetachingMag_Store m_store;
	ref MagazineHide_W4T m_hideM;

	void WeaponDetachingMag (Weapon_Base w = NULL, WeaponStateBase parent = NULL, WeaponActions action = WeaponActions.NONE, int actionType = -1)
	{
		m_action = action;
		m_actionType = actionType;

		// setup nested state machine
		m_start = new WeaponDetachingMag_1(m_weapon, this, m_action, m_actionType);
		m_store = new WeaponDetachingMag_Store(m_weapon, this);
		m_hideM = new MagazineHide_W4T(m_weapon, this);
		// events
		WeaponEventBase _fin_ = new WeaponEventHumanCommandActionFinished;
		WeaponEventBase __md_ = new WeaponEventAnimMagazineDetached;
		WeaponEventBase __mh_ = new WeaponEventAnimMagazineHide;

		m_fsm = new WeaponFSM(this); // @NOTE: set owner of the submachine fsm
		m_fsm.AddTransition(new WeaponTransition(m_start, __md_, m_store));
		m_fsm.AddTransition(new WeaponTransition(m_store, __mh_, m_hideM));
		m_fsm.AddTransition(new WeaponTransition(m_hideM, _fin_, NULL));
		
		// Safety exits
		m_fsm.AddTransition(new WeaponTransition(m_store  , _fin_, null));
		m_fsm.AddTransition(new WeaponTransition(m_start  , _fin_, null));	

		m_fsm.SetInitialState(m_start);
	}

	override void OnEntry (WeaponEventBase e)
	{
		if (e != NULL)
		{
			WeaponEventDetachMagazine de;
			if (Class.CastTo(de, e))
			{
				if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("WeaponDetachingMag type=" + typename.EnumToString(InventoryLocationType, de.m_dst.GetType())); }
				m_magazine = e.m_magazine;
				m_dst = de.m_dst;
				
				m_store.m_magazine = m_magazine;
				m_store.m_dst = m_dst;
				if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("WeaponDetachingMag type=" + typename.EnumToString(InventoryLocationType, m_store.m_dst.GetType())); }
			}
		}
		super.OnEntry(e); // @NOTE: super at the end (prevent override from submachine start)
	}

	override void OnExit (WeaponEventBase e)
	{
		m_dst = NULL;
		m_magazine = NULL;
		super.OnExit(e);
	}

	override bool SaveCurrentFSMState (ParamsWriteContext ctx)
	{
		if (!super.SaveCurrentFSMState(ctx))
			return false;

		if (!ctx.Write(m_magazine))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag.SaveCurrentFSMState: cannot write m_magazine for weapon=" + m_weapon);
			return false;
		}

		if (!OptionalLocationWriteToContext(m_dst, ctx))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag.SaveCurrentFSMState: cannot write m_st for weapon=" + m_weapon);
			return false;
		}
		return true;
	}

	override bool LoadCurrentFSMState (ParamsReadContext ctx, int version)
	{
		if (!super.LoadCurrentFSMState(ctx, version))
			return false;

		if (!ctx.Read(m_magazine))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag.LoadCurrentFSMState: cannot read m_magazine for weapon=" + m_weapon);
			return false;
		}
		if (!OptionalLocationReadFromContext(m_dst, ctx))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMag.LoadCurrentFSMState: cannot read m_dst for weapon=" + m_weapon);
			return false;
		}
		return true;
	}
};
	
class WeaponDetachingMagOpenBolt extends WeaponStateBase
{
	WeaponActions m_action;
	int m_actionType;
	Magazine m_magazine; /// detached magazine
	ref InventoryLocation m_dst; /// detached magazine destination

	ref WeaponStateBase m_start;
	ref WeaponDetachingMag_StoreRemoveChamberBullet m_store;
	ref MagazineHide_W4T m_hideM;

	void WeaponDetachingMagOpenBolt (Weapon_Base w = NULL, WeaponStateBase parent = NULL, WeaponActions action = WeaponActions.NONE, int actionType = -1)
	{
		m_action = action;
		m_actionType = actionType;

		// setup nested state machine
		m_start = new WeaponDetachingMag_1(m_weapon, this, m_action, m_actionType);
		m_store = new WeaponDetachingMag_StoreRemoveChamberBullet(m_weapon, this);
		m_hideM = new MagazineHide_W4T(m_weapon, this);
		// events
		WeaponEventBase _fin_ = new WeaponEventHumanCommandActionFinished;
		WeaponEventBase __md_ = new WeaponEventAnimMagazineDetached;
		WeaponEventBase __mh_ = new WeaponEventAnimMagazineHide;

		m_fsm = new WeaponFSM(this); // @NOTE: set owner of the submachine fsm
		m_fsm.AddTransition(new WeaponTransition(m_start, __md_, m_store));
		m_fsm.AddTransition(new WeaponTransition(m_store, __mh_, m_hideM));
		m_fsm.AddTransition(new WeaponTransition(m_hideM, _fin_, NULL));
		
		// Safety exits
		m_fsm.AddTransition(new WeaponTransition(m_store  , _fin_, null));
		m_fsm.AddTransition(new WeaponTransition(m_start  , _fin_, null));	

		m_fsm.SetInitialState(m_start);
	}

	override void OnEntry (WeaponEventBase e)
	{
		if (e != NULL)
		{
			WeaponEventDetachMagazine de;
			if (Class.CastTo(de, e))
			{
				if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("WeaponDetachingMagOpenBolt type=" + typename.EnumToString(InventoryLocationType, de.m_dst.GetType())); }
				m_magazine = e.m_magazine;
				m_dst = de.m_dst;
				
				m_store.m_magazine = m_magazine;
				m_store.m_dst = m_dst;
				if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("WeaponDetachingMagOpenBolt type=" + typename.EnumToString(InventoryLocationType, m_store.m_dst.GetType())); }
			}
		}
		super.OnEntry(e); // @NOTE: super at the end (prevent override from submachine start)
	}

	override void OnExit (WeaponEventBase e)
	{
		m_dst = NULL;
		m_magazine = NULL;
		super.OnExit(e);
	}

	override bool SaveCurrentFSMState (ParamsWriteContext ctx)
	{
		if (!super.SaveCurrentFSMState(ctx))
			return false;

		if (!ctx.Write(m_magazine))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMagOpenBolt.SaveCurrentFSMState: cannot write m_magazine for weapon=" + m_weapon);
			return false;
		}

		if (!OptionalLocationWriteToContext(m_dst, ctx))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMagOpenBolt.SaveCurrentFSMState: cannot write m_st for weapon=" + m_weapon);
			return false;
		}
		return true;
	}

	override bool LoadCurrentFSMState (ParamsReadContext ctx, int version)
	{
		if (!super.LoadCurrentFSMState(ctx, version))
			return false;

		if (!ctx.Read(m_magazine))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMagOpenBolt.LoadCurrentFSMState: cannot read m_magazine for weapon=" + m_weapon);
			return false;
		}
		if (!OptionalLocationReadFromContext(m_dst, ctx))
		{
			Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponDetachingMagOpenBolt.LoadCurrentFSMState: cannot read m_dst for weapon=" + m_weapon);
			return false;
		}
		return true;
	}
};

