/*
 *    Copyright 2012 Jason Miller
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jj.repl;

import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.util.concurrent.TimeUnit.*;
import static org.junit.Assert.*;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;

import javax.inject.Inject;

import jj.App;
import jj.event.Listener;
import jj.event.Subscriber;
import jj.testing.JibbrJabbrTestServer;

import org.junit.After;
import org.junit.Rule;
import org.junit.Test;

/**
 * @author jason
 *
 */
@Subscriber
public class ReplIntegrationTest {
	
	@Rule
	public JibbrJabbrTestServer testServer = new JibbrJabbrTestServer(App.repl).injectInstance(this);
	
	@Inject ReplConfiguration config;

	Bootstrap bootstrap;
	CountDownLatch latch = new CountDownLatch(1);
	
	@Listener
	void replListening(ReplListening replListening) {
		latch.countDown();
	}
	
	@After
	public void after() {
		if (bootstrap != null) {
			bootstrap.group().shutdownGracefully(0, 0, SECONDS);
		}
	}
	
	@Test
	public void test() throws Throwable {
		assertTrue("timed out waiting for init", latch.await(500, MILLISECONDS));
		
		// well... it started so that's something
		// connect to config.port() and send in some commands? why not
		latch = new CountDownLatch(1);
		
		final AtomicReference<Throwable> failure = new AtomicReference<>();
		
		final StringBuilder response = new StringBuilder()
			.append("Welcome to JibbrJabbr\n>")
			.append("ReferenceError: \"whatever\" is not defined.\n")
			.append("	at repl-console:1\n")
			.append("	at base-repl-system.js:8 ($$print)\n")
			.append("	at repl-console:1\n")
			.append("\n>");
		
		bootstrap = new Bootstrap()
			.group(new NioEventLoopGroup(1))
			.channel(NioSocketChannel.class)
			.handler(new ChannelInitializer<SocketChannel>() {

				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ch.pipeline()
						.addLast(new StringEncoder(US_ASCII))
						.addLast(new StringDecoder(US_ASCII))
						.addLast(new SimpleChannelInboundHandler<String>() {

							@Override
							protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
								if (msg.equals(response.substring(0, msg.length()))) {
									response.delete(0, msg.length());
								}
								if (response.length() == 0) {
									latch.countDown();
								}
							}
						});
				}
			});
		bootstrap.connect("localhost", config.port()).addListener((ChannelFuture future) -> {
			if (future.isSuccess()) {
				future.channel().writeAndFlush("whatever\n");
			} else {
				failure.set(future.cause());
			}
		});
		
		assertTrue("timed out waiting for response", latch.await(1, SECONDS));
		if (failure.get() != null) {
			throw failure.get();
		}
	}

}

@@ -749,17 +749,16 @@
 sert.*;%0A
-%0A
 import i
@@ -1020,54 +1020,8 @@
 up;%0A
-import io.netty.channel.socket.SocketChannel;%0A
 impo
@@ -1170,24 +1170,53 @@
 ngEncoder;%0A%0A
+import java.net.InetAddress;%0A
 import java.
@@ -2790,16 +2790,19 @@
 ializer%3C
+Nio
 SocketCh
@@ -2858,16 +2858,19 @@
 Channel(
+Nio
 SocketCh
@@ -3059,24 +3059,232 @@
 %3CString%3E() %7B
+%0A%09%09%09%09%09%09%09%0A%09%09%09%09%09%09%09@Override%0A%09%09%09%09%09%09%09public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception %7B%0A%09%09%09%09%09%09%09%09failure.set(cause);%0A%09%09%09%09%09%09%09%09ctx.close();%0A%09%09%09%09%09%09%09%09latch.countDown();%0A%09%09%09%09%09%09%09%7D
 %0A%0A%09%09%09%09%09%09%09@Ov
@@ -3536,24 +3536,46 @@
 h() == 0) %7B%0A
+%09%09%09%09%09%09%09%09%09ctx.close();%0A
 %09%09%09%09%09%09%09%09%09lat
@@ -3656,19 +3656,40 @@
 ect(
-%22localhost%22
+InetAddress.getLoopbackAddress()
 , co

make sure the repl test doesn't depend on external dns being available