package electrosphere.net.server.protocol;

import java.util.LinkedList;
import java.util.List;

import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.state.inventory.RelationalInventoryState;
import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.game.data.crafting.RecipeData;
import electrosphere.game.data.crafting.RecipeIngredientData;
import electrosphere.game.data.item.Item;
import electrosphere.game.data.item.ItemDataMap;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.InventoryMessage;
import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.net.template.ServerProtocolTemplate;
import electrosphere.server.datacell.ServerDataCell;
import electrosphere.server.datacell.utils.DataCellSearchUtils;
import electrosphere.server.datacell.utils.EntityLookupUtils;
import electrosphere.server.player.PlayerActions;

/**
 * Server protocol for dealing with inventory messages
 */
public class InventoryProtocol implements ServerProtocolTemplate<InventoryMessage> {


    //the entity's equip inventory
    public static final int INVENTORY_TYPE_EQUIP = 0;
    //the natural inventory of the entity
    public static final int INVENTORY_TYPE_NATURAL = 1;
    //the toolbar
    public static final int INVENTORY_TYPE_TOOLBAR = 2;

    @Override
    public InventoryMessage handleAsyncMessage(ServerConnectionHandler connectionHandler, InventoryMessage message) {
        return message;
    }

    @Override
    public void handleSyncMessage(ServerConnectionHandler connectionHandler, InventoryMessage message) {
        Entity target;
        switch(message.getMessageSubtype()){
            case ADDITEMTOINVENTORY:
                LoggerInterface.loggerNetworking.DEBUG("[SERVER] ADD ITEM TO INVENTORY " + message.getentityId());
                target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
                if(target != null && InventoryUtils.hasNaturalInventory(target)){
                    InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
                }
                break;
            case REMOVEITEMFROMINVENTORY:
                LoggerInterface.loggerNetworking.DEBUG("[SERVER] REMOVE ITEM FROM INVENTORY " + message.getentityId());
                target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
                if(target != null && InventoryUtils.hasNaturalInventory(target)){
                    InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
                }
                break;
            case CLIENTREQUESTEQUIPITEM:
                LoggerInterface.loggerNetworking.DEBUG("[SERVER] REQUEST EQUIP ITEM " + message.getentityId());
                target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
                if(target != null && InventoryUtils.hasNaturalInventory(target)){
                    InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
                }
                break;
            case CLIENTREQUESTUNEQUIPITEM:
                LoggerInterface.loggerNetworking.DEBUG("[SERVER] REQUEST UNEQUIP ITEM " + message.getentityId());
                target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
                if(target != null && InventoryUtils.hasNaturalInventory(target)){
                    InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
                }
                break;
            case CLIENTREQUESTPERFORMITEMACTION: {
                PlayerActions.attemptPlayerAction(connectionHandler, message);
            } break;
            case CLIENTREQUESTADDTOOLBAR: {
                target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
                if(target != null && InventoryUtils.hasToolbarInventory(target)){
                    InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
                }
            } break;
            case CLIENTREQUESTADDNATURAL: {
                target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
                if(target != null && InventoryUtils.hasNaturalInventory(target)){
                    InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
                }
            } break;
            case CLIENTUPDATETOOLBAR: {
                target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
                if(target != null && InventoryUtils.hasToolbarInventory(target)){
                    InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
                }
            } break;
            case CLIENTREQUESTCRAFT: {
                target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
                Entity workshopEntity = EntityLookupUtils.getEntityById(message.getstationId());
                RecipeData recipe = Globals.gameConfigCurrent.getRecipeMap().getType(message.getrecipeId());
                if(target != null && recipe != null){
                    this.attemptCraft(target,workshopEntity,recipe);
                    // System.out.println(message.getentityId() + " " + message.getstationId() + " " + message.getrecipeId());
                    // InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
                }
            } break;
            case SERVERCOMMANDUNEQUIPITEM:
            case SERVERCOMMANDMOVEITEMCONTAINER:
            case SERVERCOMMANDEQUIPITEM:
                //silently ignore
                break;
        }
    }


    /**
     * Attempts to craft an item
     * @param crafter The entity performing the crafting
     * @param station The (optional) station for crafting
     * @param recipe The recipe to craft
     */
    private void attemptCraft(Entity crafter, Entity station, RecipeData recipe){
        if(InventoryUtils.serverGetInventoryState(crafter) == null){
            return;
        }
        UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(crafter);
        RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(crafter);

        //get data obj
        ItemDataMap itemMap = Globals.gameConfigCurrent.getItemMap();

        //get reagents
        List<Entity> reagentList = new LinkedList<Entity>();
        boolean hasReagents = true;
        //find the reagents we're going to use to craft
        for(RecipeIngredientData ingredient : recipe.getIngredients()){
            int found = 0;
            if(naturalInventory != null){
                for(Entity itemEnt : naturalInventory.getItems()){
                    if(itemMap.getItem(itemEnt).getId().matches(ingredient.getItemType())){
                        found++;
                        reagentList.add(itemEnt);
                    }
                    if(found >= ingredient.getCount()){
                        break;
                    }
                }
            }
            if(toolbarInventory != null){
                for(Entity itemEnt : toolbarInventory.getItems()){
                    if(itemEnt == null){
                        continue;
                    }
                    if(itemMap.getItem(itemEnt).getId().matches(ingredient.getItemType())){
                        found++;
                        reagentList.add(itemEnt);
                    }
                    if(found >= ingredient.getCount()){
                        break;
                    }
                }
            }
            if(found < ingredient.getCount()){
                hasReagents = false;
                break;
            }
        }
        if(hasReagents){
            for(Entity reagentEnt : reagentList){
                if(naturalInventory != null){
                    naturalInventory.removeItem(reagentEnt);
                }
                if(toolbarInventory != null){
                    toolbarInventory.tryRemoveItem(reagentEnt);
                }
                this.deleteItemInInventory(crafter, reagentEnt);
            }
            for(RecipeIngredientData product : recipe.getProducts()){
                Item productType = itemMap.getItem(product.getItemType());
                if(productType == null){
                    throw new Error("Could not locate product definition! " + productType + " " + product.getItemType());
                }
                for(int i = 0; i < product.getCount(); i++){
                    ItemUtils.serverCreateContainerItem(crafter, productType);
                }
            }
        }
    }


    /**
     * Delete an item that is in an inventory
     * @param parent The parent entity that contains the in-inventory item
     * @param itemEnt The item that is in an inventory
     */
    private void deleteItemInInventory(Entity parent, Entity inInventory){
        //the parent entity's data cell
        ServerDataCell dataCell = DataCellSearchUtils.getEntityDataCell(parent);
        //broadcast destroy entity
        dataCell.broadcastNetworkMessage(InventoryMessage.constructremoveItemFromInventoryMessage(inInventory.getId()));
        //remove the inventories
        UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(parent);
        RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(parent);
        if(naturalInventory != null){
            naturalInventory.removeItem(inInventory);
        }
        if(toolbarInventory != null){
            toolbarInventory.tryRemoveItem(inInventory);
        }
    }

}
