using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.Serialization;

namespace RoR2.Projectile;

[RequireComponent(typeof(ProjectileController))]
public class CleaverProjectile : NetworkBehaviour, IProjectileImpactBehavior
{
	private enum BoomerangState
	{
		FlyOut,
		Transition,
		FlyBack,
		Stopped
	}

	public float travelSpeed = 40f;

	public float charge;

	public float transitionDuration;

	private float maxFlyStopwatch;

	public GameObject impactSpark;

	public GameObject returnImpactSpark;

	public GameObject crosshairPrefab;

	public bool canHitCharacters;

	public bool canHitWorld;

	public bool returnOnHitWorldOrHover;

	public ProjectileController projectileController;

	[SyncVar]
	private BoomerangState boomerangState;

	private Transform ownerTransform;

	private ProjectileDamage projectileDamage;

	private Rigidbody rigidbody;

	private float stopwatch;

	private float fireAge;

	private float fireFrequency;

	[FormerlySerializedAs("distanceMultiplier")]
	public float travelTime = 2f;

	public UnityEvent onFlyBack;

	private bool setScale;

	private bool recallDamageFired;

	private float stoppedStopwatch = 3f;

	private ChefController chefController;

	private ushort predictionId;

	private bool recalled;

	private bool hasEffectiveAuthority;

	private bool disableOnRcall_ReelingCache;

	public Collider[] disableOnRecall;

	private static int kCmdCmdDestroyAndCleanup;

	public BoomerangState NetworkboomerangState
	{
		get
		{
			return boomerangState;
		}
		[param: In]
		set
		{
			ulong newValueAsUlong = (ulong)value;
			ulong fieldValueAsUlong = (ulong)boomerangState;
			SetSyncVarEnum(value, newValueAsUlong, ref boomerangState, fieldValueAsUlong, 1u);
		}
	}

	private void Awake()
	{
		rigidbody = GetComponent<Rigidbody>();
		projectileController = GetComponent<ProjectileController>();
		projectileDamage = GetComponent<ProjectileDamage>();
		maxFlyStopwatch = travelTime;
	}

	private void Start()
	{
		if ((bool)projectileController)
		{
			predictionId = projectileController.predictionId;
			if ((bool)projectileController.owner)
			{
				ownerTransform = projectileController.owner.transform;
				chefController = projectileController.owner.GetComponent<ChefController>();
				if ((bool)chefController)
				{
					chefController.AddLocalProjectileReference(predictionId, this);
					if (base.hasAuthority && !NetworkServer.active && chefController.TryRetrieveCachedProjectileFireInfo(out var fireProjectileInfo))
					{
						ApplyCachedFireProjectileInfo(fireProjectileInfo);
					}
				}
			}
		}
		hasEffectiveAuthority = Util.HasEffectiveAuthority(chefController.gameObject);
	}

	private void ApplyCachedFireProjectileInfo(FireProjectileInfo fireProjectileInfo)
	{
		ProjectileDamage component = GetComponent<ProjectileDamage>();
		TeamFilter component2 = GetComponent<TeamFilter>();
		ProjectileSimple component3 = GetComponent<ProjectileSimple>();
		if ((bool)component2)
		{
			component2.teamIndex = TeamComponent.GetObjectTeam(fireProjectileInfo.owner);
		}
		if (fireProjectileInfo.useSpeedOverride && (bool)component3)
		{
			component3.desiredForwardSpeed = fireProjectileInfo.speedOverride;
		}
		if ((bool)component)
		{
			component.damage = fireProjectileInfo.damage;
			component.force = fireProjectileInfo.force;
			component.crit = fireProjectileInfo.crit;
			component.damageColorIndex = fireProjectileInfo.damageColorIndex;
			if (fireProjectileInfo.damageTypeOverride.HasValue)
			{
				component.damageType = fireProjectileInfo.damageTypeOverride.Value;
			}
		}
		projectileController.DispatchOnInitialized();
	}

	private void OnEnable()
	{
		recalled = false;
		recallDamageFired = false;
	}

	public void OnProjectileImpact(ProjectileImpactInfo impactInfo)
	{
		if (!canHitWorld)
		{
			return;
		}
		if (impactInfo.collider.gameObject.layer == LayerIndex.world.intVal)
		{
			if (returnOnHitWorldOrHover)
			{
				NetworkboomerangState = BoomerangState.FlyBack;
			}
			else
			{
				NetworkboomerangState = BoomerangState.Stopped;
			}
		}
		if (boomerangState != BoomerangState.Stopped)
		{
			EffectManager.SimpleImpactEffect(recalled ? returnImpactSpark : impactSpark, impactInfo.estimatedPointOfImpact, -base.transform.forward, transmit: true);
		}
	}

	private bool Reel()
	{
		Vector3 vector = projectileController.owner.transform.position - base.transform.position;
		_ = vector.normalized;
		float magnitude = vector.magnitude;
		if (!chefController)
		{
			chefController = projectileController.owner.GetComponent<ChefController>();
		}
		return magnitude <= 2f;
	}

