using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using HG;
using RoR2.HudOverlay;
using RoR2.UI;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;

namespace RoR2;

[RequireComponent(typeof(InputBankTest))]
[RequireComponent(typeof(TeamComponent))]
[RequireComponent(typeof(CharacterBody))]
public class VoidSurvivorController : NetworkBehaviour, IOnTakeDamageServerReceiver, IOnDamageDealtServerReceiver
{
	[Header("Cached Components")]
	public CharacterBody characterBody;

	public Animator characterAnimator;

	public EntityStateMachine corruptionModeStateMachine;

	public EntityStateMachine bodyStateMachine;

	public EntityStateMachine weaponStateMachine;

	[Header("Corruption Values")]
	public float maxCorruption;

	public float minimumCorruptionPerVoidItem;

	public float corruptionPerSecondInCombat;

	public float corruptionPerSecondOutOfCombat;

	public float corruptionForFullDamage;

	public float corruptionForFullHeal;

	public float corruptionPerCrit;

	public float corruptionDeltaThresholdToAnimate;

	[Header("Corruption Mode")]
	public BuffDef corruptedBuffDef;

	public float corruptionFractionPerSecondWhileCorrupted;

	[SerializeField]
	[Header("UI")]
	public GameObject overlayPrefab;

	[SerializeField]
	public string overlayChildLocatorEntry;

	private ChildLocator overlayInstanceChildLocator;

	private Animator overlayInstanceAnimator;

	private OverlayController overlayController;

	private List<ImageFillController> fillUiList = new List<ImageFillController>();

	private TextMeshProUGUI uiCorruptionText;

	private int voidItemCount;

	[SyncVar(hook = "OnCorruptionModified")]
	private float _corruption;

	private static int isCorruptedParamHash = Animator.StringToHash("isCorrupted");

	private static int corruptionParamHash = Animator.StringToHash("corruption");

	public float corruption => _corruption;

	public float corruptionFraction => corruption / maxCorruption;

	public float corruptionPercentage => corruptionFraction * 100f;

	public float minimumCorruption => minimumCorruptionPerVoidItem * (float)voidItemCount;

	public bool isFullCorruption => corruption >= maxCorruption;

	public bool isCorrupted
	{
		get
		{
			if ((bool)characterBody)
			{
				return characterBody.HasBuff(corruptedBuffDef);
			}
			return false;
		}
	}

	public bool isPermanentlyCorrupted => minimumCorruption >= maxCorruption;

	private HealthComponent bodyHealthComponent => characterBody.healthComponent;

	public float Network_corruption
	{
		get
		{
			return _corruption;
		}
		[param: In]
		set
		{
			if (NetworkServer.localClientActive && !base.syncVarHookGuard)
			{
				base.syncVarHookGuard = true;
				OnCorruptionModified(value);
				base.syncVarHookGuard = false;
			}
			SetSyncVar(value, ref _corruption, 1u);
		}
	}

	private void OnEnable()
	{
		OverlayCreationParams overlayCreationParams = default(OverlayCreationParams);
		overlayCreationParams.prefab = overlayPrefab;
		overlayCreationParams.childLocatorEntry = overlayChildLocatorEntry;
		OverlayCreationParams overlayCreationParams2 = overlayCreationParams;
		overlayController = HudOverlayManager.AddOverlay(base.gameObject, overlayCreationParams2);
		overlayController.onInstanceAdded += OnOverlayInstanceAdded;
		overlayController.onInstanceRemove += OnOverlayInstanceRemoved;
		if ((bool)characterBody)
		{
			characterBody.onInventoryChanged += OnInventoryChanged;
			if (NetworkServer.active)
			{
				HealthComponent.onCharacterHealServer += OnCharacterHealServer;
			}
		}
	}

	private void OnDisable()
	{
		if (overlayController != null)
		{
			overlayController.onInstanceAdded -= OnOverlayInstanceAdded;
			overlayController.onInstanceRemove -= OnOverlayInstanceRemoved;
			fillUiList.Clear();
			HudOverlayManager.RemoveOverlay(overlayController);
		}
		if ((bool)characterBody)
		{
			characterBody.onInventoryChanged -= OnInventoryChanged;
			if (NetworkServer.active)
			{
				HealthComponent.onCharacterHealServer -= OnCharacterHealServer;
			}
		}
	}

