package mmo.server;

import com.google.common.collect.Sets;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.Future;
import mmo.server.model.Coord;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Singleton
public class GameLoop {
    private final DefaultEventExecutorGroup loop;
    private Room room = new Room();

    public interface Callback {
        void tick();

        void tock();

        void cannotEnter();

        void endered(Coord coord);

        void left(Coord coord);

        void inRoom(Map<Coord, Callback> inRoom);
    }

    private final HashedWheelTimer timer;
    private boolean tickTock = false;
    private TimerTask task = new TimerTask() {

        @Override
        public void run(Timeout timeout) throws Exception {
            tickTock ^= true;
            if (tickTock) {
                tick();
            } else {
                tock();
            }
            schedule();
        }
    };
    private Set<Callback> callbacks = Sets.newConcurrentHashSet();

    @Inject
    public GameLoop(HashedWheelTimer timer) {
        this.timer = timer;
        schedule();
        loop = new DefaultEventExecutorGroup(1);
    }

    private void tock() {
        System.out.println("tock");
        for (Callback cb : callbacks) {
            cb.tock();
        }
    }

    private void tick() {
        System.out.println("tick");
        for (Callback cb : callbacks) {
            cb.tick();
        }
    }

    private void schedule() {
        timer.newTimeout(task, 1, TimeUnit.SECONDS);
    }

    public void login(Callback cb) {
        callbacks.add(cb);

        Coord coord = room.enter(new Coord(8, 8), cb);
        if (coord == null) {
            cb.cannotEnter();
        } else {
            for (Callback c : room.contents().values()) {
                c.endered(coord);
            }
            cb.inRoom(room.contents());
        }
    }

    public void logout(Callback cb) {
        Coord coord = room.leave(cb);
        for (Callback c : room.contents().values()) {
            c.left(coord);
        }

        callbacks.remove(cb);
    }

    public Future<?> shutdownGracefully() {
        return loop.shutdownGracefully();
    }
}

@@ -1794,16 +1794,22 @@
 d login(
+final 
 Callback
@@ -1807,32 +1807,131 @@
  Callback cb) %7B%0A
+        loop.submit(new Runnable() %7B%0A            @Override%0A            public void run() %7B%0A        
         callback
@@ -1938,24 +1938,32 @@
 s.add(cb);%0A%0A
+        
         Coor
@@ -2009,24 +2009,32 @@
 b);%0A        
+  
+      
 if (coord ==
@@ -2054,16 +2054,24 @@
         
+      
+  
 cb.canno
@@ -2076,24 +2076,32 @@
 notEnter();%0A
+        
         %7D el
@@ -2109,32 +2109,40 @@
 e %7B%0A            
+ 
+       
 for (Callback c 
@@ -2187,16 +2187,24 @@
         
+        
 c.endere
@@ -2205,32 +2205,40 @@
 endered(coord);%0A
+        
             %7D%0A  
@@ -2247,16 +2247,24 @@
         
+        
 cb.inRoo
@@ -2283,33 +2283,67 @@
 nts());%0A        
-%7D
+        %7D%0A            %7D%0A        %7D);
 %0A    %7D%0A%0A    publ
@@ -2357,16 +2357,22 @@
  logout(
+final 
 Callback
@@ -2370,32 +2370,131 @@
  Callback cb) %7B%0A
+        loop.submit(new Runnable() %7B%0A            @Override%0A            public void run() %7B%0A        
         Coord co
@@ -2511,24 +2511,32 @@
 .leave(cb);%0A
+        
         for 
@@ -2589,16 +2589,24 @@
         
+     
+   
 c.left(c
@@ -2604,32 +2604,40 @@
  c.left(coord);%0A
+        
         %7D%0A%0A     
@@ -2631,32 +2631,40 @@
      %7D%0A%0A        
+ 
+       
 callbacks.remove
@@ -2665,24 +2665,50 @@
 remove(cb);%0A
+            %7D%0A        %7D);%0A
     %7D%0A%0A    p

fix error where game loop actions were executed on wrong thread