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


package engine.net.client.msg;

import engine.Enum.GameObjectType;
import engine.Enum.MinionType;
import engine.Enum.ProtectionState;
import engine.gameManager.PowersManager;
import engine.net.AbstractConnection;
import engine.net.ByteBufferReader;
import engine.net.ByteBufferWriter;
import engine.net.client.Protocol;
import engine.objects.*;
import engine.powers.EffectsBase;
import org.joda.time.DateTime;
import org.joda.time.Period;
import org.joda.time.Seconds;
import org.pmw.tinylog.Logger;

import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Order NPC
 */
public class ManageNPCMsg extends ClientNetMsg {

    private int targetType;
    private int targetID;
    private int unknown03;
    private int unknown04;
    private int unknown05;
    private int unknown06;

    private int unknown07;
    private int unknown08;
    private int unknown09;
    private int unknown10;
    private int unknown11;
    private int buyNormal;
    private int buyGuild;
    private int buyNation;
    private int sellNormal;
    private int sellGuild;
    private int sellNation;

    private String CityName;
    private String OwnerName;
    private String GuildName;
    private int unknown12;

    private int unknown13;
    private int unknown14;
    private int unknown15;
    private int unknown16;
    private int unknown17;
    private int unknown18;

    private int messageType;

    private int unknown19; //Arraylist motto length?
    private String motto; //motto Length 60 max?

    private int unknown01;

    private int buildingID;
    private int unknown20;
    private int unknown21;
    private int unknown22;
    private int unknown23;
    private int unknown24;
    private int unknown25;
    private int unknown26;
    private int unknown28;
    private int unknown30;
    private int unknown31;
    private int unknown32;
    private int unknown33;
    private int unknown34;
    private int unknown35;
    private int unknown36;
    private int unknown37;
    private int unknown38;
    private int unknown39;
    private int unknown40;
    private int unknown41;
    private int unknown42;
    private int unknown43;
    private int unknown44;
    private int unknown45;
    private int unknown46;
    private int unknown47;
    private int unknown48;
    private int unknown49;
    private int unknown50;
    private int unknown51;
    private int unknown52;
    private int unknown53;
    private int unknown54;
    private int unknown55;
    private int unknown56;
    private int unknown57;
    private int unknown58;
    private int unknown59;
    private int unknown60;
    private int unknown61;
    private int unknown62;
    private int unknown63;
    private int unknown64;
    private int unknown65;
    private int unknown66;
    private int unknown67;
    private int unknown68;
    private int unknown69;
    private int unknown70;
    private int unknown71;
    private int unknown72;
    private int unknown73;
    private int unknown74;
    private int unknown75;
    private int unknown76;
    private int unknown77;
    private int unknown78;
    private int unknown79;
    private int unknown80;
    private int unknown81;
    private int unknown82;
    private int unknown83;

    /**
     * This is the general purpose constructor
     */
    public ManageNPCMsg(AbstractCharacter ac) {
        super(Protocol.MANAGENPC);
        this.targetType = ac.getObjectType().ordinal();
        this.targetID = ac.getObjectUUID();
        this.buyGuild = 26;  //TODO pull all these from the NPC object
        this.buyNation = 26;
        this.buyNormal = 26;
        this.sellGuild = 100;
        this.sellNation = 100;
        this.sellNormal = 100;
        this.messageType = 1; //This seems to be the "update Hireling window" value flag

        //Unknown defaults...
        this.unknown20 = 0;
        this.unknown21 = 0;
        this.unknown22 = 0;
        this.unknown23 = 0;
        this.unknown24 = 0;
        this.unknown25 = 0;
        this.unknown26 = 0;
        this.unknown28 = 0;
        this.unknown30 = 0;
        this.unknown31 = 0;
        this.unknown32 = 0;
        this.unknown33 = 0;
        this.unknown34 = 0;
        this.unknown35 = 0;
        this.unknown36 = 0;
        this.unknown37 = 1;//1
        this.unknown38 = 0;//0
        this.unknown39 = 0;//0
        this.unknown40 = 1;//1
        this.unknown41 = 0;//0
        this.unknown42 = 1;//1 [Toggles tree icon in protection slots]
        this.unknown43 = 0;//0
        this.unknown44 = 1;//1
        this.unknown45 = 0;//0
        this.unknown46 = 0;//0
        this.unknown47 = 0;//0
        this.unknown48 = 0;//0
        this.unknown49 = 0;//0
        this.unknown50 = 0;//0
        this.unknown51 = 0;//0
        this.unknown52 = 0;//0
        this.unknown53 = 0;//0
        this.unknown54 = 1;//1
        this.unknown55 = 0;//0
        this.unknown56 = 3;//3
        this.unknown57 = 3;//3
        this.unknown58 = 0;//0
        this.unknown59 = 5;//5
        this.unknown60 = 0;//0
        this.unknown61 = 0;//0
        this.unknown62 = 0;//0
        this.unknown63 = 64;//64
        this.unknown64 = 0;//0
        this.unknown65 = 0;//0
        this.unknown66 = 0;//0
        this.unknown67 = 0;//0
        this.unknown68 = 1;//1
        this.unknown69 = 1;//1
        this.unknown70 = 0;//0
        this.unknown71 = 1;//1
        this.unknown72 = 0;
        this.unknown73 = 0;
        this.unknown74 = 5;
        this.unknown75 = 1;
        this.unknown76 = 2;
        this.unknown77 = 15;
        this.unknown78 = 3;
        this.unknown79 = 18;
        this.unknown80 = 0;
        this.unknown81 = 0;
        this.unknown82 = 0;
        this.unknown83 = 0;
    }

