package engine.net.client.handlers;

import engine.Enum;
import engine.Enum.ItemType;
import engine.exception.MsgSendException;
import engine.gameManager.*;
import engine.math.Bounds;
import engine.math.Vector3fImmutable;
import engine.net.Dispatch;
import engine.net.DispatchMessage;
import engine.net.client.ClientConnection;
import engine.net.client.msg.*;
import engine.objects.*;
import engine.powers.PowersBase;

import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/*
 * @Author:
 * @Summary: Processes application protocol message which actives
 * items such as charters and deeds in the character's inventory
 */
public class ObjectActionMsgHandler extends AbstractClientMsgHandler {

    // Reentrant lock for dropping banes

    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public ObjectActionMsgHandler() {
        super(ObjectActionMsg.class);
    }
    @Override
    protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException {

        // Member variable declaration
        ObjectActionMsg msg;
        PlayerCharacter player;
        CharacterItemManager itemMan;
        ArrayList<Long> comps;
        Dispatch dispatch;
        boolean waterbucketBypass = false;

        // Member variable assignment
        msg = (ObjectActionMsg) baseMsg;
        player = SessionManager.getPlayerCharacter(origin);

        if (player == null) {
            return true;
        }

        itemMan = player.getCharItemManager();

        if (itemMan == null) {
            return true;
        }

        comps = msg.getTargetCompID();

        if (comps.isEmpty()) {
            return true;
        }

        long comp = comps.get(0);

        if (((int) comp) != 0) {
            Item item = Item.getFromCache((int) comp);

            if (item == null) {
                return true;
            }

            //dupe check
            if (!item.validForInventory(origin, player, itemMan)) {
                return true;
            }

            ItemBase ib = item.getItemBase();

            if (ib == null) {
                return true;
            }

            if (itemMan.doesCharOwnThisItem(item.getObjectUUID())) {

                if (ib.isConsumable() || item.template.item_type.equals(ItemType.REAGENT)) {

                    int uuid = item.templsteID;

                    switch (item.template.item_type) {

                        case DEED: //buildings
                            //Call add building screen here, ib.getUseID() get's building ID

                            //if inside player city, center loc on tol. otherwise center on player.
                            Vector3fImmutable loc = player.getLoc();
                            Zone zone = ZoneManager.findSmallestZone(player.getLoc());

                            if (zone != null) {
                                if (zone.guild_zone) {
                                    loc = zone.getLoc();
                                }
                            }

                            PlaceAssetMsg pam = new PlaceAssetMsg();
                            pam.setActionType(2);
                            pam.setContractID(item.getObjectUUID());
                            pam.setX(loc.getX() + 64); //offset grid from tol
                            pam.setY(loc.getY());
                            pam.setZ(loc.getZ() + 64); //offset grid from tol
                            pam.addPlacementInfo(item.template.deed_structure_id);

                            dispatch = Dispatch.borrow(player, pam);
                            DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);

                            //itemMan.consume(item); //temporary fix for dupe.. TODO Make Item Unusable after This message is sent.
                            break;
                        case FURNITUREDEED: //furniture
                            //Call add furniture screen here. ib.getUseID() get's furniture ID
                            break;
                        case OFFERING:
                            long shrineCompID = comps.get(1);
                            Building shrineBuilding = BuildingManager.getBuilding((int) shrineCompID);
                            if (shrineBuilding == null) {
                                return true;
                            }
                            if (shrineBuilding.getBlueprint() != null && shrineBuilding.getBlueprint().getBuildingGroup() != engine.Enum.BuildingGroup.SHRINE) {
                                return true;
                            }

                            if (shrineBuilding.getRank() == -1) {
                                return true;
                            }
                            Shrine shrine = Shrine.shrinesByBuildingUUID.get(shrineBuilding.getObjectUUID());

                            if (shrine == null) {
                                return true;
                            }

                            if (shrine.addFavor(player, item)) {
                                shrineBuilding.addEffectBit(1000000 << 2);
                                shrineBuilding.updateEffects();
                                shrineBuilding.removeEffectBit(1000000 << 2);
                            }
                            break;

                        case REALMCHARTER:
                            int charterType = 0;
                            switch (uuid) {
                                case 910020:
                                    charterType = 762228431;
                                    break;
                                case 910021:
                                    charterType = -15978914;
                                    break;
                                case 910022:
                                    charterType = -600065291;
                                    break;
                            }
                            if (Realm.claimRealm(player, charterType) == true) {
                                itemMan.consume(item);
                            }
                            break;
                        case WAND: //rod of command
                            long compID = comps.get(1);

                            int objectType = AbstractWorldObject.extractTypeID(compID).ordinal();
                            Mob toCommand;
                            if (objectType == engine.Enum.GameObjectType.Mob.ordinal()) {
                                toCommand = Mob.getFromCache((int) compID);
                            } //Only Command Mob Types.
                            else {
                                return true;
                            }

                            if (toCommand == null) {
                                return true;
                            }

                            if (!toCommand.isSiege())
                                return true;

                            if (player.commandSiegeMinion(toCommand)) {
                                itemMan.consume(item);
                            }
                            break;
                        //ANNIVERSERY GIFT
                        case TREASURE:
                            // *** Disabled for now: Needs bootyset created

                            //if (ib.getUUID() == 971012) {
                            //    int random = ThreadLocalRandom.current().nextInt(ItemBase.AnniverseryGifts.size());
                            //    int annyID = ItemBase.AnniverseryGifts.get(random);

                            //    ItemBase annyIB = ItemBase.getItemBase(annyID);
                            //    if (annyIB != null) {
                            //        Item gift = MobLoot.createItemForPlayer(player, annyIB);
                            //        if (gift != null) {
                            //            itemMan.addItemToInventory(gift);
                            //            itemMan.consume(item);
                            //        }
                            //    }
                            //    break;
                            //}

                            LootManager.peddleFate(player, item);
                            break;

                        case BUCKET: //water bucket
                        case POTION: //potions, tears of saedron

                        case SCROLL: //runes, petition, warrant, scrolls
                            if (uuid > 3000 && uuid < 3050) { //Discipline Runes
                                if (ApplyRuneMsg.applyRune(uuid, origin, player)) {
                                    itemMan.consume(item);
                                }
                                break;
                            } else if (uuid > 249999 && uuid < 250123) { //stat and mastery runes
                                if (ApplyRuneMsg.applyRune(uuid, origin, player)) {
                                    itemMan.consume(item);
                                }
                                break;
                            } else if (uuid > 250114 && uuid < 250123) { //mastery runes
                                if (ApplyRuneMsg.applyRune(uuid, origin, player)) {
                                    itemMan.consume(item);
                                }
                                break;
                            } else if (uuid > 252122 && uuid < 252128) { //mastery runes
                                if (ApplyRuneMsg.applyRune(uuid, origin, player)) {
                                    itemMan.consume(item);
                                }
                                break;
                            } else if (uuid > 680069 && uuid < 680074) //Handle Charter, Deed, Petition, Warrant here
                            {
                                break;
                            } else if (uuid > 910010 && uuid < 910019) {

                                int rank = uuid - 910010;

                                if (rank < 1 || rank > 8) {
                                    ChatManager.chatSystemError(player, "Invalid Rank for bane scroll!");
                                    return true;
                                }
                                // Only one banestone at a time
                                lock.writeLock().lock();

                                try {
                                    if (Bane.summonBanestone(player, origin, rank) == true)
                                        itemMan.consume(item);
                                } finally {
                                    lock.writeLock().unlock();
                                }
                                break;
                            } else if (uuid == 910010) { //tears of saedron
                                if (comps.size() > 1) {
                                    AbstractCharacter.removeRune(player, origin, comps.get(1).intValue());
                                }
                                break;
                            } else if ((byte) item.chargesRemaining > 0) {
                                ArrayList<Long> tarList = msg.getTargetCompID();
                                AbstractWorldObject target = player;
                                if (tarList.size() > 1) {
                                    long tarID = tarList.get(1);
                                    if (tarID != 0) {
                                        AbstractGameObject tarAgo = AbstractGameObject.getFromTypeAndID(tarID);
                                        if (tarAgo != null && tarAgo instanceof AbstractWorldObject) {
                                            target = (AbstractWorldObject) tarAgo;
                                        }
                                    }
                                }

                                // Bypass for waterbuckets

                                // test character targeted

                                if (ib.getUUID() == 910005) {

                                    // test for valid target type
                                    if (target.getObjectType() == Enum.GameObjectType.PlayerCharacter)
                                        waterbucketBypass = true;
                                    else {
                                        // test distance to structure
                                        Building targetBuilding = (Building) target;
                                        Bounds testBounds = Bounds.borrow();
                                        testBounds.setBounds(player.getLoc(), 25);

                                        if (Bounds.collide(targetBuilding.getBounds(), testBounds, .1f) == false) {
                                            ChatManager.chatSystemError(player, "Not in range of structura for to heal!");
                                            return true;
                                        }
                                    }

                                    // Send piss bucket animation

                                    VisualUpdateMessage vum = new VisualUpdateMessage(player, 16323);
                                    vum.configure();
                                    DispatchMessage.sendToAllInRange(player, vum);
                                }

                                if (waterbucketBypass == false) {
                                    String powerString = item.template.item_power_action.keySet().iterator().next();
                                    PowersBase powerAction = PowersManager.powersBaseByIDString.get(powerString);
                                    int powerValue = item.template.item_power_action.get(powerString)[0];
                                    PowersManager.applyPower(player, target, Vector3fImmutable.ZERO, powerAction.getToken(), powerValue, true);
                                }
                                itemMan.consume(item);
                            } else //just remove the item at this point
                                itemMan.consume(item);

                            dispatch = Dispatch.borrow(player, msg);
                            DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);
                            player.cancelOnSpell();
                            break;
                        default: //shouldn't be here, consume item
                            dispatch = Dispatch.borrow(player, msg);
                            DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);
                            // itemMan.consume(item);
                    }
                }
            } else {
                // TODO log item does not belong to player
                // System.out.println("Item does not belong to player");
                // Cleanup duped item here
            }
        }

        return true;
    }

}