package tc.oc.pgm.tablist;

import org.bukkit.entity.Player;
import org.bukkit.event.EventException;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
import tc.oc.commons.bukkit.tablist.PlayerTabEntry;
import tc.oc.commons.bukkit.tablist.TabManager;
import tc.oc.pgm.events.PlayerChangePartyEvent;
import tc.oc.pgm.match.Match;
import tc.oc.pgm.match.MatchPlayer;
import tc.oc.pgm.ffa.Tribute;
import tc.oc.pgm.events.MatchPlayerDeathEvent;
import tc.oc.pgm.ffa.events.MatchResizeEvent;
import tc.oc.pgm.spawns.events.ParticipantSpawnEvent;
import tc.oc.pgm.teams.Team;
import tc.oc.pgm.events.MatchUnloadEvent;
import tc.oc.pgm.events.PlayerJoinMatchEvent;
import tc.oc.pgm.teams.TeamMatchModule;
import tc.oc.pgm.events.PartyRenameEvent;
import tc.oc.commons.core.util.DefaultMapAdapter;
import tc.oc.pgm.teams.events.TeamResizeEvent;

import javax.annotation.Nullable;
import java.util.Map;

public class MatchTabManager extends TabManager implements Listener {
    public static final long RENDER_DELAY = 1;

    private final Map<Team, TeamTabEntry> teamEntries = new DefaultMapAdapter<>(new TeamTabEntry.Factory(), true);
    private final Map<Match, MapTabEntry> mapEntries = new DefaultMapAdapter<>(new MapTabEntry.Factory(), true);
    private final Map<Match, MatchFooterTabEntry> footerEntries = new DefaultMapAdapter<>(new MatchFooterTabEntry.Factory(), true);
    private final Map<Match, FreeForAllTabEntry> freeForAllEntries = new DefaultMapAdapter<>(new FreeForAllTabEntry.Factory(), true);

    private BukkitTask renderTask;

    public MatchTabManager(Plugin plugin) {
        super(plugin, new MatchTabView.Factory(), null);
    }

    public void enable() {
        getPlugin().eventRegistry().registerListener(this);
    }

    public void disable() {
        if(this.renderTask != null) {
            this.renderTask.cancel();
            this.renderTask = null;
        }

        HandlerList.unregisterAll(this);
    }

    @Override
    protected void invalidate() {
        super.invalidate();

        if(this.renderTask == null) {
            Runnable render = new Runnable() {
                @Override public void run() {
                    MatchTabManager.this.renderTask = null;
                    MatchTabManager.this.render();
                }
            };
            this.renderTask = this.getPlugin().getServer().getScheduler().runTaskLater(this.getPlugin(), render, RENDER_DELAY);
        }
    }

    @Override
    public @Nullable MatchTabView getViewOrNull(Player viewer) {
        return (MatchTabView) super.getViewOrNull(viewer);
    }

    @Override
    public @Nullable MatchTabView getView(Player viewer) {
        return (MatchTabView) super.getView(viewer);
    }

    public @Nullable PlayerTabEntry getPlayerEntry(MatchPlayer player) {
        return (PlayerTabEntry) this.getPlayerEntry(player.getBukkit());
    }

    public TeamTabEntry getTeamEntry(Team team) {
        return this.teamEntries.get(team);
    }

    public FreeForAllTabEntry getFreeForAllEntry(Match match) {
        return this.freeForAllEntries.get(match);
    }

    public MapTabEntry getMapEntry(Match match) {
        return this.mapEntries.get(match);
    }

    public MatchFooterTabEntry getFooterEntry(Match match) {
        return this.footerEntries.get(match);
    }

    protected void invalidate(MatchPlayer player) {
        if(player.getBukkit().willBeOnline()) {
            getPlayerEntry(player).invalidate();
        }
        if(player.getMatch().getMapInfo().isAuthor(player.getPlayerId())) {
            MapTabEntry mapEntry = mapEntries.get(player.getMatch());
            if(mapEntry != null) mapEntry.invalidate();
        }
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onJoin(PlayerJoinEvent event) {
        MatchTabView view = this.getView(event.getPlayer());
        if(view != null) view.enable(this);
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onMatchUnload(MatchUnloadEvent event) {
        TeamMatchModule tmm = event.getMatch().getMatchModule(TeamMatchModule.class);
        if(tmm != null) {
            for(Team team : tmm.getTeams()) {
                this.teamEntries.remove(team);
            }
        }
        this.freeForAllEntries.remove(event.getMatch());
        this.mapEntries.remove(event.getMatch());
        this.footerEntries.remove(event.getMatch());
    }

    /**
     * Delegated events
     */

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onJoinMatch(PlayerJoinMatchEvent event) {
        MatchTabView view = this.getView(event.getPlayer().getBukkit());
        if(view != null) view.onViewerJoinMatch(event);

        invalidate(event.getPlayer());
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onPlayerTeamChange(PlayerChangePartyEvent event) throws EventException {
        event.yield();

        invalidate(event.getPlayer());

        if(event.getOldParty() instanceof Team) {
            this.getTeamEntry((Team) event.getOldParty()).invalidate();
        }

        if(event.getNewParty() instanceof Team) {
            this.getTeamEntry((Team) event.getNewParty()).invalidate();
        }

        if(event.getOldParty() instanceof Tribute || event.getNewParty() instanceof Tribute) {
            this.getFreeForAllEntry(event.getMatch()).invalidate();
        }
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onTeamRename(PartyRenameEvent event) {
        if(event.getParty() instanceof Team) {
            this.getTeamEntry((Team) event.getParty()).invalidate();
        }
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onTeamResize(TeamResizeEvent event) {
        this.getTeamEntry(event.getTeam()).invalidate();
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onMatchResize(MatchResizeEvent event) {
        FreeForAllTabEntry entry = this.getFreeForAllEntry(event.getMatch());
        if(entry != null) entry.invalidate();
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onDeath(MatchPlayerDeathEvent event) {
        invalidate(event.getVictim());
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onSpawn(ParticipantSpawnEvent event) {
        invalidate(event.getPlayer());
    }
}
