using System;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Serialization;

namespace RoR2;

[CreateAssetMenu(menuName = "RoR2/SpawnCards/CharacterSpawnCard")]
public class CharacterSpawnCard : SpawnCard, MasterSummon.IInventorySetupCallback
{
	public bool noElites;

	public bool forbiddenAsBoss;

	[CanBeNull]
	[Tooltip("The loadout for any summoned character to use.")]
	[FormerlySerializedAs("loadout")]
	[SerializeField]
	private SerializableLoadout _loadout;

	[CanBeNull]
	[Tooltip("The inventory from which to initially paste into any summoned character's inventory. Will skip certain non-bequeathable items. This is usually not a good idea to set up in the editor, and is more reserved for runtime.")]
	public Inventory inventoryToCopy;

	[CanBeNull]
	public Func<ItemIndex, bool> inventoryItemCopyFilter;

	[Tooltip("The set of equipment to grant to any summoned character, after inventory copy.")]
	[NotNull]
	public EquipmentDef[] equipmentToGrant = Array.Empty<EquipmentDef>();

	[Tooltip("The set of items to grant to any summoned character, after inventory copy.")]
	[NotNull]
	public ItemCountPair[] itemsToGrant = Array.Empty<ItemCountPair>();

	[CanBeNull]
	protected Loadout runtimeLoadout;

	private static ResourceAvailability loadoutAvailability;

	[CanBeNull]
	public SerializableLoadout loadout
	{
		get
		{
			return _loadout;
		}
		set
		{
			if (_loadout != value)
			{
				_loadout = value;
				if (loadoutAvailability.available)
				{
					SetLoadoutFromSerializedLoadout();
				}
				else
				{
					loadoutAvailability.CallWhenAvailable(SetLoadoutFromSerializedLoadout);
				}
			}
		}
	}

	public void Awake()
	{
		if (!loadoutAvailability.available)
		{
			loadoutAvailability.CallWhenAvailable(SetLoadoutFromSerializedLoadout);
		}
		else
		{
			SetLoadoutFromSerializedLoadout();
		}
	}

	private void SetLoadoutFromSerializedLoadout()
	{
		SetLoadout(loadout);
	}

	private void SetLoadout([CanBeNull] SerializableLoadout serializableLoadout)
	{
		if (Application.isPlaying)
		{
			runtimeLoadout = ((serializableLoadout != null && !serializableLoadout.isEmpty) ? new Loadout() : null);
			if (runtimeLoadout != null)
			{
				serializableLoadout.Apply(runtimeLoadout);
			}
		}
	}

	[CanBeNull]
	protected virtual Loadout GetRuntimeLoadout()
	{
		return runtimeLoadout;
	}

	[CanBeNull]
	protected virtual Action<CharacterMaster> GetPreSpawnSetupCallback()
	{
		return null;
	}

	protected override void Spawn(Vector3 position, Quaternion rotation, DirectorSpawnRequest directorSpawnRequest, ref SpawnResult result)
	{
		MasterSummon masterSummon = new MasterSummon
		{
			masterPrefab = prefab,
			position = position,
			rotation = rotation,
			summonerBodyObject = directorSpawnRequest.summonerBodyObject,
			teamIndexOverride = directorSpawnRequest.teamIndexOverride,
			ignoreTeamMemberLimit = directorSpawnRequest.ignoreTeamMemberLimit,
			loadout = GetRuntimeLoadout(),
			inventoryToCopy = inventoryToCopy,
			inventoryItemCopyFilter = inventoryItemCopyFilter,
			inventorySetupCallback = this,
			preSpawnSetupCallback = GetPreSpawnSetupCallback(),
			useAmbientLevel = true
		};
		result.spawnedInstance = masterSummon.Perform()?.gameObject;
		result.success = result.spawnedInstance;
	}

	[SystemInitializer(new Type[] { typeof(Loadout) })]
	private static void Init()
	{
		loadoutAvailability.MakeAvailable();
	}

	void MasterSummon.IInventorySetupCallback.SetupSummonedInventory(MasterSummon masterSummon, Inventory summonedInventory)
	{
		SetupSummonedInventory(masterSummon, summonedInventory);
	}

	protected virtual void SetupSummonedInventory(MasterSummon masterSummon, Inventory summonedInventory)
	{
		for (int i = 0; i < equipmentToGrant.Length; i++)
		{
			EquipmentState equipment = summonedInventory.GetEquipment((uint)i);
			summonedInventory.SetEquipment(new EquipmentState(equipmentToGrant[i]?.equipmentIndex ?? EquipmentIndex.None, equipment.chargeFinishTime, equipment.charges), (uint)i);
		}
		for (int j = 0; j < itemsToGrant.Length; j++)
		{
			ItemCountPair itemCountPair = itemsToGrant[j];
			summonedInventory.GiveItem(itemCountPair.itemDef, itemCountPair.count);
		}
	}

	public void OnValidate()
	{
		if (occupyPosition)
		{
			Debug.LogErrorFormat("{0} OccupyPosition=1.This is only intended for limited spawns, and will prevent spawning on this node in the future! Are ya sure? ", this);
		}
	}
}