    /**
     * 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 ManageNPCMsg(AbstractConnection origin, ByteBufferReader reader) {
        super(Protocol.MANAGENPC, origin, reader);
    }

    //Serializes lists for Bulwarks
    private static void serializeBulwarkList(ByteBufferWriter writer, int minion) {
        int minionIndex;

        if (minion < 1 || minion > 3)
            minionIndex = 1;
        else
            minionIndex = minion;

        writer.putInt(0);
        for (int i = 0; i < 3; i++) {
            writer.putInt(0); //static
        }
        writer.putInt(9);
        writer.putInt(5);
        writer.putInt(9);
        writer.putInt(5);
        writer.put((byte) 0);

        writer.putInt((minion == 1) ? 900 : 600); //roll time
        writer.putInt(0);
        writer.putInt(0);
        writer.putInt(0); //Array
        writer.put((byte) 0);

        if (minion == 1)
            writer.putString("Trebuchet");
        else if (minion == 2)
            writer.putString("Ballista");
        else
            writer.putString("Mangonel");
        writer.put((byte) 1);
        writer.putString("A weapon suited to laying siege");
    }

    private static void serializeGuardList(ByteBufferWriter writer, int minion) {

        writer.putInt(1);

        for (int i = 0; i < 3; i++)
            writer.putInt(0); //static

        writer.putInt(minion);
        writer.putInt(1);
        writer.putInt(minion);
        writer.putInt(1);
        writer.put((byte) 0);

        writer.putInt(600); //roll time
        writer.putInt(0);
        writer.putInt(0);
        writer.putInt(0); //Array
        writer.put((byte) 0);

        MinionType minionType = MinionType.ContractToMinionMap.get(minion);
        writer.putString(minionType != null ? minionType.getRace() + " " + minionType.getName() : "Minion Guard");
        writer.put((byte) 1);
        writer.putString("A Guard To Protect Your City.");
    }

    @Override
    protected int getPowerOfTwoBufferSize() {
        return (19); // 2^10 == 1024
    }

    /**
     * Deserializes the subclass specific items to the supplied NetMsgWriter.
     */
    @Override
    protected void _deserialize(ByteBufferReader reader) {
        //TODO do we need to do anything here? Does the client ever send this message to the server?
    }

