/* 
    This file is a part of the SteamWar software.
    
    Copyright (C) 2020  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.world;

import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.FlatteningWrapper;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitTask;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;

public class AutoLoader extends AbstractAutoLoader implements Listener {

    private static final Map<Player, AutoLoader> players = new HashMap<>();

    public static AutoLoader getLoader(Player player){
        if(!players.containsKey(player))
            return new AutoLoader(player);
        return players.get(player);
    }

    public static boolean hasLoader(Player player){
        return players.containsKey(player);
    }

    private final Player player;
    private final BukkitTask task;

    private final LinkedList<LoaderAction> actions = new LinkedList<>();
    private int ticksBetweenShots = 80;
    private int ticksBetweenBlocks = 1;

    private int lastActivation;
    private int waitTicks;
    private ListIterator<LoaderAction> lastAction;
    private boolean setup;
    private Location lastLocation;

    private AutoLoader(Player player){
        this.player = player;
        Bukkit.getPluginManager().registerEvents(this, BauSystem.getPlugin());
        task = Bukkit.getScheduler().runTaskTimer(BauSystem.getPlugin(), this::run, 1, 1);
        players.put(player, this);
        player.sendMessage(BauSystem.PREFIX + "§7Schieße bitte einmal die Kanone ab");
        player.sendMessage(BauSystem.PREFIX + "§7Und starte anschließend den AutoLader mit §8/§eloader start");
        setup();
    }

    public void setup(){
        print("§aAutoLader im Setup-Modus", false);
        setup = true;
        waitTicks = 0;
        lastActivation = 0;
    }

    public void start(){
        if(actions.isEmpty()){
            print("§cKeine Aktion vorhanden", false);
            return;
        }

        if(!setup){
            print("§cAutoLader läuft bereits", false);
            return;
        }

        setup = false;
        waitTicks = 0;
        lastActivation = 0;
        lastAction = actions.listIterator();
        print("§aAutoLader gestartet", false);
    }

    public void stop(){
        print("§cAutoLader gestoppt", false);
        players.remove(player);
        HandlerList.unregisterAll(this);
        if(task != null)
            task.cancel();
    }

    public void undo(){
        if(actions.isEmpty()){
            print("§cKeine Aktion vorhanden", false);
            return;
        }

        actions.removeLast();
        print("§aUndo erfolgreich", true);
    }

    public void wait(int time){
        if(time < 1){
            print("§cDie Wartezeit ist zu klein", false);
            return;
        }

        print("§aSchusswartezeit §e" + time + " §aTicks§8, zuvor " + ticksBetweenShots, false);
        ticksBetweenShots = time;
    }

    public void blockWait(int time){
        if(time < 1){
            print("§cDie Wartezeit ist zu klein", false);
            return;
        }

        print("§aSetzwartezeit §e" + time + " §aTicks§8, zuvor " + ticksBetweenBlocks, false);
        ticksBetweenBlocks = time;
    }

    public int getTicksBetweenShots() {
        return ticksBetweenShots;
    }

    public int getTicksBetweenBlocks() {
        return ticksBetweenBlocks;
    }

    public boolean isSetup() {
        return setup;
    }

    @EventHandler
    public void onBlockPlace(BlockPlaceEvent event){
        if(!setup || !event.getPlayer().equals(player))
            return;
        if(event.getBlock().getType() != Material.TNT)
            return;

        new TNTPlaceAction(this, event.getBlock().getLocation());
        print("§eTNT platziert", true);
    }

    //BlockRedstoneEvent?
    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent event){
        if (event.getAction() != Action.RIGHT_CLICK_BLOCK && event.getAction() != Action.PHYSICAL)
            return;
        if (event.getClickedBlock().getType() == Material.OBSERVER)
            return;
        if (event.getPlayer().isSneaking())
            return;

        if (!setup || !event.getPlayer().equals(player))
            return;

        Detoloader detoloader = FlatteningWrapper.impl.onPlayerInteractLoader(event);
        if (detoloader == null || detoloader.getActivation() < 0) return;

        if (lastLocation != null && lastLocation.distance(event.getClickedBlock().getLocation()) <= 1) return;

        if (detoloader.useActive) {
            new AbstractAutoLoader.RedstoneActivation(this, event.getClickedBlock().getLocation()
                    , detoloader.getActivation() == 0 ? getLastActivation() : detoloader.getActivation()
                    , detoloader.isActive());
        } else {
            new AbstractAutoLoader.TemporaryActivation(this, event.getClickedBlock().getLocation()
                    , detoloader.getActivation());
        }
        print(detoloader.addBack ? "§e" + detoloader.getBlock() + " betätigt" :
                detoloader.getBlock(), detoloader.addBack);
        lastLocation = event.getClickedBlock().getLocation();
    }

    @EventHandler
    public void onLeave(PlayerQuitEvent event){
        if(!event.getPlayer().equals(player))
            return;
        stop();
    }

    private void run(){
        lastActivation++;
        if(setup)
            return;
        while(lastActivation >= waitTicks){
            lastActivation = 0;

            LoaderAction action = lastAction.next();
            if(action.perform()){
                waitTicks = action.ticks();
            }else{
                waitTicks = 1;
                lastAction.previous();
            }

            if(!lastAction.hasNext()){
                lastAction = actions.listIterator();
                waitTicks = ticksBetweenShots;
            }
        }
    }

    @Override
    Player getPlayer() {
        return player;
    }

    @Override
    boolean setRedstone(Location location, boolean active){
        return FlatteningWrapper.impl.setRedstone(location, active);
    }

    @Override
    LinkedList<LoaderAction> getActions() {
        return actions;
    }

    @Override
    void resetLastActivation() {
        lastActivation = 0;
    }

    @Override
    int getLastActivation() {
        return lastActivation;
    }

    class TNTPlaceAction extends AbstractAutoLoader.LoaderAction {

        TNTPlaceAction(AbstractAutoLoader loader, Location location){
            super(loader, location);
        }

        @Override
        public boolean perform() {
            return FlatteningWrapper.impl.tntPlaceActionPerform(location);
        }

        @Override
        int ticks(){
            return ticksBetweenBlocks;
        }
    }
}
