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


package engine.net.client.msg;

import engine.Enum.*;
import engine.gameManager.BuildingManager;
import engine.gameManager.ZoneManager;
import engine.math.Vector3fImmutable;
import engine.net.AbstractConnection;
import engine.net.ByteBufferReader;
import engine.net.ByteBufferWriter;
import engine.net.client.Protocol;
import engine.objects.*;
import org.joda.time.DateTime;
import org.joda.time.Period;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Open manage city asset window
 */
public class ManageCityAssetsMsg extends ClientNetMsg {

    //messageType
    //C->S 2: S->C: 0, 3, 4, 6
    //C->S 15: S->C: 15
    //C->S 14: S->C: 14
    //C->S ?: S->C: 10, 11, 16

    //C->S	2 = manage this asset
    //		20 = manage entire city

    //S->C,	0 = error message
    //		3 = manage asset
    //		4 = no access / building info

    public int actionType;
    public String assetName;
    public String CityName;
    public int upgradeCost;
    public byte labelProtected;
    public byte labelSiege;
    public byte labelCeaseFire;
    public byte buttonTransfer;
    public byte buttonDestroy;
    public byte buttonAbandon;
    public byte buttonUpgrade;
    private int targetID;
    private int targetType;
    private int targetType1;
    private int targetType2;
    private int targetType3;
    private int targetID1;
    private int targetID2;
    private int targetID3;
    private String AssetName1;
    private int rank;
    private int symbol;
    private int unknown04;
    private int unknown05;
    private int unknown06;
    private int unknown07;
    private int unknown14;
    private int unknown15;
    private int unknown16;
    private int unknown17;
    private int unknown54;
    private int preName01;
    private byte UnkByte03;
    private byte UnkByte04;
    private int strongbox;
    private int baneHour;
    private PlayerCharacter assetManager;
    private Building asset;

    /**
     * This is the general purpose constructor
     */
    public ManageCityAssetsMsg() {
        super(Protocol.MANAGECITYASSETS);
        this.actionType = 0;
        this.targetType = 0;
        this.targetID = 0;
        this.preName01 = 0;
        this.assetName = "";
        this.CityName = "";
        this.rank = 0;
        this.symbol = 0;
        this.unknown04 = 0;
        this.unknown06 = 0;
        this.unknown07 = 0;
        this.unknown14 = 0;
        this.unknown15 = 0;
        this.unknown16 = 0;
        this.unknown17 = 0;

        this.strongbox = 0;

        this.targetType1 = 0;
        this.targetType2 = 0;
        this.targetType3 = 0;

        this.targetID1 = 0;
        this.targetID2 = 0;
        this.targetID3 = 0;
        this.UnkByte03 = 0;
        this.UnkByte04 = 0;
        this.AssetName1 = "";
        this.unknown54 = 0;
        this.strongbox = 0;
        this.upgradeCost = 0;

        this.labelProtected = 0;
        this.labelSiege = 0;
        this.labelCeaseFire = 0;
        this.buttonTransfer = 0;
        this.buttonDestroy = 0;
        this.buttonAbandon = 0;
        this.buttonUpgrade = 0;

    }

    public ManageCityAssetsMsg(PlayerCharacter pc, Building asset) {
        super(Protocol.MANAGECITYASSETS);
        this.assetManager = pc;
        this.asset = asset;
    }

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

    public int getTargetID() {
        return targetID;
    }

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

    protected int getPowerOfTwoBufferSize() {
        return (20); // 2^10 == 1024
    }

    public int getTargetType() {
        return targetType;
    }

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

    public void setTargetType3(int targetType3) {
        this.targetType3 = targetType3;
    }

    public void setTargetID3(int targetID3) {
        this.targetID3 = targetID3;
    }

    /**
     * Deserializes the subclass specific items to the supplied NetMsgWriter.
     */
    @Override
    protected void _deserialize(ByteBufferReader reader) {
        actionType = reader.getInt();
        targetType = reader.getInt();
        targetID = reader.getInt();
        if (this.actionType == 20) {
            reader.getInt();
            this.baneHour = reader.getInt();
            reader.getInt();
            reader.getInt();
            reader.getInt();
            reader.getInt();
            reader.getInt();

        } else if (this.actionType == 5) { //rename building.
            reader.getInt();
            assetName = reader.getString();
            for (int i = 0; i < 5; i++)
                reader.getInt();
        } else if (this.actionType == 2) {
            reader.getInt();
            this.strongbox = reader.getInt();
            reader.getInt();
            reader.getInt();
            reader.getInt();
            reader.getInt();


        } else {
            for (int i = 0; i < 6; i++)
                reader.getInt();
        }
    }

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

