package org.screamingsandals.bedwars.holograms;

import static misat11.lib.lang.I.i18n;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.screamingsandals.bedwars.Main;
import org.screamingsandals.bedwars.api.statistics.PlayerStatistic;
import org.screamingsandals.bedwars.commands.AdminCommand;
import org.screamingsandals.lib.nms.holograms.Hologram;
import org.screamingsandals.lib.nms.holograms.TouchHandler;

public class NMSUtilsHologramInteraction implements IHologramInteraction, TouchHandler {

    private ArrayList<Location> hologramLocations = null;
    private Map<Player, List<Hologram>> holograms = null;

	@Override
	public void addHologramLocation(Location eyeLocation) {
        this.hologramLocations.add(eyeLocation.subtract(0, 3, 0));
        this.updateHologramDatabase();
	}

	@Override
	public ArrayList<Location> getHologramLocations() {
		return hologramLocations;
	}

	@Override
	public String getType() {
        return "NMSUtils.Holograms";
	}

    @SuppressWarnings("unchecked")
    public void loadHolograms() {
        if (!Main.isHologramsEnabled()) {
            return;
        }

        if (this.holograms != null && this.hologramLocations != null) {
            // first unload all holograms
            this.unloadHolograms();
        }

        this.holograms = new HashMap<>();
        this.hologramLocations = new ArrayList<>();

        File file = new File(Main.getInstance().getDataFolder(), "holodb.yml");
        if (file.exists()) {
            YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
            List<Location> locations = (List<Location>) config.get("locations");
            assert locations != null;
            this.hologramLocations.addAll(locations);
        }

        if (this.hologramLocations.size() == 0) {
            return;
        }

        this.updateHolograms();
    }

	@Override
	public void onHologramTouch(Player player, Location holoLocation) {
        // NOT NEEDED HERE
	}

	@Override
	public void unloadAllHolograms(Player player) {
        if (!this.holograms.containsKey(player)) {
            return;
        }

        for (Hologram holo : this.holograms.get(player)) {
            holo.destroy();
        }

        this.holograms.remove(player);
	}

	@Override
	public void unloadHolograms() {
        if (Main.isHologramsEnabled()) {
        	for (List<Hologram> holos : holograms.values()) {
        		for (Hologram holo : holos) {
        			holo.destroy();
        		}
        	}
        }
	}

	@Override
	public void updateHolograms(Player player) {
        Main.getInstance().getServer().getScheduler().runTask(Main.getInstance(), () -> {
            for (Location holoLocation : NMSUtilsHologramInteraction.this.hologramLocations) {
            	NMSUtilsHologramInteraction.this.updatePlayerHologram(player, holoLocation);
            }
        });
	}

	@Override
	public void updateHolograms(Player player, long delay) {
        Main.getInstance().getServer().getScheduler().runTaskLater(Main.getInstance(), () -> {
        	NMSUtilsHologramInteraction.this.updateHolograms(player);
        }, delay);
	}

	@Override
	public void updateHolograms() {
        for (final Player player : Bukkit.getServer().getOnlinePlayers()) {
            Main.getInstance().getServer().getScheduler().runTask(Main.getInstance(), () -> {
                for (Location holoLocation : NMSUtilsHologramInteraction.this.hologramLocations) {
                	NMSUtilsHologramInteraction.this.updatePlayerHologram(player, holoLocation);
                }
            });
        }
	}

    private Hologram getHologramByLocation(List<Hologram> holograms, Location holoLocation) {
        for (Hologram holo : holograms) {
            if (holo.getLocation().getX() == holoLocation.getX() && holo.getLocation().getY() == holoLocation.getY()
                    && holo.getLocation().getZ() == holoLocation.getZ()) {
                return holo;
            }
        }

        return null;
    }
	
	public void updatePlayerHologram(Player player, Location holoLocation) {
        List<Hologram> holograms;
        if (!this.holograms.containsKey(player)) {
            this.holograms.put(player, new ArrayList<>());
        }

        holograms = this.holograms.get(player);
        Hologram holo = this.getHologramByLocation(holograms, holoLocation);
        if (holo == null && player.getWorld() == holoLocation.getWorld()) {
            holograms.add(this.createPlayerStatisticHologram(player, holoLocation));
        } else if (holo != null) {
            if (holo.getLocation().getWorld() == player.getWorld()) {
                this.updatePlayerStatisticHologram(player, holo);
            } else {
                holograms.remove(holo);
                holo.destroy();
            }
        }
	}

