/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.bausystem.features.tracer;

import de.steamwar.bausystem.region.Region;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.util.Vector;

import java.util.List;
import java.util.Optional;

/**
 * Recording of a tnt at a specific tick
 */
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@Getter
public class TNTPoint {
    /**
     * Unique number to identify records being of the same tnt
     */
    private int tntId;

    /**
     * Whether this is a record of a tnt explosion or an entity
     */
    private boolean explosion;

    /**
     * Whether this is a record of a tnt that was in water
     */
    private boolean inWater;

    /**
     * Whether this record was taken after the first tnt exploded
     */
    private boolean afterFirstExplosion;

    /**
     * Whether this record has destroyed blocks in build area
     */
    private boolean destroyedBuildArea;

    /**
     * Whether this record has destroyed blocks in testblock area
     */
    private boolean destroyedTestBlock;

    /**
     * Tick offset, from this record being taken to the start of the trace
     */
    private long ticksSinceStart;

    /**
     * Fuse ticks of the recorded tnt (0 if this is an explosion)
     */
    private int fuse;

    /**
     * Location of the recorded tnt
     */
    private Location location;

    /**
     * Velocity of the recorded tnt
     */
    private Vector velocity;

    /**
     * List of all tnt records, that are represent the same tnt
     */
    private List<TNTPoint> history;

    /**
     * Constructor for object creation in trace recording
     */
    protected TNTPoint(int tntId, TNTPrimed tnt, boolean explosion, boolean afterFirstExplosion, long ticksSinceStart,
                       List<TNTPoint> history, List<Block> destroyedBlocks) {
        this.tntId = tntId;
        this.explosion = explosion;
        this.inWater = tnt.isInWater();
        this.afterFirstExplosion = afterFirstExplosion;
        this.ticksSinceStart = ticksSinceStart;
        fuse = tnt.getFuseTicks();
        location = tnt.getLocation();
        velocity = tnt.getVelocity();
        this.history = history;

        boolean buildDestroy = false;
        boolean testblockDestroy = false;
        for (Block destroyedBlock : destroyedBlocks) {
            if (Region.getRegion(destroyedBlock.getLocation()).getBuildArea().inRegion(destroyedBlock.getLocation(), true)) {
                buildDestroy = true;
            }
            if (Region.getRegion(destroyedBlock.getLocation()).getTestblockArea().inRegion(destroyedBlock.getLocation(), true)) {
                testblockDestroy = true;
            }
        }

        destroyedBuildArea = buildDestroy;
        destroyedTestBlock = testblockDestroy;
    }

    /**
     * Method for getting the next record of the tnt represented by this record
     *
     * @return the next record
     */
    public Optional<TNTPoint> getNext() {
        int index = history.indexOf(this);
        return index == history.size() - 1 ? Optional.empty() : Optional.of(history.get(index + 1));
    }

    /**
     * Method for getting the previous record of the tnt represented by this record
     *
     * @return the previous record
     */
    public Optional<TNTPoint> getPrevious() {
        int index = history.indexOf(this);
        return index == 0 ? Optional.empty() : Optional.of(history.get(index - 1));
    }

    /**
     * Internal methode for setting the history of this record
     * during deserialization.
     *
     * @param history
     */
    void setHistory(List<TNTPoint> history) {
        this.history = history;
    }

    @Override
    public String toString() {
        return "TNTPoint{" +
                "tntId=" + tntId +
                ", explosion=" + explosion +
                ", inWater=" + inWater +
                ", afterFirstExplosion=" + afterFirstExplosion +
                ", destroyedBuildArea=" + destroyedBuildArea +
                ", destroyedTestBlock=" + destroyedTestBlock +
                ", ticksSinceStart=" + ticksSinceStart +
                ", fuse=" + fuse +
                ", location=" + location +
                ", velocity=" + velocity +
                '}';
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof TNTPoint)) return false;
        TNTPoint record = (TNTPoint) obj;
        if (record.getTntId() != tntId) return false;
        if (record.getFuse() != fuse) return false;
        if (record.getTicksSinceStart() != ticksSinceStart) return false;
        if (record.isExplosion() != explosion) return false;
        if (!record.getLocation().equals(location)) return false;
        if (!record.getVelocity().equals(velocity)) return false;
        return true;
    }
}
