package emu.grasscutter.game.avatar;

import java.util.Iterator;
import java.util.List;

import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketAvatarChangeCostumeNotify;
import emu.grasscutter.server.packet.send.PacketAvatarFlycloakChangeNotify;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;

public class AvatarStorage extends BasePlayerManager implements Iterable<Avatar> {
	private final Int2ObjectMap<Avatar> avatars;
	private final Long2ObjectMap<Avatar> avatarsGuid;
	
	public AvatarStorage(Player player) {
		super(player);
		this.avatars = new Int2ObjectOpenHashMap<>();
		this.avatarsGuid = new Long2ObjectOpenHashMap<>();
	}

	public Int2ObjectMap<Avatar> getAvatars() {
		return avatars;
	}
	
	public int getAvatarCount() {
		return this.avatars.size();
	}
	
	public Avatar getAvatarById(int id) {
		return getAvatars().get(id);
	}
	
	public Avatar getAvatarByGuid(long id) {
		return avatarsGuid.get(id);
	}
	
	public boolean hasAvatar(int id) {
		return getAvatars().containsKey(id);
	}
	
	public boolean addAvatar(Avatar avatar) {
		if (avatar.getAvatarData() == null || this.hasAvatar(avatar.getAvatarId())) {
			return false;
		}
		
		// Set owner first
		avatar.setOwner(getPlayer());

		// Put into maps
		this.avatars.put(avatar.getAvatarId(), avatar);
		this.avatarsGuid.put(avatar.getGuid(), avatar);

		avatar.save();

		return true;
	}
	
	public void addStartingWeapon(Avatar avatar) {
		// Make sure avatar owner is this player
		if (avatar.getPlayer() != this.getPlayer()) {
			return;
		}
		
		// Create weapon
		GameItem weapon = new GameItem(avatar.getAvatarData().getInitialWeapon());
		
		if (weapon.getItemData() != null) {
			this.getPlayer().getInventory().addItem(weapon);
			
			avatar.equipItem(weapon, true);
		}
	}
	
	public boolean wearFlycloak(long avatarGuid, int flycloakId) {
		Avatar avatar = this.getAvatarByGuid(avatarGuid);

		if (avatar == null || !getPlayer().getFlyCloakList().contains(flycloakId)) {
			return false;
		}
		
		avatar.setFlyCloak(flycloakId);
		avatar.save();
		
		// Update
		getPlayer().sendPacket(new PacketAvatarFlycloakChangeNotify(avatar));

		return true;
	}
	
	public boolean changeCostume(long avatarGuid, int costumeId) {
		Avatar avatar = this.getAvatarByGuid(avatarGuid);

		if (avatar == null) {
			return false;
		}
		
		if (costumeId != 0 && !getPlayer().getCostumeList().contains(costumeId)) {
			return false;
		}
		
		// TODO make sure avatar can wear costume
		
		avatar.setCostume(costumeId);
		avatar.save();

		// Update entity
		EntityAvatar entity = avatar.getAsEntity();
		if (entity == null) {
			entity = new EntityAvatar(avatar);
			getPlayer().sendPacket(new PacketAvatarChangeCostumeNotify(entity));
		} else {
			getPlayer().getScene().broadcastPacket(new PacketAvatarChangeCostumeNotify(entity));
		}
		
		// Done
		return true;
	}
	
	public void loadFromDatabase() {
		List<Avatar> avatars = DatabaseHelper.getAvatars(getPlayer());
		
		for (Avatar avatar : avatars) {
			// Should never happen
			if (avatar.getObjectId() == null) {
				continue;
			}
			
			AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId());
			AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId());
			if (avatarData == null || skillDepot == null) {
				continue;
			}
			
			// Set ownerships
			avatar.setAvatarData(avatarData);
			avatar.setSkillDepot(skillDepot);
			avatar.setOwner(getPlayer());
			
			// Force recalc of const boosted skills
			avatar.recalcConstellations();
			
			// Add to avatar storage
			this.avatars.put(avatar.getAvatarId(), avatar);
			this.avatarsGuid.put(avatar.getGuid(), avatar);
		}
	}
	
	public void postLoad() {
		for (Avatar avatar : this) {
			// Weapon check
			if (avatar.getWeapon() == null) {
				this.addStartingWeapon(avatar);
			}
			// Recalc stats
			avatar.recalcStats();
		}
	}

	@Override
	public Iterator<Avatar> iterator() {
		return getAvatars().values().iterator();
	}
}
