StationSystem.java
package com.devcharles.piazzapanic.componentsystems;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import com.badlogic.ashley.core.Engine;
import com.badlogic.ashley.core.Entity;
import com.badlogic.ashley.core.Family;
import com.badlogic.ashley.systems.IteratingSystem;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.devcharles.piazzapanic.GameScreen;
import com.devcharles.piazzapanic.components.ControllableComponent;
import com.devcharles.piazzapanic.components.FoodComponent;
import com.devcharles.piazzapanic.components.PlayerComponent;
import com.devcharles.piazzapanic.components.StationComponent;
import com.devcharles.piazzapanic.components.TintComponent;
import com.devcharles.piazzapanic.components.CookingComponent;
import com.devcharles.piazzapanic.components.FoodComponent.FoodType;
import com.devcharles.piazzapanic.input.KeyboardInput;
import com.devcharles.piazzapanic.scene2d.Hud;
import com.devcharles.piazzapanic.utility.EntityFactory;
import com.devcharles.piazzapanic.utility.Difficulty;
import com.devcharles.piazzapanic.utility.Mappers;
import com.devcharles.piazzapanic.utility.Station;
import com.devcharles.piazzapanic.utility.Station.StationType;
import com.devcharles.piazzapanic.utility.WorldTilemapRenderer;
import static com.devcharles.piazzapanic.utility.Station.StationType.oven;
/**
 * This system manages player-station interaction and station food processing.
 */
