// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      Magicbane Emulator Project © 2013 - 2022
//                www.magicbane.com


package engine.net.client.msg;

import engine.Enum.GameObjectType;
import engine.gameManager.BuildingManager;
import engine.gameManager.PowersManager;
import engine.net.AbstractConnection;
import engine.net.AbstractNetMsg;
import engine.net.ByteBufferReader;
import engine.net.ByteBufferWriter;
import engine.net.client.Protocol;
import engine.objects.Building;
import engine.objects.Item;
import engine.objects.MobLoot;
import engine.objects.NPC;
import engine.powers.EffectsBase;

import java.util.ArrayList;
import java.util.HashMap;

public class ItemProductionMsg extends ClientNetMsg {

    private static final int ACTION_PRODUCE = 1;
    private static final int ACTION_JUNK = 2;
    private static final int ACTION_RECYCLE = 3;
    private static final int ACTION_COMPLETE = 4;
    private static final int ACTION_DEPOSIT = 6;
    private static final int ACTION_SETPRICE = 5;
    private static final int ACTION_TAKE = 7;
    private static final int ACTION_CONFIRM_SETPRICE = 9;
    private static final int ACTION_CONFIRM_DEPOSIT = 10;
    private static final int ACTION_CONFIRM_TAKE = 11;     // Unsure. Sent by client
    private static final int ACTION_CONFIRM_PRODUCE = 8;

    private ArrayList<Long> ItemList;
    private int size;
    private int buildingUUID;
    private int unknown01;
    private int itemUUID;
    private int itemType;
    private int totalProduction;
    private int unknown03;
    private int pToken;
    private int sToken;
    private String name;
    private int actionType;
    private int npcUUID;
    private boolean add;
    private int itemPrice;
    private boolean isMultiple;
    private HashMap<Integer, Integer> itemIDtoTypeMap;

    /**
     * This is the general purpose constructor.
     */

    public ItemProductionMsg() {
        super(Protocol.ITEMPRODUCTION);
        this.actionType = 0;
        this.size = 0;
        this.buildingUUID = 0;
        this.unknown01 = 0;
        this.itemUUID = 0;
        this.totalProduction = 0;
        this.unknown03 = 0;
        this.pToken = 0;
        this.sToken = 0;
        this.name = "";
        this.itemPrice = 0;
        this.itemType = 0;

    }

    ;

    public ItemProductionMsg(Building building, NPC vendor, Item item, int actionType, boolean add) {
        super(Protocol.ITEMPRODUCTION);
        this.actionType = actionType;
        this.size = 0;
        this.buildingUUID = building.getObjectUUID();
        this.npcUUID = vendor.getObjectUUID();
        this.itemType = item.getObjectType().ordinal();
        this.itemUUID = item.getObjectUUID();
        this.unknown01 = 0;
        this.totalProduction = 0;
        this.unknown03 = 0;
        this.pToken = 0;
        this.sToken = 0;
        this.name = "";
        this.add = add;
        this.itemPrice = item.getValue();

    }

    /**
     * This constructor is used by NetMsgFactory. It attempts to deserialize the
     * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading
     * past the limit) then this constructor Throws that Exception to the
     * caller.
     */
    public ItemProductionMsg(AbstractConnection origin, ByteBufferReader reader) {
        super(Protocol.ITEMPRODUCTION, origin, reader);

    }

    /**
     * @see AbstractNetMsg#getPowerOfTwoBufferSize()
     */
    @Override
    protected int getPowerOfTwoBufferSize() {
        //Larger size for historically larger opcodes
        return (16); // 2^16 == 64k
    }

