package ch.zhaw.catan;

import java.util.HashMap;
import java.util.List;

/**
 * This class is here in order to maintain the resources of the players, the amount of structures that they can build.
 * And check if a player holds enough resources to build.
 *
 * @author Leonardo Brandenberger, Roman Schenk, Andrin Fassbind, Stefan Amador
 */
public class Player {

    /**
     * faction: The faction of the player
     * resources: The resources the player owns
     * structureToUse: The structures a player can build.
     */
    private final Config.Faction faction;
    private final HashMap<Config.Resource, Integer> resources;
    private final HashMap<Config.Structure, Integer> structureToUse;

    /**
     * The constructor initializes the faction and the Hashmaps resources and structureToUse.
     *
     * @param faction faction of the player.
     */
    public Player(Config.Faction faction) {
        //Data fields
        this.faction = faction;
        //Initialize available structures
        structureToUse = new HashMap<>();
        structureToUse.put(Config.Structure.ROAD, Config.Structure.ROAD.getStockPerPlayer());
        structureToUse.put(Config.Structure.SETTLEMENT, Config.Structure.SETTLEMENT.getStockPerPlayer());
        structureToUse.put(Config.Structure.CITY, Config.Structure.CITY.getStockPerPlayer());
        //Initialize resources
        resources = new HashMap<>();
        for (Config.Resource resource : Config.Resource.values()) {
            resources.put(resource, 0);
        }
    }

    /**
     * This method returns all the resources the player has at the moment.
     *
     * @return HashMap with the count of every resource
     */
    public HashMap<Config.Resource, Integer> getResources() {
        return resources;
    }

    /**
     * This method returns the player's faction.
     *
     * @return the faction of the player.
     */
    public Config.Faction getFaction() {
        return faction;
    }

    /**
     * This method returns the amount of a specific resource that a player owns.
     *
     * @param resource the resource that is needed
     * @return the amount of the specific resource
     */
    public int getSpecificResource(Config.Resource resource) {
        return resources.get(resource);
    }

    /**
     * This method adds a specific resource to resources of the player.
     *
     * @param resource    to add
     * @param numberToAdd how much to add
     */
    public void addResource(Config.Resource resource, int numberToAdd) {
        resources.put(resource, resources.get(resource) + numberToAdd);
    }

    /**
     * This method subtracts a specific resource from the player's resources. but check first if player has the specific resources.
     *
     * @param resource         to subtract
     * @param numberToSubtract how much to subtract
     * @return true if resource has been subtracted false if player has not enough resources
     */
    public boolean subtractResource(Config.Resource resource, int numberToSubtract) {
        int inPossession = resources.get(resource);
        if (inPossession - numberToSubtract < 0) {
            return false;
        }
        resources.put(resource, inPossession - numberToSubtract);
        return true;
    }

    /**
     * This method has to be used when a player wants to build a structure. It checks if a player has enough of the specific structure
     * and resources to build . If the player is able to build, this method subtracts the building cost from the resources
     * in possession by the player.
     * It reduces the amount of the specific structure a player can build by 1.
     *
     * @return true if the structure can be created false if the structure can't be created.
     */
    public boolean build(Config.Structure structure) {
        List<Config.Resource> costs = structure.getCosts();
        if (structureToUse.get(structure) < 1 || !checkResourceToBuild(costs)) {
            return false;
        }
        structureToUse.put(structure, structureToUse.get(structure) - 1);
        return true;
    }

    /**
     * This method checks the amount of resources a player has in order to determine if he haas enough
     * resources to build a new structure.
     *
     * @param list the list that shows how many resources are required.
     * @return true if the player has enough resources to build and return false if he doesn't.
     */
    private boolean checkResourceToBuild(List<Config.Resource> list) {
        HashMap<Config.Resource, Integer> costs = new HashMap<>();
        for (Config.Resource resource : list) {
            if (costs.containsKey(resource)) {
                costs.put(resource, costs.get(resource) + 1);
            } else {
                costs.put(resource, 1);
            }
        }
        for (Config.Resource resource : costs.keySet()) {
            if (resources.get(resource) < costs.get(resource)) {
                return false;
            }
        }
        return true;
    }
}