public class StationSystem extends IteratingSystem {
    KeyboardInput input;
    boolean interactingStation = false;
    EntityFactory factory;
    WorldTilemapRenderer mapRenderer;
    Hud hud;
    private GameScreen gameScreen;
    private TintComponent readyTint;
    private float tickAccumulator = 0;
    private final Float[] tillBalance;
    private Difficulty difficulty;
    public Integer timer = 15;
    public StationSystem(KeyboardInput input, EntityFactory factory, WorldTilemapRenderer mapRenderer,
            Float[] tillBalance, Hud hud, Difficulty difficulty, GameScreen gameScreen) {
        super(Family.all(StationComponent.class).get());
        this.input = input;
        this.factory = factory;
        this.mapRenderer = mapRenderer;
        this.tillBalance = tillBalance;
        this.hud = hud;
        this.difficulty = difficulty;
        this.gameScreen = gameScreen;
    }
    @Override
    public void update(float deltaTime) {
        tickAccumulator += deltaTime;
        super.update(deltaTime);
        if (tickAccumulator > 0.5f) {
            tickAccumulator -= 0.5f;
        }
    }
    @Override
    protected void processEntity(Entity entity, float deltaTime) {
        StationComponent station = Mappers.station.get(entity);
        stationTick(station, deltaTime);
        if (station.interactingCook != null) {
            PlayerComponent player = Mappers.player.get(station.interactingCook);
            if (player == null) {
                return;
            }
            if (player.putDown) {
                player.putDown = false;
                ControllableComponent controllable = Mappers.controllable.get(station.interactingCook);
                switch (station.type) {
                    case ingredient:
                        controllable.currentFood.pushItem(factory.createFood(station.ingredient),
                                station.interactingCook);
                        System.out.println(station.ingredient);
                        break;
                    case bin:
                        processBin(controllable);
                        break;
                    case serve:
                        processServe(station.interactingCook);
                        break;
                    default:
                        processStation(controllable, station);
                        break;
                }
            } else if (player.pickUp) {
                player.pickUp = false;
                ControllableComponent controllable = Mappers.controllable.get(station.interactingCook);
                switch (station.type) {
                    case ingredient:
                        controllable.currentFood.pushItem(factory.createFood(station.ingredient),
                                station.interactingCook);
                        break;
                    case bin:
                    case serve:
                        break;
                    default:
                        stationPickup(station, controllable);
                        break;
                }
            } else if (player.interact) {
                player.interact = false;
                interactStation(station);
            }
        }
    }
    /**
     * Try and process the food from the player.
     */
    private void processStation(ControllableComponent controllable, StationComponent station) {
        if (station.locked) {
            tryBuy(station);
            return;
        }
        if (controllable.currentFood.isEmpty()) {
            return;
        }
        Gdx.app.log("putDown", Mappers.food.get(controllable.currentFood.peek()).type.name());
        FoodComponent food = Mappers.food.get(controllable.currentFood.peek());
        HashMap<FoodType, FoodType> recipes = Station.recipeMap.get(station.type);
        if (recipes == null) {
            return;
        }
        FoodType result = recipes.get(food.type);
        if (result == null) {
            return;
        }
        int foodIndex = station.food.indexOf(null);
        // If there is space on the station
        if (foodIndex != -1) {
            // Pop if off player stack
            // Store in station
            station.food.set(foodIndex, controllable.currentFood.pop());
        } else {
            return;
        }
        // success
        CookingComponent cooking = getEngine().createComponent(CookingComponent.class);
        cooking.timer.start();
        // Flag the food as processed if InstaCook is active
        if (gameScreen.InstaCook) {
            cooking.processed = true;
        }
        station.food.get(foodIndex).add(cooking);
        Gdx.app.log("Food processed", String.format("%s turned into %s", food.type, result));
        // If the station is an oven start the cooking animation.
        if (station.type == oven) {
            mapRenderer.animateOven(station.tileMapPosition);
        }
    }
    /**
     * Perform special action (flipping patties, etc.)
     * 
     * @param station the station the action is being performed on.
     */
    private void interactStation(StationComponent station) {
        for (Entity food : station.food) {
            if (food == null || !Mappers.cooking.has(food)) {
                continue;
            }
            CookingComponent cooking = Mappers.cooking.get(food);
            // Check if it's ready without ticking the timer
            boolean ready = cooking.timer.tick(0);
            // Make the food ready if the InstaCook powerup is active
            if (gameScreen.InstaCook) {
                ready = true;
                return;
            }
            if (cooking.processed) {
                food.remove(TintComponent.class);
                return;
            }
            if (ready && !cooking.processed) {
                food.remove(TintComponent.class);
                cooking.processed = true;
                cooking.timer.reset();
                return;
            }
        }
    }
    /**
     * Try to combine the ingredients at the top of the player's inventory stack
     * (max 3) into a ready meal.
     * 
     * @param cook the cook whos inventory is being used for creating the food.
     */
    private void processServe(Entity cook) {
        ControllableComponent controllable = Mappers.controllable.get(cook);
        if (controllable.currentFood.size() < 2) {
            return;
        }
        int count = 2;
        FoodType result = tryAssemble(controllable, count);
        if (result == null) {
            result = tryAssemble(controllable, ++count);
            if (result == null) {
                return;
            }
        }
        for (int i = 0; i < count; i++) {
            Entity e = controllable.currentFood.pop();
            getEngine().removeEntity(e);
        }
        controllable.currentFood.pushItem(factory.createFood(result), cook);
    }
    /**
     * Attempt to create a food.
     * 
     * @param count number of ingredients to combine
     */
    private FoodType tryAssemble(ControllableComponent controllable, int count) {
        Set<FoodType> ingredients = new HashSet<FoodType>();
        int i = 0;
        for (Entity foodEntity : controllable.currentFood) {
            if (i > count - 1) {
                break;
            }
            ingredients.add(Mappers.food.get(foodEntity).type);
            i++;
        }
        return Station.assembleRecipes.get(ingredients);
    }
    /**
     * Destroy the top food in the inventory of a cook.
     */
    private void processBin(ControllableComponent controllable) {
        if (controllable.currentFood.isEmpty()) {
            return;
        }
        Entity e = controllable.currentFood.pop();
        getEngine().removeEntity(e);
    }
    /**
     * Pick up ready food from a station
     */
    private void stationPickup(StationComponent station, ControllableComponent controllable) {
        for (Entity foodEntity : station.food) {
            if (foodEntity != null && !Mappers.cooking.has(foodEntity)) {
                if (controllable.currentFood.pushItem(foodEntity, station.interactingCook)) {
                    station.food.set(station.food.indexOf(foodEntity), null);
                    Mappers.transform.get(foodEntity).scale.set(1, 1);
                    Gdx.app.log("Picked up", Mappers.food.get(foodEntity).type.toString());
                }
                return;
            }
        }
    }
    /**
     * Cook the food in the station. This progresses the timer in the food being
     * cooked in the station.
     * 
     * @param station
     * @param deltaTime
     */
    private void stationTick(StationComponent station, float deltaTime) {
        if (station.type == StationType.cutting_board && station.interactingCook == null) {
            return;
        }
        for (Entity foodEntity : station.food) {
            if (foodEntity == null || !Mappers.cooking.has(foodEntity)) {
                continue;
            }
            CookingComponent cooking = Mappers.cooking.get(foodEntity);
            boolean ready = cooking.timer.tick(deltaTime);
            if (gameScreen.InstaCook) {
                ready = true;
            }
            if (ready && cooking.processed) {
                cooking.timer.stop();
                cooking.timer.reset();
                switch (station.type) {
                    case cutting_board:
                        gameScreen.audio.playChop();
                        break;
                    case grill:
                        gameScreen.audio.playSizzle();
                        break;
                    case oven:
                        gameScreen.audio.playDing();
                        break;
                    case ingredient:
                        gameScreen.audio.playTap();
                        break;
                    case serve:
                        gameScreen.audio.playTap();
                        break;
                    default:
                        break;
                }
                FoodComponent food = Mappers.food.get(foodEntity);
                // Process the food into its next form
                food.type = Station.recipeMap.get(station.type).get(food.type);
                Mappers.texture.get(foodEntity).region = EntityFactory.getFoodTexture(food.type);
                foodEntity.remove(CookingComponent.class);
                Gdx.app.log("Food ready", food.type.name());
                // If the station is an oven turn off the animation.
                if (station.type == oven) {
                    mapRenderer.removeOvenAnimation(station.tileMapPosition);
                }
            } else if (ready) {
                if (tickAccumulator > 0.5f) {
                    if (!Mappers.tint.has(foodEntity)) {
                        foodEntity.add(readyTint);
                    } else {
                        foodEntity.remove(TintComponent.class);
                    }
                }
            }
        }
    }
    /**
     * Unlocks the current station if the player is in endless mode and has enough
     * money.
     * 
     * @param station The current station component with details about the current
     *                station.
     */
    public void tryBuy(StationComponent station) {
        // TODO sound effect for success or failure.
        // TODO set price for new stations.
        if (difficulty == Difficulty.SCENARIO) {
            hud.displayInfoMessage("You can only unlock new stations in endless mode");
            return;
        }
        float priceOfNewStation = 5;
        if (tillBalance[0] - priceOfNewStation < 0) {
            hud.displayInfoMessage("Insufficient funds - Each station costs $" + priceOfNewStation);
        } else {
            mapRenderer.unlockStation(station.tileMapPosition, station.type.getValue());
            tillBalance[0] -= priceOfNewStation;
            station.locked = false;
            hud.displayInfoMessage("New station unlocked!");
        }
    }
    @Override
    public void addedToEngine(Engine engine) {
        super.addedToEngine(engine);
        readyTint = getEngine().createComponent(TintComponent.class);
        readyTint.tint = Color.ORANGE;
    }
}