    private Hologram createPlayerStatisticHologram(Player player, Location holoLocation) {
        final Hologram holo = Main.getHologramManager().spawnHologramTouchable(player, holoLocation);
        holo.addHandler(this);

        String headline = Main.getConfigurator().config.getString("holograms.headline", "Your §eBEDWARS§f stats");
        if (!headline.trim().isEmpty()) {
            holo.addLine(headline);
        }

        this.updatePlayerStatisticHologram(player, holo);
        return holo;
    }

    private void updateHologramDatabase() {
        try {
            // update hologram-database file
            File file = new File(Main.getInstance().getDataFolder(), "holodb.yml");
            YamlConfiguration config = new YamlConfiguration();

            if (!file.exists()) {
                file.createNewFile();
            }

            config.set("locations", hologramLocations);
            config.save(file);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private Location getHologramLocationByLocation(Location holoLocation) {
        for (Location loc : this.hologramLocations) {
            if (loc.getX() == holoLocation.getX() && loc.getY() == holoLocation.getY()
                    && loc.getZ() == holoLocation.getZ()) {
                return loc;
            }
        }

        return null;
    }

	@Override
	public void handle(Player player, Hologram holo) {
        if (!player.hasMetadata("bw-remove-holo") || (!player.isOp() && !player.hasPermission(AdminCommand.ADMIN_PERMISSION))) {
            return;
        }

        player.removeMetadata("bw-remove-holo", Main.getInstance());
        Main.getInstance().getServer().getScheduler().runTask(Main.getInstance(), () -> {
            // remove all player holograms on this location
            for (Entry<Player, List<Hologram>> entry : holograms.entrySet()) {
                Iterator<Hologram> iterator = entry.getValue().iterator();
                while (iterator.hasNext()) {
                    Hologram hologram = iterator.next();
                    if (hologram.getLocation().getX() == holo.getLocation().getX() && hologram.getLocation().getY() == holo.getLocation().getY()
                            && hologram.getLocation().getZ() == holo.getLocation().getZ()) {
                        hologram.destroy();
                        iterator.remove();
                    }
                }
            }

            Location holoLocation = getHologramLocationByLocation(holo.getLocation());
            if (holoLocation != null) {
                hologramLocations.remove(holoLocation);
                updateHologramDatabase();
            }
            player.sendMessage(i18n("holo_removed"));
        });
	}

    private void updatePlayerStatisticHologram(Player player, final Hologram holo) {
        PlayerStatistic statistic = Main.getPlayerStatisticsManager().getStatistic(player);
        
        List<String> lines = new ArrayList<>();

        lines.add(i18n("statistics_kills", false).replace("%kills%",
                Integer.toString(statistic.getKills() + statistic.getCurrentKills())));
        lines.add(i18n("statistics_deaths", false).replace("%deaths%",
                Integer.toString(statistic.getDeaths() + statistic.getCurrentDeaths())));
        lines.add(i18n("statistics_kd", false).replace("%kd%",
                Double.toString(statistic.getCurrentKD())));
        lines.add(i18n("statistics_wins", false).replace("%wins%",
                Integer.toString(statistic.getWins() + statistic.getCurrentWins())));
        lines.add(i18n("statistics_loses", false).replace("%loses%",
                Integer.toString(statistic.getLoses() + statistic.getCurrentLoses())));
        lines.add(i18n("statistics_games", false).replace("%games%",
                Integer.toString(statistic.getGames() + statistic.getCurrentGames())));
        lines.add(i18n("statistics_beds", false).replace("%beds%",
                Integer.toString(statistic.getDestroyedBeds() + statistic.getCurrentDestroyedBeds())));
        lines.add(i18n("statistics_score", false).replace("%score%",
                Integer.toString(statistic.getScore() + statistic.getCurrentScore())));
        
        int size = holo.length();
        int increment = 0;
        if (size == 1 || size > lines.size()) {
        	increment = 1;
        }

        for (int i = 0; i < lines.size(); i++) {
        	holo.setLine(i + increment, lines.get(i));
        }
    }

}