package io.badgeup.sponge.eventlistener;

import com.flowpowered.math.vector.Vector3i;
import io.badgeup.sponge.JSONSerializable;
import io.badgeup.sponge.event.BadgeUpEvent;
import io.badgeup.sponge.event.Modifier;
import io.badgeup.sponge.event.ModifierOperation;
import org.json.JSONArray;
import org.json.JSONObject;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.Order;
import org.spongepowered.api.event.entity.MoveEntityEvent;
import org.spongepowered.api.event.network.ClientConnectionEvent;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class MoveEventListener extends BadgeUpEventListener {

    private Map<UUID, PlayerPath> playerPaths;

    public MoveEventListener() {
        super();
        this.playerPaths = new HashMap<>();
    }

    @Listener(order = Order.POST)
    public void move(MoveEntityEvent event) {
        if (event.isCancelled() || !(event.getTargetEntity() instanceof Player)) {
            return;
        }

        Player player = (Player) event.getTargetEntity();

        // If the player teleported, just send the distance event (not including
        // their new location) and remove their player path
        if (event instanceof MoveEntityEvent.Teleport) {
            if (this.playerPaths.containsKey(player.getUniqueId())) {
                PlayerPath playerPath = this.playerPaths.get(player.getUniqueId());
                BadgeUpEvent distanceEvent = new BadgeUpEvent("distance", player.getUniqueId(),
                        new Modifier(ModifierOperation.INC, playerPath.getDistance()));
                distanceEvent.addDataEntry("path", playerPath);

                send(distanceEvent);

                this.playerPaths.remove(player.getUniqueId());
            }
            return;
        }

        PlayerPath playerPath;
        if (this.playerPaths.containsKey(player.getUniqueId())) {
            playerPath = this.playerPaths.get(player.getUniqueId());
        } else {
            playerPath = new PlayerPath();
            this.playerPaths.put(player.getUniqueId(), playerPath);
        }

        Vector3i position = player.getLocation().getBlockPosition();
        playerPath.addPoint(position);

        // Send the event every 20 blocks moved
        if (playerPath.size() >= 20) {
            BadgeUpEvent distanceEvent = new BadgeUpEvent("distance", player.getUniqueId(),
                    new Modifier(ModifierOperation.INC, playerPath.getDistance()));
            distanceEvent.addDataEntry("path", playerPath);

            send(distanceEvent);

            this.playerPaths.remove(player.getUniqueId());
        }

    }

    @Listener
    public void playerJoin(ClientConnectionEvent.Join event) {
        Player player = event.getTargetEntity();

    }

    class PlayerPath implements JSONSerializable {

        private double distance;
        private List<Vector3i> points;

        public PlayerPath() {
            this.distance = 0;
            this.points = new ArrayList<>();
        }

        public void addPoint(Vector3i point) {
            if (this.points.size() == 0) {
                this.points.add(point);
            } else {
                Vector3i lastPoint = this.points.get(this.points.size() - 1);
                if (!point.equals(lastPoint)) {
                    this.points.add(point);
                }
                this.distance += lastPoint.distance(point);
            }
        }

        public double getDistance() {
            return this.distance;
        }

        public int size() {
            return this.points.size();
        }

        @Override
        public JSONObject toJSON() {
            JSONObject obj = new JSONObject();
            obj.put("distance", this.distance);

            JSONArray pathArray = new JSONArray();
            for (Vector3i point : this.points) {
                pathArray.put(new JSONObject().put("x", point.getX()).put("y", point.getY()).put("z", point.getZ()));
            }
            obj.put("path", pathArray);
            return obj;
        }

    }

}

@@ -455,24 +455,87 @@
 vent.Order;%0A
+import org.spongepowered.api.event.entity.DestructEntityEvent;%0A
 import org.s
@@ -1450,471 +1450,16 @@
     
-if (this.playerPaths.containsKey(player.getUniqueId())) %7B%0A                PlayerPath playerPath = this.playerPaths.get(player.getUniqueId());%0A                BadgeUpEvent distanceEvent = new BadgeUpEvent(%22distance%22, player.getUniqueId(),%0A                        new Modifier(ModifierOperation.INC, playerPath.getDistance()));%0A                distanceEvent.addDataEntry(%22path%22, playerPath);%0A%0A                send(distanceEvent);%0A%0A                this.playerPaths.r
+sendAndR
 emov
@@ -1479,38 +1479,24 @@
 niqueId());%0A
-            %7D%0A
             
@@ -2031,66 +2031,218 @@
     
-BadgeUpEvent distanceEvent = new BadgeUpEvent(%22distance%22, 
+sendAndRemove(player.getUniqueId());%0A        %7D%0A%0A    %7D%0A%0A    @Listener%0A    public void playerLeave(ClientConnectionEvent.Disconnect event) %7B%0A        Player player = event.getTargetEntity();%0A        sendAndRemove(
 play
@@ -2261,98 +2261,305 @@
 Id()
-,
+);
 %0A    
+%7D%0A
     
+%0A
     
-        new Modifier(ModifierOperation.INC, playerPath.getDistance()
+@Listener%0A    public void playerDeath(DestructEntityEvent.Death event) %7B%0A        if (!(event.getTargetEntity() instanceof Player)) %7B%0A            return;%0A        %7D%0A        %0A        Player player = (Player) event.getTargetEntity();%0A        sendAndRemove(player.getUniqueId(
 ));%0A    
     
@@ -2558,24 +2558,41 @@
     
+%7D%0A
     
+%0A
     
+// Sends the %22
 distance
 Even
@@ -2591,82 +2591,193 @@
 ance
-E
+%22 e
 vent
-.addDataEntry(%22path%22, playerPath);%0A%0A            send(distanceEvent);%0A
+ and removes the player from the path map%0A    private void sendAndRemove(UUID playerUUID) %7B%0A        if (!this.playerPaths.containsKey(playerUUID)) %7B%0A            return;%0A        %7D
 %0A   
@@ -2773,35 +2773,64 @@
       %7D%0A        
+%0A  
    
+   PlayerPath playerPath =
  this.playerPath
@@ -2835,35 +2835,22 @@
 ths.
-remove
+get
 (player
-.getUniqueId()
+UUID
 );%0A 
@@ -2860,145 +2860,299 @@
     
-%7D%0A
 %0A    
-%7D%0A%0A
     
-@Listener%0A    public void playerJoin(ClientConnectionEvent.Join event) %7B%0A        Player player = event.getTargetEntity(
+BadgeUpEvent distanceEvent = new BadgeUpEvent(%22distance%22, playerUUID,%0A                new Modifier(ModifierOperation.INC, playerPath.getDistance()));%0A        distanceEvent.addDataEntry(%22path%22, playerPath);%0A%0A        send(distanceEvent);%0A%0A        this.playerPaths.remove(playerUUID
 );%0A
-%0A
     

Send distance event when player leaves or dies