package emu.grasscutter.game.entity;

import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.GadgetBornTypeOuterClass.GadgetBornType;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;

public class EntityItem extends EntityBaseGadget {
	private final Position pos;
	private final Position rot;
	
	private final GameItem item;
	private final long guid;

	private final boolean share;

	public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) {
		super(scene);
		this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
		this.pos = new Position(pos);
		this.rot = new Position();
		this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
		this.item = new GameItem(itemData, count);
		this.share = true;
	}

	// In official game, some drop items are shared to all players, and some other items are independent to all players
	// For example, if you killed a monster in MP mode, all players could get drops but rarity and number of them are different
	// but if you broke regional mine, when someone picked up the drop then it disappeared
	public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) {
		super(scene);
		this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
		this.pos = new Position(pos);
		this.rot = new Position();
		this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
		this.item = new GameItem(itemData, count);
		this.share = share;
	}
	
	@Override
	public int getId() {
		return this.id;
	}
	
	private GameItem getItem() {
		return this.item;
	}

	public ItemData getItemData() {
		return this.getItem().getItemData();
	}

	public long getGuid() {
		return guid;
	}

	public int getCount() {
		return this.getItem().getCount();
	}
	
	@Override
	public int getGadgetId() {
		return this.getItemData().getGadgetId();
	}

	@Override
	public Position getPosition() {
		return this.pos;
	}

	@Override
	public Position getRotation() {
		return this.rot;
	}
	
	@Override
	public Int2FloatOpenHashMap getFightProperties() {
		return null;
	}

	public boolean isShare() {
		return share;
	}
	
	@Override
	public void onInteract(Player player, GadgetInteractReq interactReq) {
	    // check drop owner to avoid someone picked up item in others' world
        if (!this.isShare()) {
            int dropOwner = (int) (this.getGuid() >> 32);
            if (dropOwner != player.getUid()) {
                return;
            }
        }
        
        this.getScene().removeEntity(this);
        GameItem item = new GameItem(this.getItemData(), this.getCount());
        
        // Add to inventory
        boolean success = player.getInventory().addItem(item, ActionReason.SubfieldDrop);
        if (success) {
            if (!this.isShare()) { // not shared drop
                player.sendPacket(new PacketGadgetInteractRsp(this, InteractType.INTERACT_TYPE_PICK_ITEM));
            } else {
                this.getScene().broadcastPacket(new PacketGadgetInteractRsp(this, InteractType.INTERACT_TYPE_PICK_ITEM));
            }
        }
    }

	@Override
	public SceneEntityInfo toProto() {
		EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
				.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
				.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
				.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
				.setBornPos(Vector.newBuilder())
				.build();
		
		SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
				.setEntityId(getId())
				.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
				.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
				.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
				.setEntityClientData(EntityClientData.newBuilder())
				.setEntityAuthorityInfo(authority)
				.setLifeState(1);
		
		PropPair pair = PropPair.newBuilder()
				.setType(PlayerProperty.PROP_LEVEL.getId())
				.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
				.build();
		entityInfo.addPropList(pair);
		
		SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
				.setGadgetId(this.getItemData().getGadgetId())
				.setTrifleItem(this.getItem().toProto())
				.setBornType(GadgetBornType.GADGET_BORN_TYPE_IN_AIR)
				.setAuthorityPeerId(this.getWorld().getHostPeerId())
				.setIsEnableInteract(true);

		entityInfo.setGadget(gadgetInfo);
		
		return entityInfo.build();
	}
}