        if (this.actionType == 2) {
            writer.putInt(asset.getObjectType().ordinal());
            writer.putInt(asset.getObjectUUID());
            writer.putInt(0);
            writer.putInt(asset.reserve);
            writer.putInt(0);
            return;
        }

        if (this.actionType == 13) {
            writer.putInt(asset.getObjectType().ordinal());
            writer.putInt(asset.getObjectUUID());
            writer.putInt(0);
            writer.putInt(0);
            writer.putInt(asset.getHirelings().size());
            for (AbstractCharacter hireling : asset.getHirelings().keySet()) {
                if (!hireling.getObjectType().equals(GameObjectType.NPC))
                    writer.putString(hireling.getName());
                else {
                    NPC npc = (NPC) hireling;
                    if (!npc.getNameOverride().isEmpty()) {
                        writer.putString(npc.getNameOverride());
                    } else if (npc.getContract() != null) {
                        if (npc.getContract().isTrainer()) {
                            writer.putString(npc.getName() + ", " + npc.getContract().getName());
                        } else {
                            writer.putString(npc.getName() + " " + npc.getContract().getName());
                        }
                    } else {
                        writer.putString(npc.getName());
                    }
                }
            }

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

        //Bane window

        if (this.actionType == 11) {
            writer.putInt(asset.getObjectType().ordinal());
            writer.putInt(asset.getObjectUUID());
            for (int a = 0; a < 5; a++)
                writer.putInt(0);

            writer.putInt(asset.getHirelings().size());

            for (AbstractCharacter npcHire : asset.getHirelings().keySet()) {
                writer.putInt(npcHire.getObjectType().ordinal());
                writer.putInt(npcHire.getObjectUUID());
                if (npcHire.getObjectType() == GameObjectType.NPC)
                    writer.putString(((NPC) npcHire).getContract().getName());
                else
                    writer.putString("Guard Captain");


                writer.putString(npcHire.getName());
                writer.putInt(1);
                writer.putInt(Blueprint.getNpcMaintCost(npcHire.getRank()));
                if (npcHire.getObjectType() == GameObjectType.NPC)
                    writer.putInt(((NPC) npcHire).getContract().getIconID()); // Was 60
                else if (npcHire.getObjectType() == GameObjectType.Mob) {
                    writer.putInt(((Mob) npcHire).getContract().getIconID()); // Was 60
                } else
                    writer.putInt(5);
                writer.put((byte) 0);
                writer.put((byte) 0);
                writer.put((byte) 1);
                writer.put((byte) 0);
                writer.put((byte) 0);
            }
            return;
        }

        if (this.actionType == 15) {
            writer.putInt(1);
            writer.putInt(1);
            City city = null;
            Zone playerZone = ZoneManager.findSmallestZone(assetManager.getLoc());
            Set<Building> buildings = ZoneManager.findSmallestZone(assetManager.getLoc()).zoneBuildingSet;

            Building tol = null;
            if (playerZone.playerCityUUID != 0)
                city = City.GetCityFromCache(playerZone.playerCityUUID);

            if (city != null)
                tol = city.getTOL();


            writer.putInt(0); // 1 + String = custom message, cant control assets.
            writer.putInt(0);
            writer.putInt(0); //array

            writer.putInt(buildings.size());

            int i = 0;
            for (Building building : buildings) {

                i++;
                writer.putString(building.getName()); //ARRAY
                writer.putInt(building.getObjectType().ordinal()); //?
                writer.putInt(building.getObjectUUID()); //?

                writer.putInt(4);
                writer.putInt(4);

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

                //max distance to bypass clientside check.
                float maxDistance = 2000;


                writer.putFloat(maxDistance);

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

                if (building.getPatrolPoints() != null) {
                    writer.putInt(building.getPatrolPoints().size());
                    for (Vector3fImmutable patrolPoint : building.getPatrolPoints()) {
                        writer.putVector3f(patrolPoint);
                    }
                } else {
                    writer.putInt(0);
                }
                writer.putInt(0); //Sentry Point

                if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK) {
                    writer.putInt(1); //Tab left Random Town? //Opens up 16 Bytes
                    writer.putInt(4);
                    writer.putInt(0);
                    writer.putInt(0);
                    writer.putInt(4);
                } else
                    writer.putInt(0);
                writer.putInt(0); //array with size 32 bytes. // Adds information of building
            }
            writer.putInt(0); //ARRAY
            writer.putInt(0);
        }