    /**
     * Serializes the subclass specific items to the supplied NetMsgWriter.
     */
    @Override
    protected void _serialize(ByteBufferWriter writer) {
        Building building = BuildingManager.getBuildingFromCache(this.buildingUUID);
        if (building == null)
            return;
        // Common Header

        writer.putInt(this.actionType);
        writer.putInt(GameObjectType.Building.ordinal());
        writer.putInt(this.buildingUUID);
        writer.putInt(GameObjectType.NPC.ordinal());
        writer.putInt(this.npcUUID);

        switch (this.actionType) {

            case ACTION_CONFIRM_DEPOSIT:
                writer.putInt(0); // Not item ordinal?
                writer.putInt(0); // Not item uuid?
                writer.putInt(1);
                writer.putInt(0);

                if (!add) {
                    writer.put((byte) 1);
                    Item item = Item.getFromCache(this.itemUUID);
                    if (item != null)
                        Item.serializeForClientMsgWithoutSlot(item, writer);
                    writer.putInt(building.getStrongboxValue());
                    writer.putInt(0);
                    writer.putInt(0);
                    writer.put((byte) 0);
                    break;
                }
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.put((byte) 1);
                Item item;
                if (this.itemType == GameObjectType.Item.ordinal())
                    item = Item.getFromCache(this.itemUUID);
                else
                    item = MobLoot.getFromCache(this.itemUUID);
                if (item != null)
                    Item.serializeForClientMsgWithoutSlot(item, writer);
                writer.putInt(building.getStrongboxValue());
                writer.putInt(0);
                writer.putInt(0);
                writer.put((byte) 0);
                break;
            case ACTION_CONFIRM_TAKE:
                writer.putInt(this.itemType);
                writer.putInt(this.itemUUID);
                writer.putInt(1);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.put((byte) 0);
                break;
            case ACTION_SETPRICE:
                writer.putInt(this.itemType);
                writer.putInt(this.itemUUID);
                writer.putInt(1);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(this.itemPrice); // new price
                writer.putInt(0);
                writer.putInt(0);
                writer.put((byte) 0);
                break;
            case ACTION_CONFIRM_SETPRICE:
                writer.putInt(this.itemType);
                writer.putInt(this.itemUUID);
                writer.putInt(1);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.put((byte) 1);
                writer.putInt(building.getStrongboxValue()); // new price
                writer.putInt(0);
                writer.putInt(0);
                //writer.put((byte) 0);
                break;
            case ACTION_DEPOSIT:
                writer.putInt(this.itemType);
                writer.putInt(this.itemUUID);
                writer.putInt(1);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.put((byte) 0);
                break;
            case ACTION_TAKE:
            case ACTION_RECYCLE:

                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(1);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.put((byte) 1);
                writer.putInt(building.getStrongboxValue());

                if (this.itemIDtoTypeMap != null) {
                    writer.putInt(this.itemIDtoTypeMap.size());

                    for (int itemID : this.itemIDtoTypeMap.keySet()) {
                        writer.putInt(this.itemIDtoTypeMap.get(itemID));
                        writer.putInt(itemID);
                    }

                } else
                    writer.putInt(0);

                writer.putInt(0);

                break;
            case ACTION_CONFIRM_PRODUCE:
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(1);
                MobLoot toRoll = MobLoot.getFromCache(this.itemUUID);
                writer.putInt(-1497023830);
                if (toRoll != null && toRoll.getPrefix() != null && !toRoll.getPrefix().isEmpty()) {
                    EffectsBase eb = PowersManager.getEffectByIDString(toRoll.getPrefix());
                    if (eb == null)
                        this.pToken = 0;
                    else
                        this.pToken = eb.getToken();
                }

                if (toRoll != null && toRoll.getSuffix() != null && !toRoll.getSuffix().isEmpty()) {
                    EffectsBase eb = PowersManager.getEffectByIDString(toRoll.getSuffix());
                    if (eb == null)
                        this.sToken = 0;
                    else
                        this.sToken = eb.getToken();
                }
                if (toRoll.isRandom() == false || (toRoll != null && toRoll.isComplete())) {
                    writer.putInt(this.pToken);
                    writer.putInt(this.sToken);
                } else {
                    writer.putInt(0);
                    writer.putInt(0);
                }

                writer.putString(toRoll.getCustomName());
                ;
                writer.putInt(GameObjectType.MobLoot.ordinal());
                writer.putInt(this.itemUUID);
                writer.putInt(0); //items left to produce?
                if (toRoll != null) {
                    writer.putInt(toRoll.getItemBaseID());
                    writer.putInt(toRoll.getValue());
                } else {
                    writer.putInt(0);
                    writer.putInt(0);
                }

                NPC vendor = NPC.getFromCache(this.npcUUID);
                if (vendor != null) {
                    if (toRoll.isComplete()) {
                        writer.putInt(0);
                        writer.putInt(0);
                    } else {
                        long timeLeft = toRoll.getDateToUpgrade() - System.currentTimeMillis();

                        timeLeft /= 1000;
                        writer.putInt((int) timeLeft);
                        writer.putInt(vendor.getRollingTimeInSeconds(toRoll.getItemBaseID()));
                    }

                } else {
                    writer.putInt(0);
                    writer.putInt(0);
                }
                if (toRoll.isComplete())
                    writer.putInt(0);
                else
                    writer.putInt(1);
                writer.put((byte) 0);

                if (toRoll != null && toRoll.isComplete())
                    writer.put((byte) 1);
                else
                    writer.put((byte) 0);
                writer.put((byte) 0);
                writer.put((byte) 1);
                writer.putInt(vendor.getBuilding().getStrongboxValue());

                writer.putInt(0);
                writer.putInt(0);
                //writer.putInt(0); //error popup

                break;
            case ACTION_COMPLETE:
                writer.putInt(this.itemType);
                writer.putInt(this.itemUUID);
                writer.putInt(this.totalProduction);
                writer.putInt(this.unknown03);
                writer.putInt(this.pToken);
                writer.putInt(this.sToken);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.put((byte) 0);
                break;
            case ACTION_JUNK:
                writer.putInt(this.itemType);
                writer.putInt(this.itemUUID);
                writer.putInt(this.totalProduction);
                writer.putInt(this.unknown03);
                writer.putInt(this.pToken);
                writer.putInt(this.sToken);
                writer.putString(this.name);
                writer.putInt(0);
                writer.putInt(0);
                writer.putShort((short) 0);
                writer.put((byte) 0);
                break;
            default:
                writer.putInt(0);
                writer.putInt(1);
                writer.putInt(0);
                writer.putInt(0);
                break;
        }

    }

