package engine.net.client.handlers;

import engine.exception.MsgSendException;
import engine.gameManager.*;
import engine.math.Bounds;
import engine.math.Vector3fImmutable;
import engine.mbEnums;
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 handles
 * items in the character's inventory which are used.  Potions, Deeds, etc.
 */

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.charItemManager;

        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;
            }

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

                int uuid = item.templateID;

                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, mbEnums.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() != mbEnums.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 == mbEnums.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
                        LootManager.peddleFate(player, item);
                        break;

                    case BUCKET: //water bucket
                    case POTION: //potions, tears of saedron
                    case SCROLL: //runes, petition, warrant, scrolls
                        if (uuid > 680069 && uuid < 680074) //Handle Charter, Deed, Petition, Warrant here
                            break;

                        if (item.template.item_bane_rank > 0) {

                            // Only one banestone at a time

                            lock.writeLock().lock();

                            try {
                                if (Bane.summonBanestone(player, origin, item.template.item_bane_rank) == true)
                                    itemMan.consume(item);
                            } finally {
                                lock.writeLock().unlock();
                            }
                            break;
                        }

                        if (uuid == 910010) { //tears of saedron
                            if (comps.size() > 1)
                                AbstractCharacter.removeRune(player, origin, comps.get(1).intValue());
                            break;
                        }

                        if (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 instanceof AbstractWorldObject) {
                                        target = (AbstractWorldObject) tarAgo;
                                    }
                                }
                            }

                            // Bypass for waterbuckets

                            if (item.template.item_type.equals(mbEnums.ItemType.BUCKET)) {

                                // test for valid target type
                                if (target.getObjectType() == mbEnums.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_grant.keySet().iterator().next();
                                PowersBase powerAction = PowersManager.powersBaseByIDString.get(powerString);
                                int powerValue = item.template.item_power_grant.get(powerString);
                                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, mbEnums.DispatchChannel.SECONDARY);
                        player.cancelOnSpell();
                        break;
                    case RUNE:
                        ApplyRuneMsg.applyRune(uuid,origin,player);
                        itemMan.consume(item);
                        break;
                    default: //shouldn't be here, consume item
                        dispatch = Dispatch.borrow(player, msg);
                        DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.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;
    }

}