package de.is24.util.monitoring.tools;

import de.flapdoodle.embed.process.runtime.Network;
import org.junit.rules.ExternalResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import static java.lang.String.format;
import static java.lang.Thread.sleep;


/**
 * Mock for a statsd server using non-blocking IO according to http://www.onjava.com/pub/a/onjava/2002/09/04/nio.html?page=2
 */
public class StatsdMockServer extends ExternalResource implements Runnable {
  private static final Logger LOG = LoggerFactory.getLogger(StatsdMockServer.class);

  private ServerSocketChannel server;
  private int port;
  private Selector selector;
  private Thread thread;

  @Override
  public void before() throws Throwable {
    server = ServerSocketChannel.open();
    server.configureBlocking(false);

    int maxTries = 3;
    int sleepInSec = 2;
    for (int i = 0; i < maxTries; i++) {
      try {
        port = Network.getFreeServerPort();
        server.socket().bind(new InetSocketAddress(port));
        LOG.info("StatsdMockServer started : port={}", port);
      } catch (Throwable e) {
        int lastTry = maxTries - 1;
        if (i < lastTry) {
          LOG.warn(format("Starting StatsdMockServer: try %s of %s failed. Retry after %s seconds.", i + 1, maxTries,
              sleepInSec), e);
          sleep(sleepInSec * 1000);
        } else {
          LOG.error(format("Starting StatsdMockServer: %s of %s failed. No retries left.", i + 1, maxTries));
          throw e;
        }
      }
    }

    selector = Selector.open();
    server.register(selector, SelectionKey.OP_ACCEPT);

    thread = new Thread(this);
    thread.start();
  }

  @Override
  public void after() {
    try {
      thread.interrupt();
    } catch (Exception e) {
    }
    try {
      selector.close();
    } catch (IOException e) {
    }
    try {
      server.close();
    } catch (Exception e) {
    }
  }

  public int getPort() {
    return port;
  }

  @Override
  public void run() {
    try {
      while (!thread.isInterrupted()) {
        if (selector.select() > 0) {
          Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
          while (iterator.hasNext()) {
            SelectionKey key = iterator.next();
            iterator.remove();

            if (!key.isValid()) {
              continue;
            }

            if (key.isAcceptable()) {
              SocketChannel client = server.accept();
              client.configureBlocking(false);
              client.register(selector, SelectionKey.OP_READ);
              continue;
            }

            if (key.isReadable()) {
              SocketChannel client = (SocketChannel) key.channel();
              int BUFFER_SIZE = 32;
              ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
              try {
                client.read(buffer);
              } catch (Exception e) {
                e.printStackTrace();
              }

              continue;
            }
          }
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
    }


  }
}

add StatsdMockServer to simplify integration testing