package cz.jzitnik.tui;

import java.util.HashMap;
import java.util.Optional;

/**
 * Abstract class representing a sprite that can have multiple visual states.
 * Each state is represented by a resource loaded from a specified location.
 *
 * @param <E> Enum type representing different states of the sprite.
 */
public abstract class Sprite<E extends Enum<E>> {
    private HashMap<E, String> resourcesLocation;
    private HashMap<E, Optional<String>> resources;
    private String defaultResource;
    private String defaultResourceLocation;
    private Class<E> enumClass;

    /**
     * Loads the resource locations for each sprite state.
     *
     * @param values     A map from enum keys to resource locations.
     * @param enumClass  Class object of the enum used for states.
     */
    protected final void loadResources(HashMap<E, String> values, Class<E> enumClass) {
        resources = new HashMap<>();
        resourcesLocation = new HashMap<>();
        for (E key : values.keySet()) {
            var value = values.get(key);
            resourcesLocation.put(key, value);
            resources.put(key, Optional.empty());
        }

        this.enumClass = enumClass;
    }

    /**
     * Loads the resource for a specific sprite state from its location.
     *
     * @param key Enum key representing the sprite state.
     * @return Loaded resource as a String.
     */
    private String loadResource(E key) {
        var location = resourcesLocation.get(key);
        var resource = ResourceLoader.loadResource(location);

        resources.put(key, Optional.of(resource));
        resourcesLocation.remove(key);

        return resource;
    }
    /**
     * Sets the location for the default sprite resource.
     *
     * @param resource Path to the default resource.
     */
    protected final void loadResource(String resource) {
        defaultResourceLocation = resource;
    }

    /**
     * Loads the default sprite resource from its location.
     *
     * @return Loaded default resource as a String.
     */
    private String loadResource() {
        var resource = ResourceLoader.loadResource(defaultResourceLocation);

        defaultResource = resource;

        return resource;
    }

    /**
     * Returns the loaded resource for the given sprite state.
     * If not loaded yet, it is loaded and cached.
     *
     * @param key Enum key representing the sprite state.
     * @return Resource as a String.
     */
    protected final String getResource(E key) {
        var resource = resources.get(key);

        return resource.orElseGet(() -> loadResource(key));
    }

    /**
     * Returns the loaded default resource.
     * If not loaded yet, it is loaded and cached.
     *
     * @return Default resource as a String.
     */
    protected final String getResource() {
        if (defaultResource != null) {
            return defaultResource;
        } else {
            return loadResource();
        }
    }
    /**
     * Returns the sprite for the default state.
     *
     * @return String representing the sprite.
     */
    public abstract String getSprite();

    /**
     * Returns the sprite for a given state.
     *
     * @param key Enum key representing the sprite state.
     * @return String representing the sprite.
     */
    public abstract String getSprite(E key);


    /**
     * Returns the enum class used to represent sprite states, if available. This function is used for automated tests.
     *
     * @return Optional containing the enum class, or empty if not set.
     */
    public Optional<Class<E>> getStates() {
        return Optional.ofNullable(enumClass);
    }
}