        if (this.actionType == 18) {
            Zone zone = asset.getParentZone();

            if (zone == null)
                return;

            City banedCity = City.getCity(zone.playerCityUUID);

            if (banedCity == null)
                return;

            Bane bane = banedCity.getBane();

            if (bane == null)
                return;

            Guild attackerGuild = bane.getOwner().getGuild();

            if (attackerGuild == null)
                return;

            writer.putInt(asset.getObjectType().ordinal());
            writer.putInt(asset.getObjectUUID());

            writer.putInt(0);
            writer.putString(attackerGuild.getName());
            writer.putString(Guild.GetGL(attackerGuild).getName());
            writer.putInt(bane.getSiegePhase().ordinal()); //1 challenge //2 standoff //3 war
            writer.put((byte) 0);

            if (!bane.isAccepted() && this.assetManager.getGuild() == banedCity.getGuild() && GuildStatusController.isInnerCouncil(this.assetManager.getGuildStatus()))
                writer.put((byte) 1); //canSetTime
            else
                writer.put((byte) 0);

            DateTime placedOn = bane.getLiveDate();

            if (placedOn == null)
                placedOn = new DateTime(DateTime.now());

            //set Calander to date of bane live.
            DateTime now = DateTime.now();
            DateTime defaultTime = new DateTime(bane.getPlacementDate());
            DateTime playerEnterWorldTime = new DateTime(this.assetManager.getTimeStamp("EnterWorld"));
            Period period = new Period(playerEnterWorldTime.getMillis(), now.getMillis());
            int hoursLoggedIn = period.getHours();
            hoursLoggedIn = hoursLoggedIn < 0 ? 0 : hoursLoggedIn;

            defaultTime = defaultTime.plusDays(2);
            defaultTime = defaultTime.hourOfDay().setCopy(22);
            defaultTime = defaultTime.minuteOfHour().setCopy(0);
            defaultTime = defaultTime.secondOfMinute().setCopy(0);

            long curTime = now.getMillis();
            long timeLeft = 0;

            if (bane.getLiveDate() != null)
                timeLeft = bane.getLiveDate().getMillis() - curTime;
            else
                timeLeft = defaultTime.getMillis() - curTime + 1000;

            //DO not touch these. They are static formula's until i get the correct converter for SB Time.

            writer.put((byte) placedOn.dayOfMonth().get());
            writer.put((byte) placedOn.monthOfYear().get());
            writer.putInt(placedOn.year().get() - 1900);
            writer.put((byte) 0);
            writer.put((byte) 0);
            writer.put((byte) 0);

            if (timeLeft < 0)
                writer.putInt(0);
            else
                writer.putInt((int) timeLeft / 1000); // Time remaing until bane/Seconds

            if (attackerGuild.getGuildState() == GuildState.Sworn)
                writer.putInt(4); //3 capture/errant,4 capture/sworn, 5 destroy/soveirgn.
            else
                writer.putInt(5);

            writer.put((byte) (16 - hoursLoggedIn)); // hour start
            writer.put((byte) (24 - hoursLoggedIn)); // hour end
            writer.put((byte) 2);
            writer.putString(banedCity.getCityName());
            writer.putString(Guild.GetGL(bane.getOwner().getGuild()) != null ? Guild.GetGL(bane.getOwner().getGuild()).getName() : "No Guild Leader");
            GuildTag._serializeForDisplay(attackerGuild.getGuildTag(), writer);
            GuildTag._serializeForDisplay(attackerGuild.getNation().getGuildTag(), writer);
            writer.putInt(0);
            writer.putInt(0);
            writer.putInt(0);
            writer.putInt(0);
            writer.putInt(0);
        }