    /**
     * Serializes the subclass specific items from the supplied NetMsgReader.
     */
    @Override
    protected void _serialize(ByteBufferWriter writer) {

        Period upgradePeriod;
        int upgradePeriodInSeconds;

        try {


            writer.putInt(messageType); //1
            if (messageType == 5) {
                writer.putInt(unknown20);//0
                writer.putInt(targetType);
                writer.putInt(targetID);

                writer.putInt(GameObjectType.Building.ordinal());
                writer.putInt(buildingID);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);
                writer.putInt(0);

            } else if (messageType == 1) {
                NPC npc = null;
                Mob mobA = null;

                if (this.targetType == GameObjectType.NPC.ordinal()) {

                    npc = NPC.getFromCache(this.targetID);

                    if (npc == null) {
                        Logger.error("Missing NPC of ID " + this.targetID);
                        return;
                    }

                    Contract contract = null;
                    contract = npc.getContract();

                    if (contract == null) {
                        Logger.error("Missing contract for NPC " + this.targetID);
                        return;
                    }

                    writer.putInt(0); //anything other than 0 seems to mess up the client
                    writer.putInt(targetType);
                    writer.putInt(targetID);
                    writer.putInt(0); //static....
                    writer.putInt(0);//static....
                    writer.putInt(Blueprint.getNpcMaintCost(npc.getRank()));  // salary

                    writer.putInt(npc.getUpgradeCost());

                    if (npc.isRanking() && npc.getUpgradeDateTime().isAfter(DateTime.now()))
                        upgradePeriod = new Period(DateTime.now(), npc.getUpgradeDateTime());
                    else
                        upgradePeriod = new Period(0);

                    writer.put((byte) upgradePeriod.getDays());    //for timer
                    writer.put((byte) unknown26);//unknown
                    writer.putInt(100); //unknown

                    writer.put((byte) upgradePeriod.getHours());    //for timer
                    writer.put((byte) upgradePeriod.getMinutes());  //for timer
                    writer.put((byte) upgradePeriod.getSeconds());  //for timer

                    if (npc.isRanking() && npc.getUpgradeDateTime().isAfter(DateTime.now()))
                        upgradePeriodInSeconds = Seconds.secondsBetween(DateTime.now(), npc.getUpgradeDateTime()).getSeconds();
                    else
                        upgradePeriodInSeconds = 0;

                    writer.putInt(upgradePeriodInSeconds);

                    writer.put((byte) 0);
                    writer.put((byte) (npc.getRank() == 7 ? 0 : 1));  //0 will make the upgrade field show "N/A"
                    writer.put((byte) 0);
                    writer.put((byte) 0);
                    writer.putInt(0);
                    writer.putInt(10000);  //no idea...
                    writer.put((byte) 0);
                    writer.put((byte) 0);
                    writer.put((byte) 0);
                    writer.putInt(0);

                    NPCProfits profit = NPC.GetNPCProfits(npc);

                    if (profit == null)
                        profit = NPCProfits.defaultProfits;
                    //adding .000000001 to match client.
                    int buyNormal = (int) ((profit.buyNormal + .000001f) * 100);
                    int buyGuild = (int) ((profit.buyGuild + .000001f) * 100);
                    int buyNation = (int) ((profit.buyNation + .000001f) * 100);

                    int sellNormal = (int) ((profit.sellNormal + .000001f) * 100);
                    int sellGuild = (int) ((profit.sellGuild + .000001f) * 100);
                    int sellNation = (int) ((profit.sellNation + .000001f) * 100);

                    writer.putInt(buyNormal);
                    writer.putInt(buyGuild);
                    writer.putInt(buyNation);
                    writer.putInt(sellNormal);
                    writer.putInt(sellGuild);
                    writer.putInt(sellNation);

                    if (contract.isRuneMaster()) {
                        writer.putInt(0); //vendor slots
                        writer.putInt(0); //artillery slots

                        //figure out number of protection slots based on building rank
                        int runemasterSlots = (2 * npc.getRank()) + 6;

                        writer.putInt(runemasterSlots);

                        for (int i = 0; i < 13; i++) {
                            writer.putInt(0); //statics
                        }
                        //some unknown list
                        writer.putInt(4); //list count
                        writer.putInt(17);
                        writer.putInt(2);
                        writer.putInt(12);
                        writer.putInt(23);

                        writer.putInt(0); //static
                        writer.putInt(0); //static

                        //TODO add runemaster list here

                        ArrayList<Building> buildingList = npc.getProtectedBuildings();

                        writer.putInt(buildingList.size());

                        for (Building b : buildingList) {
                            writer.putInt(3);
                            writer.putInt(b.getObjectType().ordinal());
                            writer.putInt(b.getObjectUUID());

                            writer.putInt(npc.getParentZone().getObjectType().ordinal());
                            writer.putInt(npc.getParentZone().getObjectUUID());

                            writer.putLong(0); //TODO Identify what Comp this is suppose to be.
                            if (b.getProtectionState() == ProtectionState.PENDING)
                                writer.put((byte) 1);
                            else
                                writer.put((byte) 0);
                            writer.put((byte) 0);
                            writer.putString(b.getName());
                            writer.putInt(1);//what?
                            writer.putInt(1);//what?
                            //taxType = b.getTaxType()
                            switch (b.taxType) {
                                case NONE:
                                    writer.putInt(0);
                                    writer.putInt(0);
                                    break;
                                case WEEKLY:
                                    writer.putInt(b.taxAmount);
                                    writer.putInt(0);
                                    break;
                                case PROFIT:
                                    writer.putInt(0);
                                    writer.putInt(b.taxAmount);
                                    break;

                            }
                            writer.put(b.enforceKOS ? (byte) 1 : 0); //ENFORCE KOS
                            writer.put((byte) 0); //??
                            writer.putInt(1);
                        }

                        writer.putInt(0); //artillery captain list

                    } else if (contract.isArtilleryCaptain()) {
                        int slots = 1;
                        if (contract.getContractID() == 839)
                            slots = 3;


                        writer.putInt(0); //vendor slots
                        writer.putInt(slots); //artillery slots
                        writer.putInt(0); //runemaster slots

                        for (int i = 0; i < 13; i++) {
                            writer.putInt(0); //statics
                        }
                        //some unknown list
                        writer.putInt(1); //list count
                        writer.putInt(16);

                        writer.putInt(0); //static
                        writer.putInt(0); //static
                        writer.putInt(0); //runemaster list

                        //artillery captain list
                        ConcurrentHashMap<Mob, Integer> siegeMinions = npc.getSiegeMinionMap();
                        writer.putInt(1 + siegeMinions.size());
                        serializeBulwarkList(writer, 1); //Trebuchet
                        //serializeBulwarkList(writer, 2); //Ballista

                        if (siegeMinions != null && siegeMinions.size() > 0)

                            for (Mob mob : siegeMinions.keySet()) {
                                this.unknown83 = mob.getObjectUUID();
                                writer.putInt(2);
                                writer.putInt(mob.getObjectType().ordinal());
                                writer.putInt(this.unknown83);
                                writer.putInt(0);
                                writer.putInt(10);
                                writer.putInt(0);
                                writer.putInt(1);
                                writer.putInt(1);

                                long curTime = System.currentTimeMillis() / 1000;
                                long upgradeTime = (mob.deathTime + (mob.spawnTime * 1000)) / 1000;
                                long timeLife = upgradeTime - curTime;
                                if (upgradeTime * 1000 > System.currentTimeMillis()) {
                                    if (mob.npcOwner.isAlive()) {
                                        writer.put((byte) 0);//shows respawning timer
                                        writer.putInt(mob.spawnTime);
                                        writer.putInt(mob.spawnTime);
                                        writer.putInt((int) timeLife); //time remaining for mob that is dead
                                        writer.putInt(0);
                                        writer.put((byte) 0);
                                        writer.putString(mob.getNameOverride().isEmpty() ? mob.getName() : mob.getNameOverride());
                                        writer.put((byte) 0);
                                    } else {
                                        writer.put((byte) 0);//shows respawning timer
                                        writer.putInt(0);
                                        writer.putInt(0);
                                        writer.putInt(0); //time remaining for mob that is dead
                                        writer.putInt(0);
                                        writer.put((byte) 0);
                                        writer.putString(mob.getNameOverride().isEmpty() ? mob.getName() : mob.getNameOverride());
                                        writer.put((byte) 0);
                                    }
                                } else {
                                    //nothing required for countdown for a mob that is alive
                                    writer.put((byte) 1);//shows "Standing By"
                                    writer.putInt(0);
                                    writer.putInt(0);
                                    writer.putInt(0);
                                    writer.putInt(0);
                                    writer.put((byte) 0);
                                    writer.putString(mob.getNameOverride().isEmpty() ? mob.getName() : mob.getNameOverride());
                                    writer.put((byte) 0);
                                }
                            }
                        return;

                    } else {

                        if (Contract.NoSlots(npc.getContract()))
                            writer.putInt(0);
                        else
                            writer.putInt(npc.getRank()); //vendor slots
                        writer.putInt(0); //artilerist slots
                        writer.putInt(0); //runemaster slots

                        writer.putInt(1); //is this static?
                        for (int i = 0; i < 4; i++) {
                            writer.putInt(0); //statics
                        }
                        //Begin Item list for creation.
                        writer.putInt(npc.getCanRoll().size());

                        for (Integer ib : npc.getCanRoll()) {
                            ItemBase item = ItemBase.getItemBase(ib);
                            writer.put((byte) 1);
                            writer.putInt(0);
                            writer.putInt(ib); //itemID
                            writer.putInt(item.getBaseValue());
                            writer.putInt(600);
                            writer.put((byte) 1);
                            writer.put((byte) item.getModTable());
                            writer.put((byte) item.getModTable());
                            writer.put((byte) item.getModTable());
                            writer.put((byte) item.getModTable());//EffectItemType
                        }
                        ArrayList<MobLoot> itemList = npc.getRolling();

                        if (itemList.isEmpty())
                            writer.putInt(0);
                        else {
                            if (itemList.size() < npc.getRank())
                                writer.putInt(itemList.size());
                            else
                                writer.putInt(npc.getRank());
                            for (Item i : itemList) {
                                if (itemList.indexOf(i) >= npc.getRank())
                                    break;
                                ItemBase ib = i.getItemBase();
                                writer.put((byte) 0); // ? Unknown45
                                writer.putInt(i.getObjectType().ordinal());
                                writer.putInt(i.getObjectUUID());

                                writer.putInt(0);
                                writer.putInt(i.getItemBaseID());
                                writer.putInt(ib.getBaseValue());
                                long curTime = System.currentTimeMillis() / 1000;
                                long upgradeTime = i.getDateToUpgrade() / 1000;
                                long timeLife = i.getDateToUpgrade() - System.currentTimeMillis();

                                timeLife /= 1000;
                                writer.putInt((int) timeLife);
                                writer.putInt(npc.getRollingTimeInSeconds(i.getItemBaseID()));
                                writer.putInt(1);
                                if (i.isComplete())
                                    writer.put((byte) 1);
                                else
                                    writer.put((byte) 0);

                                ArrayList<String> effectsList = i.getEffectNames();
                                EffectsBase prefix = null;
                                EffectsBase suffix = null;

                                for (String effectName : effectsList) {
                                    if (effectName.contains("PRE"))
                                        prefix = PowersManager.getEffectByIDString(effectName);
                                    if (effectName.contains("SUF"))
                                        suffix = PowersManager.getEffectByIDString(effectName);

                                }

                                if ((prefix == null && suffix == null))
                                    writer.putInt(0);
                                else
                                    writer.putInt(-1497023830);
                                if ((prefix != null && !i.isRandom()) || (prefix != null && i.isComplete()))
                                    writer.putInt(prefix.getToken());
                                else
                                    writer.putInt(0);
                                if ((suffix != null && !i.isRandom()) || (suffix != null && i.isComplete()))
                                    writer.putInt(suffix.getToken());
                                else
                                    writer.putInt(0);
                                writer.putString(i.getCustomName());
                            }
                        }

                        writer.putInt(0);
                        writer.putInt(0);
                        writer.putInt(1);
                        writer.putInt(0);
                        writer.putInt(3);
                        writer.putInt(3);
                        writer.putInt(0);
                        writer.putString("Repair items");
                        writer.putString("percent");
                        writer.putInt(npc.getRepairCost()); //cost for repair
                        writer.putInt(0);
                        //ArrayList<Integer> modSuffixList =
                        ArrayList<Integer> modPrefixList = npc.getModTypeTable();
                        Integer mod = modPrefixList.get(0);

                        if (mod != 0) {
                            writer.putInt(npc.getModTypeTable().size()); //Effects size
                            for (Integer mtp : npc.getModTypeTable()) {

                                Integer imt = modPrefixList.indexOf(mtp);
                                writer.putInt(npc.getItemModTable().get(imt)); //?
                                writer.putInt(0);
                                writer.putInt(0);
                                writer.putFloat(2);
                                writer.putInt(0);
                                writer.putInt(1);
                                writer.putInt(2);
                                writer.putInt(0);
                                writer.putInt(1);
                                writer.put(npc.getItemModTable().get(imt));
                                writer.put(npc.getItemModTable().get(imt));
                                writer.put(npc.getItemModTable().get(imt));
                                writer.put(npc.getItemModTable().get(imt));//writer.putInt(-916801465); effectItemType
                                writer.putInt(mtp); //prefix
                                Integer mts = modPrefixList.indexOf(mtp);
                                writer.putInt(npc.getModSuffixTable().get(mts)); //suffix
                            }
                        } else
                            writer.putInt(0);
                        ArrayList<Item> inventory = npc.getInventory();


                        writer.putInt(inventory.size()); //placeholder for item cnt


                        for (Item i : inventory) {

                            Item.serializeForClientMsgWithoutSlot(i, writer);
                        }


                        writer.putInt(0);
                        writer.putInt(5);
                        writer.putInt(1);
                        writer.putInt(2);
                        writer.putInt(15);
                        writer.putInt(3);
                        writer.putInt(18);

                        writer.putInt(0);
                        writer.putInt(0);
                        writer.putInt(0);
                        writer.putInt(0);
                    }

                } else if (this.targetType == GameObjectType.Mob.ordinal()) {

                    mobA = Mob.getFromCacheDBID(this.targetID);
                    if (mobA == null) {
                        Logger.error("Missing Mob of ID " + this.targetID);
                        return;
                    }

                    if (mobA != null) {
                        Contract con = mobA.getContract();
                        if (con == null) {
                            Logger.error("Missing contract for NPC " + this.targetID);
                            return;
                        }

                        int maxSlots = 1;

                        switch (mobA.getRank()) {
                            case 1:
                            case 2:
                                maxSlots = 1;
                                break;
                            case 3:
                                maxSlots = 2;
                                break;
                            case 4:
                            case 5:
                                maxSlots = 3;
                                break;
                            case 6:
                                maxSlots = 4;
                                break;
                            case 7:
                                maxSlots = 5;
                                break;
                            default:
                                maxSlots = 1;

                        }
                        if (NPC.ISGuardCaptain(mobA.getContract().getContractID()) == false) {
                            maxSlots = 0;
                        }
                        writer.putInt(0); //anything other than 0 seems to mess up the client
                        writer.putInt(targetType);
                        writer.putInt(targetID);
                        writer.putInt(0); //static....
                        writer.putInt(0);//static....
                        writer.putInt(Blueprint.getNpcMaintCost(mobA.getRank()));  // salary

                        writer.putInt(Mob.getUpgradeCost(mobA));

                        if (mobA.isRanking() && mobA.getUpgradeDateTime().isAfter(DateTime.now()))
                            upgradePeriod = new Period(DateTime.now(), mobA.getUpgradeDateTime());
                        else
                            upgradePeriod = new Period(0);

                        writer.put((byte) upgradePeriod.getDays());    //for timer
                        writer.put((byte) unknown26);//unknown
                        writer.putInt(100); //unknown

                        writer.put((byte) upgradePeriod.getHours());    //for timer
                        writer.put((byte) upgradePeriod.getMinutes());  //for timer
                        writer.put((byte) upgradePeriod.getSeconds());  //for timer

                        if (mobA.isRanking() && mobA.getUpgradeDateTime().isAfter(DateTime.now()))
                            upgradePeriodInSeconds = Seconds.secondsBetween(DateTime.now(), mobA.getUpgradeDateTime()).getSeconds();
                        else
                            upgradePeriodInSeconds = 0;

                        writer.putInt(upgradePeriodInSeconds);


                        writer.put((byte) 0);
                        writer.put((byte) (mobA.getRank() == 7 ? 0 : 1));  //0 will make the upgrade field show "N/A"
                        writer.put((byte) 0);
                        writer.put((byte) 0);
                        writer.putInt(0);
                        writer.putInt(10000);  //no idea...
                        writer.put((byte) 0);
                        writer.put((byte) 0);
                        writer.put((byte) 0);
                        writer.putInt(0);


                        NPCProfits profit = NPCProfits.defaultProfits;

                        writer.putInt((int) (profit.buyNormal * 100));
                        writer.putInt((int) (profit.buyGuild * 100));
                        writer.putInt((int) (profit.buyNation * 100));
                        writer.putInt((int) (profit.sellNormal * 100));
                        writer.putInt((int) (profit.sellGuild * 100));
                        writer.putInt((int) (profit.sellNation * 100));

                        writer.putInt(0); //vendor slots
                        writer.putInt(maxSlots); //artillery slots
                        writer.putInt(0); //runemaster slots

                        for (int i = 0; i < 13; i++) {
                            writer.putInt(0); //statics
                        }
                        //some unknown list
                        writer.putInt(1); //list count
                        writer.putInt(16);

                        writer.putInt(0); //static
                        writer.putInt(0); //static
                        writer.putInt(0); //runemaster list

                        //artillery captain list
                        ConcurrentHashMap<Mob, Integer> siegeMinions = mobA.getSiegeMinionMap();


                        writer.putInt(siegeMinions.size() + 1);
                        serializeGuardList(writer, mobA.getContract().getContractID()); //Guard

                        if (siegeMinions != null && siegeMinions.size() > 0)

                            for (Mob mob : siegeMinions.keySet()) {
                                this.unknown83 = mob.getObjectUUID();
                                writer.putInt(2);
                                writer.putInt(mob.getObjectType().ordinal());
                                writer.putInt(this.unknown83);
                                writer.putInt(0);
                                writer.putInt(10);
                                writer.putInt(0);
                                writer.putInt(1);
                                writer.putInt(1);

                                long curTime = System.currentTimeMillis() / 1000;
                                long upgradeTime = (mob.deathTime + (mob.spawnTime * 1000)) / 1000;
                                long timeLife = upgradeTime - curTime;
                                if (upgradeTime * 1000 > System.currentTimeMillis()) {
                                    if (mob.npcOwner.isAlive()) {
                                        writer.put((byte) 0);//shows respawning timer
                                        writer.putInt(mob.spawnTime);
                                        writer.putInt(mob.spawnTime);
                                        writer.putInt((int) timeLife); //time remaining for mob that is dead
                                        writer.putInt(0);
                                        writer.put((byte) 0);
                                        writer.putString(mob.getNameOverride().isEmpty() ? mob.getName() : mob.getNameOverride());
                                        writer.put((byte) 0);
                                    } else {
                                        writer.put((byte) 0);//shows respawning timer
                                        writer.putInt(0);
                                        writer.putInt(0);
                                        writer.putInt(0); //time remaining for mob that is dead
                                        writer.putInt(0);
                                        writer.put((byte) 0);
                                        writer.putString(mob.getNameOverride().isEmpty() ? mob.getName() : mob.getNameOverride());
                                        writer.put((byte) 0);
                                    }
                                } else {
                                    //nothing required for countdown for a mob that is alive
                                    writer.put((byte) 1);//shows "Standing By"
                                    writer.putInt(0);
                                    writer.putInt(0);
                                    writer.putInt(0);
                                    writer.putInt(0);
                                    writer.put((byte) 0);
                                    writer.putString(mob.getNameOverride().isEmpty() ? mob.getName() : mob.getNameOverride());
                                    writer.put((byte) 0);
                                }
                            }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    public String getCityName() {
        return CityName;
    }


    public void setUnknown07(int unknown07) {
        this.unknown07 = unknown07;
    }

    public String getMotto() {
        return motto;
    }

    public void setMotto(String motto) {
        this.motto = motto;
    }

    public int getUnknown01() {
        return unknown01;
    }

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

    public int getUnknown03() {
        return unknown03;
    }

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

    public int getUnknown04() {
        return unknown04;
    }

    public void setUnknown04(int unknown04) {
        this.unknown04 = unknown04;
    }

    public int getUnknown05() {
        return unknown05;
    }

    public void setUnknown05(int unknown05) {
        this.unknown05 = unknown05;
    }

    public int getUnknown06() {
        return unknown06;
    }

    public void setUnknown06(int unknown06) {
        this.unknown06 = unknown06;
    }

    public int getBuyNormal() {
        return buyNormal;
    }

    public void setBuyNormal(int buyNormal) {
        this.buyNormal = buyNormal;
    }

    public int getBuyGuild() {
        return buyGuild;
    }

    public void setBuyGuild(int buyGuild) {
        this.buyGuild = buyGuild;
    }

    public int getBuyNation() {
        return buyNation;
    }

    public void setBuyNation(int buyNation) {
        this.buyNation = buyNation;
    }

    public int getSellNormal() {
        return sellNormal;
    }

    public void setSellNormal(int sellNormal) {
        this.sellNormal = sellNormal;
    }

    public int getSellGuild() {
        return sellGuild;
    }

    public void setSellGuild(int sellGuild) {
        this.sellGuild = sellGuild;
    }

    public int getSellNation() {
        return sellNation;
    }

    public void setSellNation(int sellNation) {
        this.sellNation = sellNation;
    }

    public int getMessageType() {
        return messageType;
    }

    public void setMessageType(int messageType) {
        this.messageType = messageType;
    }

    public int getBuildingID() {
        return buildingID;
    }

    public void setBuildingID(int buildingID) {
        this.buildingID = buildingID;
    }

    public int getUnknown20() {
        return unknown20;
    }

    public void setUnknown20(int unknown20) {
        this.unknown20 = unknown20;
    }

    public int getUnknown21() {
        return unknown21;
    }

    public void setUnknown21(int unknown21) {
        this.unknown21 = unknown21;
    }

    public int getUnknown22() {
        return unknown22;
    }

    public void setUnknown22(int unknown22) {
        this.unknown22 = unknown22;
    }

    public int getUnknown23() {
        return unknown23;
    }

    public void setUnknown23(int unknown23) {
        this.unknown23 = unknown23;
    }

    public int getUnknown24() {
        return unknown24;
    }

    public void setUnknown24(int unknown24) {
        this.unknown24 = unknown24;
    }

    public int getUnknown25() {
        return unknown25;
    }

    public void setUnknown25(int unknown25) {
        this.unknown25 = unknown25;
    }

    public int getUnknown26() {
        return unknown26;
    }

    public void setUnknown26(int unknown26) {
        this.unknown26 = unknown26;
    }

    public int getUnknown28() {
        return unknown28;
    }

    public void setUnknown28(int unknown28) {
        this.unknown28 = unknown28;
    }

    public int getUnknown30() {
        return unknown30;
    }

    public void setUnknown30(int unknown30) {
        this.unknown30 = unknown30;
    }

    public int getUnknown31() {
        return unknown31;
    }

    public void setUnknown31(int unknown31) {
        this.unknown31 = unknown31;
    }

    public int getUnknown32() {
        return unknown32;
    }

    public void setUnknown32(int unknown32) {
        this.unknown32 = unknown32;
    }

    public int getUnknown33() {
        return unknown33;
    }

    public void setUnknown33(int unknown33) {
        this.unknown33 = unknown33;
    }

    public int getUnknown34() {
        return unknown34;
    }

    public void setUnknown34(int unknown34) {
        this.unknown34 = unknown34;
    }

    public int getUnknown35() {
        return unknown35;
    }

    public void setUnknown35(int unknown35) {
        this.unknown35 = unknown35;
    }

    public int getUnknown36() {
        return unknown36;
    }

    public void setUnknown36(int unknown36) {
        this.unknown36 = unknown36;
    }

    public int getUnknown37() {
        return unknown37;
    }

    public void setUnknown37(int unknown37) {
        this.unknown37 = unknown37;
    }

    public int getUnknown38() {
        return unknown38;
    }

    public void setUnknown38(int unknown38) {
        this.unknown38 = unknown38;
    }

    public int getUnknown39() {
        return unknown39;
    }

    public void setUnknown39(int unknown39) {
        this.unknown39 = unknown39;
    }

    public int getUnknown40() {
        return unknown40;
    }

    public void setUnknown40(int unknown40) {
        this.unknown40 = unknown40;
    }

    public int getUnknown41() {
        return unknown41;
    }

    public void setUnknown41(int unknown41) {
        this.unknown41 = unknown41;
    }

    public int getUnknown42() {
        return unknown42;
    }

    public void setUnknown42(int unknown42) {
        this.unknown42 = unknown42;
    }

    public int getUnknown44() {
        return unknown44;
    }

    public void setUnknown44(int unknown44) {
        this.unknown44 = unknown44;
    }

    public int getUnknown43() {
        return unknown43;
    }

    public void setUnknown43(int unknown43) {
        this.unknown43 = unknown43;
    }

    public int getUnknown45() {
        return unknown45;
    }

    public void setUnknown45(int unknown45) {
        this.unknown45 = unknown45;
    }

    public int getUnknown46() {
        return unknown46;
    }

    public void setUnknown46(int unknown46) {
        this.unknown46 = unknown46;
    }

    public int getUnknown47() {
        return unknown47;
    }

    public void setUnknown47(int unknown47) {
        this.unknown47 = unknown47;
    }

    public int getUnknown48() {
        return unknown48;
    }

    public void setUnknown48(int unknown48) {
        this.unknown48 = unknown48;
    }

    public int getUnknown49() {
        return unknown49;
    }

    public void setUnknown49(int unknown49) {
        this.unknown49 = unknown49;
    }

    public int getUnknown50() {
        return unknown50;
    }

    public void setUnknown50(int unknown50) {
        this.unknown50 = unknown50;
    }

    public int getUnknown51() {
        return unknown51;
    }

    public void setUnknown51(int unknown51) {
        this.unknown51 = unknown51;
    }

    public int getUnknown52() {
        return unknown52;
    }

    public void setUnknown52(int unknown52) {
        this.unknown52 = unknown52;
    }

    public int getUnknown53() {
        return unknown53;
    }

    public void setUnknown53(int unknown53) {
        this.unknown53 = unknown53;
    }

    public int getUnknown54() {
        return unknown54;
    }

    public void setUnknown54(int unknown54) {
        this.unknown54 = unknown54;
    }

    public int getUnknown55() {
        return unknown55;
    }

    public void setUnknown55(int unknown55) {
        this.unknown55 = unknown55;
    }

    public int getUnknown56() {
        return unknown56;
    }

    public void setUnknown56(int unknown56) {
        this.unknown56 = unknown56;
    }

    public int getUnknown57() {
        return unknown57;
    }

    public void setUnknown57(int unknown57) {
        this.unknown57 = unknown57;
    }

    public int getUnknown58() {
        return unknown58;
    }

    public void setUnknown58(int unknown58) {
        this.unknown58 = unknown58;
    }

    public int getUnknown59() {
        return unknown59;
    }

    public void setUnknown59(int unknown59) {
        this.unknown59 = unknown59;
    }

    public int getUnknown60() {
        return unknown60;
    }

    public void setUnknown60(int unknown60) {
        this.unknown60 = unknown60;
    }

    public int getUnknown61() {
        return unknown61;
    }

    public void setUnknown61(int unknown61) {
        this.unknown61 = unknown61;
    }

    public int getUnknown62() {
        return unknown62;
    }

    public void setUnknown62(int unknown62) {
        this.unknown62 = unknown62;
    }

    public int getUnknown63() {
        return unknown63;
    }

    public void setUnknown63(int unknown63) {
        this.unknown63 = unknown63;
    }

    public int getUnknown64() {
        return unknown64;
    }

    public void setUnknown64(int unknown64) {
        this.unknown64 = unknown64;
    }

    public int getUnknown65() {
        return unknown65;
    }

    public void setUnknown65(int unknown65) {
        this.unknown65 = unknown65;
    }

    public int getUnknown66() {
        return unknown66;
    }

    public void setUnknown66(int unknown66) {
        this.unknown66 = unknown66;
    }

    public int getUnknown67() {
        return unknown67;
    }

    public void setUnknown67(int unknown67) {
        this.unknown67 = unknown67;
    }

    public int getUnknown68() {
        return unknown68;
    }

    public void setUnknown68(int unknown68) {
        this.unknown68 = unknown68;
    }

    public int getUnknown69() {
        return unknown69;
    }

    public void setUnknown69(int unknown69) {
        this.unknown69 = unknown69;
    }

    public int getUnknown70() {
        return unknown70;
    }

    public void setUnknown70(int unknown70) {
        this.unknown70 = unknown70;
    }

    public int getUnknown71() {
        return unknown71;
    }

    public void setUnknown71(int unknown71) {
        this.unknown71 = unknown71;
    }

    public int getUnknown72() {
        return unknown72;
    }

    public void setUnknown72(int unknown72) {
        this.unknown72 = unknown72;
    }

    public int getUnknown73() {
        return unknown73;
    }

    public void setUnknown73(int unknown73) {
        this.unknown73 = unknown73;
    }

    public int getUnknown74() {
        return unknown74;
    }

    public void setUnknown74(int unknown74) {
        this.unknown74 = unknown74;
    }

    public int getUnknown75() {
        return unknown75;
    }

    public void setUnknown75(int unknown75) {
        this.unknown75 = unknown75;
    }

    public int getUnknown76() {
        return unknown76;
    }

    public void setUnknown76(int unknown76) {
        this.unknown76 = unknown76;
    }

    public int getUnknown77() {
        return unknown77;
    }

    public void setUnknown77(int unknown77) {
        this.unknown77 = unknown77;
    }

    public int getUnknown78() {
        return unknown78;
    }

    public void setUnknown78(int unknown78) {
        this.unknown78 = unknown78;
    }

    public int getUnknown79() {
        return unknown79;
    }

    public void setUnknown79(int unknown79) {
        this.unknown79 = unknown79;
    }

    public int getTargetType() {
        return targetType;
    }

    public void setTargetType(int targetType) {
        this.targetType = targetType;
    }

    public int getTargetID() {
        return targetID;
    }

    public void setTargetID(int targetID) {
        this.targetID = targetID;
    }


}