package uk.co.eluinhost.ultrahardcore.features.footprints;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import com.comphenix.protocol.PacketType;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;

import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;

import uk.co.eluinhost.ultrahardcore.UltraHardcore;
import uk.co.eluinhost.configuration.ConfigManager;
import uk.co.eluinhost.ultrahardcore.features.UHCFeature;

public class FootprintFeature extends UHCFeature implements Runnable {

    public static final float REGULAR_OFFSET = 1.05F;
    public static final float SNOW_OFFSET = 1.13F;

    public static final int REPEAT_INTERVAL = 40;

    public static final String MAX_RENDER_NODE = "maxdistance";
    public static final String MIN_RENDER_NODE = "mindistance";
    public static final String TIME_NODE = "time";

    private final int m_maxRenderSquared;
    private final int m_minRenderSquared;

    private static final ProtocolManager PROTOCOL_MANAGER = ProtocolLibrary.getProtocolManager();

    private final PacketContainer m_defaultPacket = PROTOCOL_MANAGER.createPacket(PacketType.Play.Server.WORLD_PARTICLES);

    public static final String FOOTPRINTS_FOR_PLAYER = BASE_PERMISSION + "footprints.leavePrints";

    private final Collection<Footstep> m_footsteps = new ArrayList<Footstep>();
    private int m_jobID = -1;

    /**
     * Leave 'footprints' behind you, requires protocollib
     */
    public FootprintFeature() {
        super("Footprints","Leave footprints behind you...");
        FileConfiguration config = ConfigManager.getInstance().getConfig();
        int maxRender = config.getInt(getBaseConfig()+MAX_RENDER_NODE);
        int minRender = config.getInt(getBaseConfig()+MIN_RENDER_NODE);
        m_maxRenderSquared = maxRender * maxRender;
        m_minRenderSquared = minRender * minRender;
    }

    @Override
    protected void enableCallback() {
        m_jobID = Bukkit.getScheduler().scheduleSyncRepeatingTask(UltraHardcore.getInstance(), this, 1, REPEAT_INTERVAL);
    }

    @Override
    protected void disableCallback() {
        Bukkit.getScheduler().cancelTask(m_jobID);
        m_jobID = -1;
        m_footsteps.clear();
    }

    @Override
    public void run() {
        for (Player p : Bukkit.getOnlinePlayers()) {
            if (!p.hasPermission(FOOTPRINTS_FOR_PLAYER) || p.getGameMode() == GameMode.CREATIVE) {
                continue;
            }
            Block block = p.getLocation().getBlock().getRelative(BlockFace.DOWN);
            if (block.getType() == Material.AIR) {
                block = block.getRelative(BlockFace.DOWN);
            }
            Location loc = p.getLocation();
            if (block.getType() != Material.AIR && block.getType() != Material.WATER && isClearOfFootprints(loc, m_minRenderSquared, p.getName())) {
                float offset = REGULAR_OFFSET;
                if (block.getType() == Material.SNOW) {
                    offset = SNOW_OFFSET;
                }
                loc.setY(block.getLocation().getY() + offset);
                Footstep newFootstep = new Footstep(
                    loc,
                    ConfigManager.getInstance().getConfig().getInt(getBaseConfig()+TIME_NODE) / 2,
                    p.getName()
                );
                sendFootstep(newFootstep);
                m_footsteps.add(newFootstep);
            }
        }
        Iterator<Footstep> iterator = m_footsteps.iterator();
        while (iterator.hasNext()) {
            Footstep footstep = iterator.next();
            footstep.decrementTimeRemaining();
            if (footstep.getTimeRemaining() <= 0) {
                iterator.remove();
            } else {
                sendFootstep(footstep);
            }
        }
    }

    /**
     * Checks for footprints within the distance of the location for the player name
     * @param loc Location
     * @param distance int
     * @param name String
     * @return boolean true if clear, false if not
     */
    private boolean isClearOfFootprints(Location loc, int distance, String name) {
        for (Footstep footstep : m_footsteps) {
            if (footstep.getName().equals(name)) {
                try {
                    if (footstep.getLocation().distanceSquared(loc) < distance) {
                        return false;
                    }
                } catch (IllegalArgumentException ignored) {/*happens on different dimension*/}
            }
        }
        return true;
    }

    /**
     * Sends the footstep to nearby players
     * @param footstep the footstep to send
     */
    private void sendFootstep(Footstep footstep){
        m_defaultPacket.getStrings().write(0, "footstep");
        Location loc = footstep.getLocation();
        m_defaultPacket.getFloat()
                .write(0, (float) loc.getX())//x
                .write(1, (float) loc.getY())//y
                .write(2, (float) loc.getZ())
                .write(3, 0.0F)//offsetx
                .write(4, 0.0F)//offsety
                .write(5, 0.0F)//offsetz
                .write(6, 1.0F);
        m_defaultPacket.getIntegers().write(0, 1);
        for (Player p : Bukkit.getOnlinePlayers()) {
            try {
                if (p.getLocation().distanceSquared(loc) < m_maxRenderSquared) {
                    PROTOCOL_MANAGER.sendServerPacket(p, m_defaultPacket);
                }
            } catch (IllegalArgumentException ignored) {
                //wrong world, doesn't matter
            } catch (InvocationTargetException ignored) {}
        }
    }
}

@@ -1043,188 +1043,8 @@
 0;%0A%0A
-    public static final String MAX_RENDER_NODE = %22maxdistance%22;%0A    public static final String MIN_RENDER_NODE = %22mindistance%22;%0A    public static final String TIME_NODE = %22time%22;%0A%0A
     
@@ -1122,16 +1122,52 @@
 Squared;
+%0A    private final int m_timeToLast;
 %0A%0A    pr
@@ -1897,23 +1897,21 @@
 g()+
-MAX_RENDER_NODE
+%22maxdistance%22
 );%0A 
@@ -1967,23 +1967,21 @@
 g()+
-MIN_RENDER_NODE
+%22mindistance%22
 );%0A 
@@ -2079,24 +2079,119 @@
  minRender;%0A
+        m_timeToLast = ConfigManager.getInstance().getConfig().getInt(getBaseConfig()+%22time%22);%0A
     %7D%0A%0A    @
@@ -3485,81 +3485,20 @@
     
-ConfigManager.getInstance().getConfig().getInt(getBaseConfig()+TIME_NODE)
+m_timeToLast
  / 2

remove unneeded constants, get time to last on construct