package se.urmo.game.entities.ghost;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import se.urmo.game.collision.GhostCollisionChecker;
import se.urmo.game.entities.BaseAnimated;
import se.urmo.game.entities.ghost.mode.ChaseGhostMode;
import se.urmo.game.entities.ghost.mode.EatenGhostMode;
import se.urmo.game.entities.ghost.mode.FrightenedGhostMode;
import se.urmo.game.entities.ghost.mode.FrozenGhostMode;
import se.urmo.game.entities.ghost.mode.GhostMode;
import se.urmo.game.entities.ghost.mode.GhostState;
import se.urmo.game.entities.ghost.mode.HouseGhostMode;
import se.urmo.game.entities.ghost.mode.ScatterGhostMode;
import se.urmo.game.entities.ghost.strategy.GhostStrategy;
import se.urmo.game.entities.pacman.PacMan;
import se.urmo.game.main.GhostManager;
import se.urmo.game.main.LevelManager;
import se.urmo.game.map.GameMap;
import se.urmo.game.util.Direction;
import se.urmo.game.util.MyPoint;

import java.awt.Color;
import java.awt.Graphics;
import java.util.EnumMap;
import java.util.Map;

@Slf4j
public class Ghost extends BaseAnimated {
    public static final double BASE_SPEED = 0.40;
    public static final int GHOST_SIZE = 32;
    private static final int ANIMATION_UPDATE_FREQUENCY = 25;

    @Getter
    private final GhostCollisionChecker collisionChecker;
    @Getter
    private final int animation;
    @Getter
    private final LevelManager levelManager;

    @Getter
    private final GhostStrategy scaterStrategy;
    private GhostState currentState;

    private final Map<GhostMode, GhostState> states = new EnumMap<>(GhostMode.class);

    // Movement-related state
    @Getter
    @Setter
    protected MyPoint position; // center of sprite
    @Getter
    @Setter
    protected Direction direction;
    @Getter
    @Setter
    protected Direction prevDirection;
    @Getter
    private static final MyPoint startPosition = new MyPoint(
            GameMap.colToScreen(13) + ((double) GameMap.MAP_TILESIZE / 2),
            GameMap.rowToScreen(12) + ((double) GameMap.MAP_TILESIZE / 2));
    @Getter
    private static final MyPoint houseEntrance = new MyPoint(
            GameMap.colToScreen(13) + ((double) GameMap.MAP_TILESIZE / 2),
            GameMap.rowToScreen(10) + ((double) GameMap.MAP_TILESIZE / 2));

    public Ghost(GhostCollisionChecker collisionChecker, GhostStrategy chaseStrategy, GhostStrategy scaterStrategy, int animation, LevelManager levelManager) {
        super(ANIMATION_UPDATE_FREQUENCY, GhostManager.MAX_SPRITE_FRAMES);
        this.collisionChecker = collisionChecker;
        this.scaterStrategy = scaterStrategy;
        this.animation = animation;
        this.levelManager = levelManager;
        this.position = startPosition;
        states.put(GhostMode.CHASE, new ChaseGhostMode(this, chaseStrategy));
        states.put(GhostMode.SCATTER, new ScatterGhostMode(this, scaterStrategy));
        states.put(GhostMode.FRIGHTENED, new FrightenedGhostMode(this));
        states.put(GhostMode.EATEN, new EatenGhostMode(this));
        states.put(GhostMode.FROZEN, new FrozenGhostMode(this));
        states.put(GhostMode.HOUSE, new HouseGhostMode(this));
    }

    public void draw(Graphics g) {
        g.drawImage(
                currentState.getAnimation()[aniIndex],
                (int) position.x - GHOST_SIZE / 2,
                (int) position.y - GHOST_SIZE / 2,
                GHOST_SIZE,
                GHOST_SIZE, null);
        g.setColor(Color.YELLOW);
//        g.fillRect((int) Ghost.startPosition.x, (int) Ghost.startPosition.y, 2, 2);
//        g.fillRect((int) Ghost.houseEntrance.x, (int) Ghost.houseEntrance.y, 2, 2);
//        g.fillRect((int) position.x, (int) position.y, 4, 4);
    }

    public void update(PacMan pacman, GameMap map) {
        currentState.update(this, pacman, map);
    }

    public void setMode(GhostMode mode) {
        GhostMode currentMode = currentMode();
        if (currentMode == null || mode.ordinal() < currentMode.ordinal()) { // only if new mode has higher prio
            log.debug("Mode changed to {}", mode);
            currentState = states.get(mode);
        } else {
            log.debug("Mode not changed to {}, current mode is {}", mode, currentMode);
        }
    }

    /**
     * Used by a state itself to request a transition to any other state
     * This bypasses the priority system since the state itself is requesting the change
     */
    public void requestModeChange(GhostMode mode) {
        log.debug("Requested mode change to {}", mode);
        currentState = states.get(mode);
    }


    public boolean isFrightened() {
        return states.get(GhostMode.FRIGHTENED) == currentState;
    }

    public boolean isEaten() {
        return states.get(GhostMode.EATEN) == currentState;
    }

    public GhostMode currentMode() {
        return states.entrySet().stream()
                .filter(s -> s.getValue() == currentState)
                .map(Map.Entry::getKey)
                .findFirst()
                .orElse(null);
    }

    public void reset() {
        currentState = states.get(GhostMode.CHASE);
        ((ChaseGhostMode) currentState).resetPosition();
    }
}
