package org.lantern;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import org.cometd.bayeux.Channel;
import org.cometd.bayeux.Message;
import org.cometd.bayeux.client.ClientSession;
import org.cometd.bayeux.client.ClientSessionChannel;
import org.cometd.bayeux.client.ClientSessionChannel.MessageListener;
import org.cometd.client.BayeuxClient;
import org.cometd.client.transport.ClientTransport;
import org.cometd.client.transport.LongPollingTransport;
import org.eclipse.jetty.client.HttpClient;
import org.junit.Test;
import org.lantern.event.Events;
import org.lantern.event.UpdateEvent;
import org.lantern.http.JettyLauncher;
import org.lantern.state.StaticSettings;

public class CometDTest {

    @Test
    public void test() throws Exception {
        final int port = LanternUtils.randomPort();
        StaticSettings.setApiPort(port);
        //RuntimeSettings.setApiPort(port);
        //LanternHub.settings().setApiPort(LanternUtils.randomPort());
        //final int port = LanternHub.settings().getApiPort();
        startJetty(TestUtils.getJettyLauncher(), port);
        final HttpClient httpClient = new HttpClient();
        // Here set up Jetty's HttpClient, for example:
        // httpClient.setMaxConnectionsPerAddress(2);
        httpClient.start();

        // Prepare the transport
        final Map<String, Object> options = new HashMap<String, Object>();
        final ClientTransport transport = 
            LongPollingTransport.create(options, httpClient);

        final ClientSession session = 
            new BayeuxClient("http://127.0.0.1:"+port+"/cometd", transport);
        
        final AtomicBoolean handshake = new AtomicBoolean(false);
        session.getChannel(Channel.META_HANDSHAKE).addListener(
            new ClientSessionChannel.MessageListener() {
                @Override
                public void onMessage(final ClientSessionChannel channel,
                    final Message message) {
                    if (message.isSuccessful()) {
                        // Here handshake is successful
                        handshake.set(true);
                    }
                }
            });
        session.handshake();
        waitForBoolean(handshake);
        assertTrue("Could not handshake?", handshake.get());
        
        final AtomicBoolean hasMessage = new AtomicBoolean(false);
        final AtomicReference<String> messagePath = new AtomicReference<String>("none");
        final MessageListener ml = new MessageListener() {
            
            @Override
            public void onMessage(final ClientSessionChannel channel, 
                final Message message) {
                final Map<String, Object> map = message.getDataAsMap();
                //data.set(map);
                
                final String path = (String) map.get("path");
                messagePath.set(path);
                
                hasMessage.set(true);
            }
        };
        //final AtomicBoolean sync = new AtomicBoolean(false);
        //final AtomicReference<String> transferPathKey = new AtomicReference<String>("");
        subscribe (session, ml);
        waitForBoolean(hasMessage);
        assertEquals("", messagePath.get());
        hasMessage.set(false);
        messagePath.set("none");
        
        final Map<String,Object> updateJson = 
            new LinkedHashMap<String,Object>();
        updateJson.put(LanternConstants.UPDATE_VERSION_KEY, 0.20);
        updateJson.put(LanternConstants.UPDATE_RELEASED_KEY, 
            "2012-10-31T11:15:00Z");
        updateJson.put(LanternConstants.UPDATE_URL_KEY, 
            "http://s3.amazonaws.com/lantern/latest.dmg");
        updateJson.put(LanternConstants.UPDATE_MESSAGE_KEY, 
            "test update");
        
        Events.asyncEventBus().post(new UpdateEvent(updateJson));
        
        waitForBoolean(hasMessage);
        assertEquals("version.latest", messagePath.get());
        hasMessage.set(false);
        messagePath.set("none");
    }

    private AtomicReference<Map<String, Object>> subscribe(
        final ClientSession session, final MessageListener ml) {
        
        final AtomicReference<Map<String, Object>> data = 
            new AtomicReference<Map<String,Object>>();
        
        session.getChannel("/sync").subscribe(ml);
        return data;
    }

    private void waitForBoolean(final AtomicBoolean bool) 
        throws InterruptedException {
        int tries = 0;
        while (tries < 200) {
            if (bool.get()) {
                break;
            }
            tries++;
            Thread.sleep(100);
        }
        assertTrue("Expected variable to be true", bool.get());
    }

    private void startJetty(final JettyLauncher jl, final int port) throws Exception {
        // The order of getting things from the injector matters unfortunately,
        // so we have to do the below.
        //injector.getInstance(DefaultXmppHandler.class);
        //final JettyLauncher jl = injector.getInstance(JettyLauncher.class); 
        final Runnable runner = new Runnable() {
            @Override
            public void run() {
                jl.start();
            }
        };
        final Thread jetty = new Thread(runner, "Jetty-Test-Thread");
        jetty.setDaemon(true);
        jetty.start();
        //Thread.sleep(5000);
        waitForServer(port);
    }

    private void waitForServer(final int port) throws Exception {
        int attempts = 0;
        boolean connected = false;
        while (attempts < 200 && connected == false) {
            final Socket sock = new Socket();
            try {
                sock.connect(new InetSocketAddress("127.0.0.1", port), 1000);
                connected = true;
                System.err.println("Got connected!!");
            } catch (final IOException e) {
                //e.printStackTrace();
            }
            Thread.sleep(100);
            attempts++;
        }
    }

}

@@ -2947,32 +2947,141 @@
 sage message) %7B%0A
+                Object%5B%5D data = (Object%5B%5D) message.getData();%0A                @SuppressWarnings(%22unchecked%22)%0A
                 
@@ -3116,30 +3116,37 @@
 p = 
-message.getDataAsMap()
+(Map%3CString, Object%3E) data%5B0%5D
 ;%0A  
@@ -3176,33 +3176,16 @@
 t(map);%0A
-                %0A
         
@@ -4306,16 +4306,17 @@
 Equals(%22
+/
 version.

fix test