package emu.grasscutter.game.entity;

import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.*;
import io.grasscutter.net.proto.define.AbilitySyncStateInfo;
import io.grasscutter.net.proto.define.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.ClientGadgetInfoOuterClass;
import io.grasscutter.net.proto.define.EntityAuthorityInfo;
import io.grasscutter.net.proto.define.EntityClientData;
import io.grasscutter.net.proto.define.EntityRendererChangedInfo;
import io.grasscutter.net.proto.fight.EvtCreateGadgetNotify;
import io.grasscutter.net.proto.define.MotionInfo;
import io.grasscutter.net.proto.define.PropPair;
import io.grasscutter.net.proto.define.ProtEntityType;
import io.grasscutter.net.proto.define.SceneEntityAiInfo;
import io.grasscutter.net.proto.define.SceneEntityInfo;
import io.grasscutter.net.proto.define.SceneGadgetInfo;
import io.grasscutter.net.proto.define.Vector;
import emu.grasscutter.utils.helpers.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;

public class EntityClientGadget extends EntityBaseGadget {
    @Getter private final Player owner;

    @Getter(onMethod_ = @Override)
    private int gadgetId;

    @Getter private int ownerEntityId;
    @Getter private int targetEntityId;
    @Getter private boolean asyncLoad;

    @Getter private int originalOwnerEntityId;

    @Getter private final GadgetData gadgetData;
    private ConfigEntityGadget configGadget;

    public EntityClientGadget(Scene scene, Player player, EvtCreateGadgetNotify notify) {
        super(
                scene,
                new Position(notify.getInitPos()),
                new Position(notify.getInitEulerAngles()),
                notify.getCampId(),
                notify.getCampType());
        this.owner = player;
        this.id = notify.getEntityId();
        this.gadgetId = notify.getConfigId();
        this.ownerEntityId = notify.getPropOwnerEntityId();
        this.targetEntityId = notify.getTargetEntityId();
        this.asyncLoad = notify.getIsAsyncLoad();

        this.gadgetData = GameData.getGadgetDataMap().get(gadgetId);
        if (gadgetData != null && gadgetData.getJsonName() != null) {
            this.configGadget = GameData.getGadgetConfigData().get(gadgetData.getJsonName());
        }

        GameEntity owner = scene.getEntityById(this.ownerEntityId);
        if (owner instanceof EntityClientGadget ownerGadget) {
            this.originalOwnerEntityId = ownerGadget.getOriginalOwnerEntityId();
        } else {
            this.originalOwnerEntityId = this.ownerEntityId;
        }

        this.initAbilities();
    }

    @Override
    public void initAbilities() {
        if (this.configGadget != null && this.configGadget.getAbilities() != null) {
            for (var ability : this.configGadget.getAbilities()) {
                addConfigAbility(ability);
            }
        }
    }

    private void addConfigAbility(ConfigAbilityData abilityData) {
        var data = GameData.getAbilityData(abilityData.getAbilityName());
        if (data != null) owner.getAbilityManager().addAbilityToEntity(this, data);
    }

    @Override
    public void onDeath(int killerId) {
        super.onDeath(killerId); // Invoke super class's onDeath() method.
    }

    @Override
    public Int2FloatMap getFightProperties() {
        return null;
    }

    @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);

        ClientGadgetInfoOuterClass.ClientGadgetInfo clientGadget =
                ClientGadgetInfoOuterClass.ClientGadgetInfo.newBuilder()
                        .setCampId(this.getCampId())
                        .setCampType(this.getCampType())
                        .setOwnerEntityId(this.getOwnerEntityId())
                        .setTargetEntityId(this.getTargetEntityId())
                        .setAsyncLoad(this.isAsyncLoad())
                        .build();

        SceneGadgetInfo.Builder gadgetInfo =
                SceneGadgetInfo.newBuilder()
                        .setGadgetId(this.getGadgetId())
                        .setOwnerEntityId(this.getOwnerEntityId())
                        .setIsEnableInteract(true)
                        .setClientGadget(clientGadget)
                        .setPropOwnerEntityId(this.getOwnerEntityId())
                        .setAuthorityPeerId(this.getOwner().getPeerId());

        entityInfo.setGadget(gadgetInfo);

        return entityInfo.build();
    }
}
