package it.polimi.ingsw.gc14.Model;

import it.polimi.ingsw.gc14.Model.Cards.BuildingCard;
import it.polimi.ingsw.gc14.Model.Cards.TribeCards.CharacterType;
import it.polimi.ingsw.gc14.Model.Cards.TribeCards.Characters.*;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * Default Player class; contains all identifiers and methods needed.
 */
public class Player implements Serializable {
    /**
     * The maximum length allowed for the username string.
     */
    private static final int MAX_VALUE = 32;

    // region Getters
    /**
     * Unique string identifier for players; must not be {@code null} and must not
     * exceed {@link #MAX_VALUE} otherwise an {@link IllegalArgumentException} will be thrown
     * when the {@link #Player(String) constructor} is called.
     */
    private final String UserName;

    /**
     * Getter for the private attribute {@link #UserName}.
     * @return {@code String} - The UserName
     */
    public String getUserName() {
        return UserName;
    }

    /**
     * Counts the total {@link it.polimi.ingsw.gc14.Model.Cards.TribeCards.Character Character Cards}
     * the player currently possesses.
     * @return {@code int} - Total Character Cards.
     */
    public int getTotCharacters() {
        return Arrays.stream(CharacterType.values())
                .mapToInt(this::getNType)
                .sum();
    }

    /**
     * Counts the total number of cards of the specified {@link it.polimi.ingsw.gc14.Model.Cards.TribeCards.CharacterType Character Type}.
     * Character Type must not be {@code null}
     * @param type the Character Type to be Counted.
     * @return {@code int} - Total number of cards of given {@code type}.
     */
    public int getNType(CharacterType type) {
        return switch (type) {
            case ARTIST -> artists.size();
            case INVENTOR -> inventors.size();
            case HUNTER -> hunters.size();
            case BUILDER -> builders.size();
            case SHAMAN -> shamans.size();
            case GATHERER -> gatherers.size();
        };
    }
    /**
     * The List containing all {@link BuildingCard Building Cards} the player currently possesses.
     */
    public ArrayList <BuildingCard> buildingCards;

    /**
     * The List containing all {@link Artist Artist Character Cards} the player currently possesses.
     */
    public ArrayList <Artist> artists;

    /**
     * The List containing all {@link Builder Builder Character Cards} the player currently possesses.
     */
    public ArrayList <Builder> builders;

    /**
     * The List containing all {@link Inventor Inventor Character Cards} the player currently possesses.
     */
    public ArrayList <Inventor> inventors;

    /**
     * The List containing all {@link Gatherer Gatherer Character Cards} the player currently possesses.
     */
    public ArrayList <Gatherer>  gatherers;

    /**
     * The List containing all {@link Shaman Shaman Character Cards} the player currently possesses.
     */
    public ArrayList <Shaman> shamans;

    /**
     * The List containing all {@link Hunter Hunter Character Cards} the player currently possesses.
     */
    public ArrayList <Hunter> hunters;

    /**
     * The current amount of {@code Food} the Player possesses.
     * Cannot be negative.
     */
    private int FoodValue;

    /**
     * Getter for the private attribute {@link #FoodValue}.
     * @return {@code int} - The amount of Food the player possesses.
     */
    public int getFoodValue() {
        return FoodValue;
    }

    /**
     * The current amount of {@code Prestige} the Player possesses.
     */
    private int PrestigeValue;

    /**
     * Getter for the private attribute {@link #PrestigeValue}.
     * @return {@code int} - The amount of Prestige the Player possesses.
     */
    public int getPrestigeValue() {
        return PrestigeValue;
    }

    /* TODO
    * private Game game;
    * public Game getGame(){
    *   return Game;
    * }
    */
    // endregion getters

    // region Setters

    /**
     * Adds {@code Value} amount of {@code Food} to the Player.
     * @param Value The amount of {@code Food} to be added.
     *              Should be positive for expected results
     *              (otherwise the method will subtract the absolute
     *              value of {@code Value}).
     * @see #FoodValue
     */
    public void addFood(int Value){
        this.FoodValue += Value;
    }

    /**
     * Removes {@code Value} amount of {@code Food} from the Player.
     * Note: {@link #FoodValue} cannot be negative, so the method will
     * return {@code False} if {@code Value} is greater than the
     * amount of {@code Food} the Player possesses, {@code False} otherwise.
     * @param Value The amount of {@code Food} to be removed.
     *              Should be positive for expected results
     *              (otherwise the method will add the absolute
     *              value of {@code Value}).
     * @return {@code Boolean} - {@code True} if {@code Value} is greater than
     * the amount of {@code Food} the Player possesses, {@code False} otherwise.
     * @see #FoodValue
     */
    public Boolean removeFood(int Value){
        if(Value > this.FoodValue){
            return false;
        }
        this.FoodValue -= Value;
        return true;
    }

    /**
     * Adds {@code Value} amount of {@code Prestige} to the Player.
     * @param Value The amount of {@code Prestige} to be added.
     *              Should be positive for expected results
     *              (otherwise the method will subtract the absolute
     *              value of {@code Value}).
     * @see #PrestigeValue
     */
    public void addPrestige(int Value){

        this.PrestigeValue += Value;
    }

    /**
     * Removes {@code Value} amount of {@code Prestige} to the Player.
     * @param Value The amount of {@code Prestige} to be removed.
     *              Should be positive for expected results
     *              (otherwise the method will add the absolute
     *              value of {@code Value}).
     * @see #PrestigeValue
     */
    public void removePrestige(int Value){

        this.PrestigeValue -= Value;
    }
    // endregion setters

    // region Constructors

    /**
     * Constructor for the class {@code Player}. Each Player is uniquely identified by the {@link #UserName}.
     * @param UserName Unique String identifier for a Player.
     * @throws IllegalArgumentException When {@code UserName} is {@code null} or exceeds {@link #MAX_VALUE}
     *                                  with message:
     *                                  <pre>{@code UserName is empty or exceeds maximum permitted length.}<pre>
     *
     * @see #UserName
     * @see #MAX_VALUE
     */
    public Player(String UserName) throws  IllegalArgumentException {
        if(UserName.isEmpty() || UserName.length() > MAX_VALUE) {
            throw new IllegalArgumentException("UserName is empty or exceeds maximum permitted length.");
        }
        this.UserName = UserName;

        this.inventors = new ArrayList <>();
        this.builders = new ArrayList <>();
        this.gatherers = new ArrayList <>();
        this.shamans = new ArrayList <>();
        this.hunters = new ArrayList <>();
        this.artists = new ArrayList<>();

        this.buildingCards = new ArrayList<>();
        this.FoodValue = 0;
        this.PrestigeValue = 0;
    }
    // endregion constructors

    // region Functions
    // TODO tostring usr + food + prestige \n tab foreach(characters.cards + spazio)
    @Override
    public String toString() {
        String toPrint = "";
        toPrint = this.getUserName()
                + ":\n\tFood: " + this.getFoodValue()
                + "\n\tPrestige: " + this.getPrestigeValue()
                + "\n\tArtists: " + this.artists.toString()
                + "\n\tBuilders: " + this.builders.toString()
                + "\n\tGatherers: " + this.gatherers.toString()
                + "\n\tShamans: " + this.shamans.toString()
                + "\n\tHunters: " + this.hunters.toString()
                + "\n\tBuildings: "
                + this.buildingCards.toString();
        return toPrint;
    }
    // endregion functions
}