    /**
     * Deserializes the subclass specific items from the supplied NetMsgReader.
     */
    @Override
    protected void _deserialize(ByteBufferReader reader) {

        // Common header

        this.actionType = reader.getInt();
        reader.getInt(); // Building type padding
        this.buildingUUID = reader.getInt();
        reader.getInt(); // NPC type padding
        this.npcUUID = reader.getInt();

        switch (this.actionType) {
            case ACTION_SETPRICE:
                this.itemType = reader.getInt();
                this.itemUUID = reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                this.itemPrice = reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.get();
                break;
            case ACTION_RECYCLE:
            case ACTION_TAKE:
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.get();
                this.size = reader.getInt();
                HashMap<Integer, Integer> tempIDs = new HashMap<>();
                for (int i = 0; i < this.size; i++) {
                    int type = reader.getInt(); // Item type padding
                    this.itemUUID = reader.getInt();
                    tempIDs.put(this.itemUUID, type);
                }
                reader.getInt();
                this.itemIDtoTypeMap = tempIDs;
                break;
            case ACTION_DEPOSIT:
                this.itemType = reader.getInt();
                this.itemUUID = reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.getInt();
                reader.get();
                break;
            case ACTION_JUNK:
                this.itemType = reader.getInt();
                this.itemUUID = reader.getInt();
                this.totalProduction = reader.getInt();
                this.unknown03 = reader.getInt();
                this.pToken = reader.getInt();
                this.sToken = reader.getInt();
                this.name = reader.getString();
                reader.getInt();
                reader.getInt();
                reader.get();
                break;
            default:
                this.itemType = reader.getInt();
                this.itemUUID = reader.getInt();
                this.totalProduction = reader.getInt();
                this.unknown03 = reader.getInt();
                this.pToken = reader.getInt();
                this.sToken = reader.getInt();
                this.name = reader.getString();
                this.isMultiple = reader.getInt() != 0 ? true : false;
                reader.getInt();

                if (this.actionType == ACTION_COMPLETE || this.actionType == ACTION_JUNK)
                    reader.get();
                else
                    reader.getShort();
                break;

        }
    }

    public int getItemType() {
        return itemType;
    }

    // TODO fix ArrayList Accessability.
    public ArrayList<Long> getItemList() {
        return this.ItemList;
    }

    public void setItemList(ArrayList<Long> value) {
        this.ItemList = value;
    }

    public void addItem(Long item) {
        this.ItemList.add(item);
    }

    public int getUnknown01() {
        return unknown01;
    }

    public void setUnknown01(int unknown01) {
        this.unknown01 = unknown01;
    }

    public int getTotalProduction() {
        return totalProduction;
    }

    public void setTotalProduction(int unknown02) {
        this.totalProduction = unknown02;
    }

    public int getUnknown03() {
        return unknown03;
    }

    public void setUnknown03(int unknown03) {
        this.unknown03 = unknown03;
    }

    public int getItemUUID() {
        return itemUUID;
    }

    public void setItemUUID(int itemUUID) {
        this.itemUUID = itemUUID;
    }

    public int getpToken() {
        return pToken;
    }

    public void setpToken(int pToken) {
        this.pToken = pToken;
    }

    public int getsToken() {
        return sToken;
    }

    public void setsToken(int sToken) {
        this.sToken = sToken;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public int getActionType() {
        return actionType;
    }

    public final void setActionType(int actionType) {
        this.actionType = actionType;
    }

    public int getNpcUUID() {
        return npcUUID;
    }

    public HashMap<Integer, Integer> getItemIDtoTypeMap() {
        return itemIDtoTypeMap;
    }

    /**
     * @return the itemPrice
     */
    public int getItemPrice() {
        return itemPrice;
    }

    public boolean isMultiple() {
        return isMultiple;
    }

    public void setMultiple(boolean isMultiple) {
        this.isMultiple = isMultiple;
    }
}