	public void FixedUpdate()
	{
		if (!hasEffectiveAuthority)
		{
			return;
		}
		if (!setScale)
		{
			setScale = true;
		}
		if (!projectileController.owner)
		{
			Object.Destroy(base.gameObject);
			return;
		}
		if (!chefController)
		{
			chefController = projectileController.owner.GetComponent<ChefController>();
		}
		recalled = recalled || chefController.recallCleaver || boomerangState == BoomerangState.FlyBack;
		if ((bool)chefController && recalled && boomerangState != BoomerangState.Transition)
		{
			NetworkboomerangState = BoomerangState.FlyBack;
		}
		if (returnOnHitWorldOrHover && boomerangState == BoomerangState.Stopped)
		{
			NetworkboomerangState = BoomerangState.FlyBack;
			recalled = true;
		}
		if (!recalled)
		{
			ToggleThrowOnlyObjects(isReeling: false);
			switch (boomerangState)
			{
			case BoomerangState.FlyOut:
				rigidbody.velocity = travelSpeed * base.transform.forward;
				stopwatch += Time.fixedDeltaTime;
				if (stopwatch >= maxFlyStopwatch)
				{
					stopwatch = 0f;
					NetworkboomerangState = BoomerangState.Stopped;
				}
				break;
			case BoomerangState.Transition:
			{
				stopwatch += Time.fixedDeltaTime;
				float num = stopwatch / transitionDuration;
				Vector3 vector = CalculatePullDirection();
				rigidbody.velocity = Vector3.Lerp(travelSpeed * base.transform.forward, travelSpeed * vector, num);
				if (num >= 0.1f)
				{
					NetworkboomerangState = BoomerangState.FlyBack;
					onFlyBack?.Invoke();
				}
				break;
			}
			case BoomerangState.Stopped:
				chefController = projectileController.owner.GetComponent<ChefController>();
				rigidbody.velocity = new Vector3(0f, 0f, 0f);
				stopwatch += Time.deltaTime;
				if (stopwatch > stoppedStopwatch)
				{
					CallCmdDestroyAndCleanup();
				}
				break;
			case BoomerangState.FlyBack:
				break;
			}
			return;
		}
		ToggleThrowOnlyObjects(isReeling: true);
		if (!recallDamageFired)
		{
			rigidbody.velocity = new Vector3(0f, 0f, 0f);
			GetComponent<ProjectileOverlapAttack>().ResetOverlapAttack();
			projectileDamage.damage *= 1.5f;
			recallDamageFired = true;
		}
		bool num2 = Reel();
		Vector3 vector2 = CalculatePullDirection();
		rigidbody.velocity = travelSpeed * vector2;
		if (num2)
		{
			if (chefController != null)
			{
				chefController.NetworkcatchDirtied = true;
			}
			CallCmdDestroyAndCleanup();
		}
	}

	private void ToggleThrowOnlyObjects(bool isReeling)
	{
		if (disableOnRcall_ReelingCache != isReeling)
		{
			int num = disableOnRecall.Length;
			for (int i = 0; i < num; i++)
			{
				disableOnRecall[i].enabled = !isReeling;
			}
			disableOnRcall_ReelingCache = isReeling;
		}
	}

	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	[Command]
	public void CmdDestroyAndCleanup()
	{
		Object.Destroy(base.gameObject);
	}

	private Vector3 CalculatePullDirection()
	{
		if ((bool)projectileController.owner)
		{
			return (projectileController.owner.transform.position - base.transform.position).normalized;
		}
		return base.transform.forward;
	}

	private void OnDestroy()
	{
		if ((bool)chefController)
		{
			chefController.RemoveLocalProjectileReference(predictionId);
		}
	}

	private void UNetVersion()
	{
	}

	protected static void InvokeCmdCmdDestroyAndCleanup(NetworkBehaviour obj, NetworkReader reader)
	{
		if (!NetworkServer.active)
		{
			Debug.LogError("Command CmdDestroyAndCleanup called on client.");
		}
		else
		{
			((CleaverProjectile)obj).CmdDestroyAndCleanup();
		}
	}

	public void CallCmdDestroyAndCleanup()
	{
		if (!NetworkClient.active)
		{
			Debug.LogError("Command function CmdDestroyAndCleanup called on server.");
			return;
		}
		if (base.isServer)
		{
			CmdDestroyAndCleanup();
			return;
		}
		NetworkWriter networkWriter = new NetworkWriter();
		networkWriter.Write((short)0);
		networkWriter.Write((short)5);
		networkWriter.WritePackedUInt32((uint)kCmdCmdDestroyAndCleanup);
		networkWriter.Write(GetComponent<NetworkIdentity>().netId);
		SendCommandInternal(networkWriter, 0, "CmdDestroyAndCleanup");
	}

	static CleaverProjectile()
	{
		kCmdCmdDestroyAndCleanup = 978502080;
		NetworkBehaviour.RegisterCommandDelegate(typeof(CleaverProjectile), kCmdCmdDestroyAndCleanup, InvokeCmdCmdDestroyAndCleanup);
		NetworkCRC.RegisterBehaviour("CleaverProjectile", 0);
	}

	public override bool OnSerialize(NetworkWriter writer, bool forceAll)
	{
		if (forceAll)
		{
			writer.Write((int)boomerangState);
			return true;
		}
		bool flag = false;
		if ((base.syncVarDirtyBits & (true ? 1u : 0u)) != 0)
		{
			if (!flag)
			{
				writer.WritePackedUInt32(base.syncVarDirtyBits);
				flag = true;
			}
			writer.Write((int)boomerangState);
		}
		if (!flag)
		{
			writer.WritePackedUInt32(base.syncVarDirtyBits);
		}
		return flag;
	}

	public override void OnDeserialize(NetworkReader reader, bool initialState)
	{
		if (initialState)
		{
			boomerangState = (BoomerangState)reader.ReadInt32();
			return;
		}
		int num = (int)reader.ReadPackedUInt32();
		if (((uint)num & (true ? 1u : 0u)) != 0)
		{
			boomerangState = (BoomerangState)reader.ReadInt32();
		}
	}

	public override void PreStartClient()
	{
	}
}
