/* 
    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.fightsystem;

import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.core.Core;
import de.steamwar.fightsystem.commands.*;
import de.steamwar.fightsystem.countdown.*;
import de.steamwar.fightsystem.event.HellsBells;
import de.steamwar.fightsystem.event.Meteor;
import de.steamwar.fightsystem.event.PersistentDamage;
import de.steamwar.fightsystem.event.TNTDistributor;
import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.fight.FightWorld;
import de.steamwar.fightsystem.fight.HotbarKit;
import de.steamwar.fightsystem.listener.Shutdown;
import de.steamwar.fightsystem.listener.*;
import de.steamwar.fightsystem.record.FileRecorder;
import de.steamwar.fightsystem.record.FileSource;
import de.steamwar.fightsystem.record.GlobalRecorder;
import de.steamwar.fightsystem.record.LiveRecorder;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.OneShotStateDependent;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.utils.*;
import de.steamwar.fightsystem.winconditions.*;
import de.steamwar.message.Message;
import de.steamwar.sql.SchematicNode;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;

public class FightSystem extends JavaPlugin {

    @Getter
    private static FightSystem plugin;

    private Message message;
    private FightTeam lastWinner;
    private String lastWinreason;
    private TechHiderWrapper techHider;
    private HullHider hullHider;

    @Override
    public void onLoad() {
        plugin = this;
    }

    @Override
    public void onEnable() {
        if(Core.getInstance() == null) {
            Core.setInstance(this);
            TinyProtocol.init();
        }

        message = new Message("de.steamwar.fightsystem.FightSystem", FightSystem.class.getClassLoader());

        new EntityDamage();
        new WaterRemover();
        new Permanent();
        new PistonListener(ArenaMode.AntiTestCheckPrepare, e -> e.setCancelled(true));
        new PistonListener(ArenaMode.Test, e -> getMessage().broadcastActionbar("PISTON_PUSHED_OUTSIDE"));
        new PistonListener(ArenaMode.Prepare, e -> {
            getMessage().broadcast("PISTON_PUSHED_OUTSIDE");
            shutdown();
        });
        new Chat();
        new ArenaBorder();
        new IngameDeath();
        new InFightDamage();
        new InFightInventory();
        new DenyWorldInteraction();
        new DenyInventoryMovement();
        new EventJoin();
        new Recording();
        new Check();
        new Shutdown();
        new SetupQuit();
        new PrepareSchem();
        new TestJoin();
        new NormalJoin();
        new RunningWorldInteraction();
        new PersonalKitCreator();
        new ArrowStopper();
        new StateDependentListener(ArenaMode.All, FightState.All, BountifulWrapper.impl.newDenyArrowPickupListener());
        new BlockFadeListener();
        new LeaveableArena();
        new ClickAnalyzer();
        new BlockPlaceCollision();
        new HotbarKit.HotbarKitListener();
        new JoinRequestListener();
        new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f));

        new EnterHandler();
        techHider = new TechHiderWrapper();
        hullHider = new HullHider();
        new FightWorld();
        new FightUI();
        new FightStatistics();
        new BungeeFightInfo();

        new WinconditionAllDead();
        new WinconditionCaptainDead();
        new WinconditionBlocks(Winconditions.WATER_TECH_KO, "WaterTechKO", "BAR_WATER", FlatteningWrapper.impl::isWater);
        new WinconditionBlocks(Winconditions.PUMPKIN_TECH_KO, "PumpkinTechKO", "BAR_CANNONS", block -> block.getType() == WinconditionBlocks.PUMPKIN_LANTERN);
        new WinconditionPercent(Winconditions.PERCENT_SYSTEM, "Percent");
        new WinconditionPoints();
        new WinconditionPointsAirShip();
        new WinconditionTimeout();
        new WinconditionTimeTechKO();
        new EventTeamOffWincondition();
        new WinconditionComparisonTimeout(Winconditions.HEART_RATIO_TIMEOUT, "HeartTimeout", "WIN_MORE_HEALTH", FightTeam::getHeartRatio);
        new WinconditionComparisonTimeout(Winconditions.PERCENT_TIMEOUT, "PercentTimeout", "WIN_LESS_DAMAGE", team -> -Wincondition.getPercentWincondition().getPercent(team));

        new HellsBells();
        new Meteor();
        new PersistentDamage();
        new TNTDistributor();
        new WinconditionAmongUs();

        new NoPlayersOnlineCountdown();
        new PreSchemCountdown();
        new PostSchemCountdown();
        new PreRunningCountdown();
        new SpectateOverCountdown();
        new EventSpectateCountdown();

        new LeaveCommand();
        new KitCommand();
        new RemoveCommand();
        new RequestsCommand();
        new InfoCommand();
        new WGCommand();
        new TBCommand();
        new GamemodeCommand();
        new ReadyCommand();
        new AkCommand();
        new LockschemCommand();
        new StateCommand();
        new SkipCommand();
        new TPSWarpCommand();
        new UnrankCommand();
        new WinCommand();

        new LiveRecorder();
        new FileRecorder();

        FileSource.startReplay();

        if(Config.mode == ArenaMode.EVENT) {
            FightState.setFightState(FightState.PRE_SCHEM_SETUP);
        }else if(Config.mode == ArenaMode.CHECK){
            SchematicNode checkSchematicNode = SchematicNode.getSchematicNode(Config.CheckSchemID);
            Fight.getBlueTeam().setSchem(checkSchematicNode);

            if (checkSchematicNode.getName().endsWith("-prepared")) {
                SchematicNode unpreparedSchematicNode = SchematicNode.getSchematicNode(checkSchematicNode.getOwner(), checkSchematicNode.getName().substring(0, checkSchematicNode.getName().length() - 9), checkSchematicNode.getParent());
                if (unpreparedSchematicNode != null) {
                    Fight.getRedTeam().setSchem(unpreparedSchematicNode);
                }
            }
        }else if(Config.mode == ArenaMode.PREPARE) {
            Fight.getUnrotated().setSchem(SchematicNode.getSchematicNode(Config.PrepareSchemID));
        }
    }

    @Override
    public void onDisable() {
        GlobalRecorder.getInstance().close();
        ClickAnalyzer.close();
    }

    public static void setSpectateState(FightTeam winFightTeam, String winreason, String subtitle, Object... params) {
        plugin.lastWinner = winFightTeam;
        plugin.lastWinreason = winreason;
        FightUI.printWin(winFightTeam, subtitle, params);

        FightState.setFightState(FightState.SPECTATE);
    }

    public static Message getMessage() {
        return plugin.message;
    }

    public static FightTeam getLastWinner() {
        return plugin.lastWinner;
    }

    public static String getLastWinreason() {
        return plugin.lastWinreason;
    }

    public static TechHiderWrapper getTechHider() {
        return plugin.techHider;
    }

    public static HullHider getHullHider() {
        return plugin.hullHider;
    }

    public static void shutdown() {
        //Staggered kick to prevent lobby overloading
        if(Bukkit.getOnlinePlayers().isEmpty()){
            Bukkit.shutdown();
            return;
        }

        Bukkit.getOnlinePlayers().iterator().next().kickPlayer(null);
        Bukkit.getScheduler().runTaskLater(plugin, FightSystem::shutdown, 10);
    }
}
