package net.glowstone.entity.ai;

import lombok.Getter;
import net.glowstone.entity.GlowLivingEntity;
import org.jetbrains.annotations.NonNls;

import java.util.concurrent.ThreadLocalRandom;

public abstract class EntityTask implements Comparable<EntityTask> {

    /**
     * The name of this EntityTask. Must be unique to each EntityTask implementation.
     *
     * @return the name of this EntityTask.
     */
    @Getter
    @NonNls
    private final String name;
    /**
     * Whether this task is currently being executed.
     *
     * @return true if this task is being executed, false otherwise.
     */
    @Getter
    private boolean executing = false;
    private int duration = 0;
    /**
     * Whether this task is paused.
     *
     * @return whether this task is paused.
     */
    @Getter
    private boolean paused = false;

    public EntityTask(@NonNls String name) {
        this.name = name;
    }

    @Override
    public int compareTo(EntityTask other) {
        return 0; // TODO: AI task priority
    }

    /**
     * Advances this task by a tick on the given entity.
     *
     * @param entity the entity to run on
     */
    public final void pulse(GlowLivingEntity entity) {
        if (paused || entity.isDead() || !entity.hasAI()) {
            return;
        }
        if (isInstant()) {
            execute(entity);
            return;
        }
        if (executing && duration > 0) {
            duration--;
            execute(entity);
            return;
        }
        if (executing && duration == 0) {
            executing = false;
            end(entity);
            return;
        }
        if (!executing && shouldStart(entity)) {
            duration = getDurationMin() == getDurationMax() ? getDurationMin()
                : ThreadLocalRandom.current().nextInt(getDurationMax() - getDurationMin())
                + getDurationMin();
            executing = true;
            start(entity);
        }
    }

    /**
     * Resets the progress of this task for this entity.
     *
     * @param entity the entity in question.
     */
    public void reset(GlowLivingEntity entity) {
        end(entity);
        duration = 0;
        executing = false;
    }

    /**
     * Resumes the previously paused task for this entity.
     *
     * @param entity the entity in question.
     */
    public final void resume(GlowLivingEntity entity) {
        if (!isPaused()) {
            return;
        }
        paused = false;
    }

    /**
     * Pauses this task for this entity.
     *
     * @param entity the entity in question.
     */
    public final void pause(GlowLivingEntity entity) {
        if (isPaused()) {
            return;
        }
        reset(entity);
        paused = true;
    }

    /**
     * The minimum duration of this task. This value is ignored if this task is instant.
     *
     * @return the minimum duration of this task, in ticks.
     */
    public abstract int getDurationMin();

    /**
     * The maximum duration of this task. This value is ignored if this task is instant.
     *
     * @return the maximum duration of this task, in ticks.
     */
    public abstract int getDurationMax();

    /**
     * Whether the task should begin executing for this entity.
     *
     * @param entity the entity in question.
     * @return true if the task should start, false otherwise.
     */
    public abstract boolean shouldStart(GlowLivingEntity entity);

    /**
     * Invoked when this task is about to start for this entity.
     *
     * @param entity the entity in question.
     */
    public abstract void start(GlowLivingEntity entity);

    /**
     * Invoked when this task is being ended for this entity.
     *
     * @param entity the entity in question.
     */
    public abstract void end(GlowLivingEntity entity);

    /**
     * Invoked each tick when this task is being executed for this entity.
     *
     * @param entity the entity in question.
     */
    public abstract void execute(GlowLivingEntity entity);

    /**
     * Whether this task is instant. An "instant" task will be executed every tick while the entity
     * is alive.
     *
     * @return the entity in question.
     */
    public abstract boolean isInstant();
}