	private void FixedUpdate()
	{
		float num = 0f;
		num = ((!characterBody.HasBuff(corruptedBuffDef)) ? (characterBody.outOfCombat ? corruptionPerSecondOutOfCombat : corruptionPerSecondInCombat) : (num + corruptionFractionPerSecondWhileCorrupted * (maxCorruption - minimumCorruption)));
		if (NetworkServer.active && !characterBody.HasBuff(RoR2Content.Buffs.HiddenInvincibility))
		{
			AddCorruption(num * Time.fixedDeltaTime);
		}
		UpdateUI();
		if ((bool)characterAnimator)
		{
			characterAnimator.SetFloat("corruptionFraction", corruptionFraction);
		}
	}

	private void UpdateUI()
	{
		foreach (ImageFillController fillUi in fillUiList)
		{
			fillUi.SetTValue(corruption / maxCorruption);
		}
		if ((bool)overlayInstanceChildLocator)
		{
			overlayInstanceChildLocator.FindChild("CorruptionThreshold").rotation = Quaternion.Euler(0f, 0f, Mathf.InverseLerp(0f, maxCorruption, corruption) * -360f);
			overlayInstanceChildLocator.FindChild("MinCorruptionThreshold").rotation = Quaternion.Euler(0f, 0f, Mathf.InverseLerp(0f, maxCorruption, minimumCorruption) * -360f);
		}
		if ((bool)overlayInstanceAnimator)
		{
			overlayInstanceAnimator.SetFloat(isCorruptedParamHash, corruption);
			overlayInstanceAnimator.SetBool(corruptionParamHash, isCorrupted);
		}
		if ((bool)uiCorruptionText)
		{
			StringBuilder stringBuilder = HG.StringBuilderPool.RentStringBuilder();
			stringBuilder.AppendInt(Mathf.FloorToInt(corruption), 1u, 3u).Append("%");
			uiCorruptionText.SetText(stringBuilder);
			HG.StringBuilderPool.ReturnStringBuilder(stringBuilder);
		}
	}

	private void OnOverlayInstanceAdded(OverlayController controller, GameObject instance)
	{
		fillUiList.Add(instance.GetComponent<ImageFillController>());
		uiCorruptionText = instance.GetComponentInChildren<TextMeshProUGUI>();
		overlayInstanceChildLocator = instance.GetComponent<ChildLocator>();
		overlayInstanceAnimator = instance.GetComponent<Animator>();
	}

	private void OnOverlayInstanceRemoved(OverlayController controller, GameObject instance)
	{
		fillUiList.Remove(instance.GetComponent<ImageFillController>());
	}

	private void OnCharacterHealServer(HealthComponent healthComponent, float amount, ProcChainMask procChainMask)
	{
		if ((object)healthComponent == bodyHealthComponent && !procChainMask.HasProc(ProcType.VoidSurvivorCrush))
		{
			float num = amount / bodyHealthComponent.fullCombinedHealth;
			AddCorruption(num * corruptionForFullHeal);
		}
	}

	public void OnDamageDealtServer(DamageReport damageReport)
	{
		if (damageReport.damageInfo.crit)
		{
			AddCorruption(damageReport.damageInfo.procCoefficient * corruptionPerCrit);
		}
	}

	public void OnTakeDamageServer(DamageReport damageReport)
	{
		float num = damageReport.damageDealt / bodyHealthComponent.fullCombinedHealth;
		if (!damageReport.damageInfo.procChainMask.HasProc(ProcType.VoidSurvivorCrush))
		{
			AddCorruption(num * corruptionForFullDamage);
		}
	}

	private void OnInventoryChanged()
	{
		voidItemCount = 0;
		Inventory inventory = characterBody.inventory;
		if ((bool)inventory)
		{
			voidItemCount = inventory.GetTotalItemCountOfTier(ItemTier.VoidTier1) + inventory.GetTotalItemCountOfTier(ItemTier.VoidTier2) + inventory.GetTotalItemCountOfTier(ItemTier.VoidTier3) + inventory.GetTotalItemCountOfTier(ItemTier.VoidBoss);
		}
	}

	[Server]
	public void AddCorruption(float amount)
	{
		if (!NetworkServer.active)
		{
			Debug.LogWarning("[Server] function 'System.Void RoR2.VoidSurvivorController::AddCorruption(System.Single)' called on client");
		}
		else
		{
			Network_corruption = Mathf.Clamp(corruption + amount, minimumCorruption, maxCorruption);
		}
	}

	private void OnCorruptionModified(float newCorruption)
	{
		if ((bool)overlayInstanceAnimator && Mathf.Abs(newCorruption - corruption) > corruptionDeltaThresholdToAnimate)
		{
			overlayInstanceAnimator.SetTrigger("corruptionIncreased");
		}
		Network_corruption = newCorruption;
	}

	private void UNetVersion()
	{
	}

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

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

	public override void PreStartClient()
	{
	}
}