        if (this.actionType == 3) {

            writer.putInt(targetType);
            writer.putInt(targetID);

            Guild nation = null;
            Building building = BuildingManager.getBuildingFromCache(targetID);
            Guild guild = building.getGuild();
            Zone zone = ZoneManager.findSmallestZone(building.getLoc());

            writer.putInt(0);//unknown  Might be to allow manager to open or not!
            writer.putString(building.getName());

            AbstractCharacter buildingOwner = building.getOwner();

            if (buildingOwner == null)
                writer.putString("Morloch");
            else
                writer.putString(buildingOwner.getName());

            if (zone == null)
                writer.putString("Forlord");
            else
                writer.putString(zone.zoneName);

            writer.putString(building.getGuild().getName());

            writer.putInt(building.getRank());

            // Maintenance costs include resource if
            // this structure is an R8 tree

            if (building.getRank() == 8)
                writer.putInt(5); // Resources included
            else
                writer.putInt(1); // Gold only

            writer.putInt(2308551); //Gold
            if (building.getBlueprint() == null)
                writer.putInt(0);
            else
                writer.putInt(building.getBlueprint().getMaintCost(building.getRank())); //  maint cost

            if (building.getRank() == 8) {
                writer.putInt(74856115); // Stone
                writer.putInt(1500); //  maint cost
                writer.putInt(-1603256692); // Lumber
                writer.putInt(1500); //  maint cost
                writer.putInt(-1596311545); // Galvor
                writer.putInt(5); //  maint cost
                writer.putInt(1532478436); // Wormwood
                writer.putInt(5); //  maint cost
            }

            LocalDateTime maintDate = building.maintDateTime;

            if (maintDate == null)
                maintDate = LocalDateTime.now();
            writer.putLocalDateTime(LocalDateTime.now()); // current time

            // utc offset?
            writer.putInt((int) java.time.Duration.between(LocalDateTime.now(), maintDate).getSeconds()); // Seconds to maint date

            writer.putInt(building.getStrongboxValue());
            writer.putInt(building.reserve);//reserve Sets the buildings reserve display
            writer.putInt(0);//prosperity under maintenance (wtf is prosperity?)
            writer.putInt(10);
            writer.putFloat((float) .1);

            if (this.buttonUpgrade == 1) {
                if (building.getBlueprint() == null)
                    this.upgradeCost = Integer.MAX_VALUE;
                else if (building.getRank() == building.getBlueprint().getMaxRank())
                    this.upgradeCost = Integer.MAX_VALUE;
                else
                    this.upgradeCost = building.getBlueprint().getRankCost(Math.min(building.getRank() + 1, 7));

                writer.putInt(this.upgradeCost);
            } else
                writer.putInt(0);

            LocalDateTime uc = LocalDateTime.now();

            if (building.getDateToUpgrade() != null)
                uc = building.getDateToUpgrade();

            long timeLeft = uc.atZone(ZoneId.systemDefault())
                    .toInstant().toEpochMilli() - System.currentTimeMillis();
            long hour = timeLeft / 3600000;
            long noHour = timeLeft - (hour * 3600000);
            long minute = noHour / 60000;
            long noMinute = noHour - (minute * 60000);
            long second = noMinute / 1000;

            writer.put((byte) 0);//Has to do with repair time. A 1 here puts 23.9 hours in repair time A 2 here is 1.9 days
            writer.put((byte) 0);//unknown
            writer.putInt(0); //unknown

            if (LocalDateTime.now().isAfter(uc)) {
                writer.put((byte) 0);
                writer.put((byte) 0);
                writer.put((byte) 0);
            } else {
                writer.put((byte) (hour));
                writer.put((byte) minute);
                writer.put((byte) second);
            }

            if (timeLeft < 0)
                writer.putInt(0);
            else
                writer.putInt((int) timeLeft);

            writer.putInt((int) building.getCurrentHitpoints());
            writer.putInt((int) building.getMaxHitPoints());
            writer.putInt(BuildingManager.GetRepairCost(building));//sets the repair cost.
            writer.putInt(0);//unknown

            if (building.getBlueprint() == null)
                writer.putInt(0);
            else
                writer.putInt(building.getBlueprint().getSlotsForRank(building.getRank()));
            writer.put((byte) 1);//Has to do with removing update timer and putting in cost for upgrade

            writer.put(labelProtected); // 1 sets protection to invulnerable.
            writer.put(labelSiege);// 1 sets the protection under siege
            writer.put(labelCeaseFire); //0 with 1 set above sets to under siege // 1 with 1 set above sets protection status to under siege(cease fire)

            writer.put(buttonTransfer);// 1 enables the transfer asset button
            writer.put(buttonDestroy);// 1 enables the destroy asset button
            writer.put(buttonAbandon);// 1 here enables the abandon asset button
            writer.put(buttonUpgrade); //disable upgrade building

            if (building.getBlueprint() == null)
                writer.putInt(0);
            else
                writer.putInt(building.getBlueprint().getIcon()); //Symbol

            if (guild == null) {
                for (int i = 0; i < 3; i++)
                    writer.putInt(16);
                for (int i = 0; i < 2; i++)
                    writer.putInt(0);
            } else {
                GuildTag._serializeForDisplay(guild.getGuildTag(), writer);
                nation = guild.getNation();
            }

            if (nation == null) {
                for (int i = 0; i < 3; i++)
                    writer.putInt(16);
                for (int i = 0; i < 2; i++)
                    writer.putInt(0);
            } else {
                GuildTag._serializeForDisplay(nation.getGuildTag(), writer);
            }
            writer.putInt(0);
            writer.putInt(0);
            writer.putInt(0);//1 makes it so manage window does not open.

            if (!building.assetIsProtected() && !building.getProtectionState().equals(ProtectionState.PENDING)) {
                writer.putInt(0);
            } else {
                writer.putInt(1); //kos on/off?
                writer.putInt(3); // was 3
                if (zone.playerCityUUID != 0 && asset.assetIsProtected()) {
                    writer.putInt(GameObjectType.Building.ordinal());
                    writer.putInt(City.getCity(zone.playerCityUUID).getTOL().getObjectUUID());
                } else {
                    writer.putInt(0);
                    writer.putInt(0);
                }

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

                writer.putInt(targetType3);
                writer.putInt(targetID3);


                if (building.getProtectionState() == ProtectionState.PENDING)
                    writer.put((byte) 1); //Accept or decline.
                else
                    writer.put((byte) 0);

                if (building.taxType == TaxType.NONE)
                    writer.put((byte) 0); //? ??
                else if (building.taxDateTime != null)
                    writer.put((byte) 1);
                else
                    writer.put((byte) 0);

                writer.putString(""); //tree of life protection tax
                writer.putInt(0); //??
                writer.putInt(0); //??
                if (building.taxType == TaxType.NONE) {
                    writer.putInt(0);
                    writer.putInt(0);
                } else if (building.taxType == TaxType.WEEKLY) {
                    writer.putInt(building.taxAmount);
                    writer.putInt(0);
                } else {
                    writer.putInt(0);
                    writer.putInt(building.taxAmount);
                }


                writer.put(building.enforceKOS ? (byte) 1 : 0); //enforceKOS
                writer.put((byte) 0); //?
                writer.putInt(1);
            }


            ConcurrentHashMap<AbstractCharacter, Integer> npcList = building.getHirelings();
            writer.putInt(npcList.size());
            if (npcList.size() > 0) {
                for (AbstractCharacter npcHire : npcList.keySet()) {
                    writer.putInt(npcHire.getObjectType().ordinal());
                    if (npcHire.getObjectType() == GameObjectType.Mob)
                        writer.putInt(((Mob) npcHire).getDBID());
                    else
                        writer.putInt(npcHire.getObjectUUID());
                    if (npcHire.getObjectType() == GameObjectType.NPC) {
                        writer.putString(((NPC) npcHire).getContract().getName());
                        writer.putString(npcHire.getName());
                    } else if (npcHire.getObjectType() == GameObjectType.Mob) {
                        writer.putString(((Mob) npcHire).getContract().getName());
                        if (((Mob) npcHire).getNameOverride().length() > 0) {
                            writer.putString(((Mob) npcHire).getNameOverride());
                        } else {
                            writer.putString(npcHire.getName());
                        }
                    } else
                        writer.putString("Error: Nothing Here");
                    writer.putInt(npcHire.getRank());
                    writer.putInt(Blueprint.getNpcMaintCost(npcHire.getRank()));
                    if (npcHire.getObjectType() == GameObjectType.NPC)
                        writer.putInt(((NPC) npcHire).getContract().getIconID()); // Was 60
                    else if (npcHire.getObjectType() == GameObjectType.Mob)
                        writer.putInt(((Mob) npcHire).getContract().getIconID()); // Was 60

                    int contractID = 0;


                    if (npcHire.getObjectType() == GameObjectType.Mob)
                        contractID = ((Mob) npcHire).getContract().getContractID();
                    else if (npcHire.getObjectType() == GameObjectType.NPC)
                        contractID = ((NPC) npcHire).getContract().getContractID();

                    if (contractID == 830) {
                        writer.putInt(24580);
                    } else if (building.getBlueprint() != null && (building.getBlueprint().getBuildingGroup() == BuildingGroup.FORGE || building.getBlueprint().getBuildingGroup() == BuildingGroup.MAGICSHOP || building.getBlueprint().getBuildingGroup() == BuildingGroup.TAILOR)) {

                        writer.put((byte) 0);
                        writer.put((byte) 4);
                        writer.put((byte) 128);
                        writer.put((byte) 0);

                    } else {
                        writer.put((byte) 0);
                        if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK)
                            writer.put((byte) 1);
                        else
                            writer.put((byte) 0);
                        writer.put((byte) 0);
                        writer.put((byte) 0);
                    }


                    if (!npcHire.isAlive()) {
                        writer.put((byte) 1); // 1 SHOWs respawning
                        int respawnRemaining = (int) (((Mob) npcHire).deathTime + ((Mob) npcHire).spawnTime * 1000 - System.currentTimeMillis()) / 1000;
                        writer.putInt(respawnRemaining); // Seconds in respawn remaining.
                        writer.putInt(((Mob) npcHire).spawnTime); // max seconds for respawn
                    } else
                        writer.put((byte) 0);

                }
            }
        }
        if (this.actionType == 4) {
            writer.putInt(targetType);
            writer.putInt(targetID);
            Building building = BuildingManager.getBuildingFromCache(targetID);

            writer.putInt(preName01);
            writer.putString(building.getName()); //assetName
            writer.putString(building.getOwnerName()); //ownerName
            writer.putString(building.getGuild().getName());//guild name
            writer.putString(building.getCityName()); //City Name
            writer.putInt(building.getRank());
            if (building.getBlueprint() == null)
                writer.putInt(0);
            else
                writer.putInt(building.getBlueprint().getIcon());

            //tags
            GuildTag._serializeForDisplay(building.getGuild().getGuildTag(), writer);
            GuildTag._serializeForDisplay(building.getGuild().getNation().getGuildTag(), writer);

            writer.putInt(unknown14);
            writer.putInt(unknown15);
            writer.putInt(unknown16);
            writer.putInt(unknown17);
            writer.putInt(0); // previously uninitialized unknown18
        }
    }

    public int getRank() {
        return rank;
    }

    public void setRank(int rank) {
        this.rank = rank;
    }

    public int getSymbol() {
        return symbol;
    }

    public void setSymbol(int symbol) {
        this.symbol = symbol;
    }

    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 void setUnknown07(int unknown07) {
        this.unknown07 = unknown07;
    }

    public String getAssetName() {
        return assetName;
    }

    public void setAssetName(String AssetName) {
        this.assetName = AssetName;
    }

    public void setAssetName1(String AssetName1) {
        this.AssetName1 = AssetName1;
    }

    public String getCityName() {
        return CityName;
    }

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

    public int getStrongbox() {
        return strongbox;
    }

    public void setStrongbox(int strongbox) {
        this.strongbox = strongbox;
    }

    public int getBaneHour() {
        return baneHour;
    }

    public Building getAsset() {
        return asset;
    }

    public void setAsset(Building asset) {
        this.asset = asset;
    }

}

//Debug Info
//Run: Failed to make object TEMPLATE:135700 INSTANCE:1717987027141... (t=50.46) (r=7/4/2011 11:56:39)
//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:108760 INSTANCE:1717987027161... (t=50.46) (r=7/4/2011 11:56:39)
//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:108760 INSTANCE:1717987027177... (t=50.67) (r=7/4/2011 11:56:39)
//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:60040 INSTANCE:1717987027344... (t=50.87) (r=7/4/2011 11:56:39)
//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:3 INSTANCE:1717987027164... (t=50.88) (r=7/4/2011 11:56:39)