package com.diguage.truman.net;

import org.junit.Test;

import java.io.IOException;
import java.net.BindException;
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.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2020-06-03 16:08
 */
public class ServerSockerChannelTest {

    @Test(expected = BindException.class)
    public void bindMultiTimes() throws InterruptedException, IOException {
        int port = 11911;
        List<ServerSocketChannel> serverSockets = new ArrayList<>(2);
        for (int i = 0; i < 2; i++) {
            ServerSocketChannel serverSocket = ServerSocketChannel.open();
            serverSockets.add(serverSocket);
            serverSocket.socket().bind(new InetSocketAddress(port));
        }
        Thread.sleep(10 * 1000);
        System.out.println("done.");
    }

    @Test
    public void multiReactor() {

    }

    public static class Reactor implements Runnable {
        final Selector selector;
        ServerSocketChannel serverSocket = null;
        int port;
        boolean isMaster;

        public Reactor(int port, boolean isMaster) throws IOException {
            this.port = port;
            this.isMaster = isMaster;
            this.selector = Selector.open();
            if (isMaster) {
                serverSocket = ServerSocketChannel.open();
                serverSocket.socket().bind(new InetSocketAddress(port));
                serverSocket.configureBlocking(false);
                SelectionKey selectionKey = this.serverSocket.register(selector, SelectionKey.OP_ACCEPT);
                selectionKey.attach(new Object());
                System.out.println("start on " + port + " ...");
            } else {
                // TODO 如何
            }
        }

        private void init() {

        }

        @Override
        public void run() {
            try {
                while (!Thread.interrupted()) {
                    selector.select();
                    Set<SelectionKey> keys = selector.selectedKeys();
                    for (SelectionKey key : keys) {
                        dispatch(key);
                    }
                    keys.clear();
                }
            } catch (IOException e) {

            }
        }

        private void dispatch(SelectionKey key) {
            Runnable run = (Runnable) key.attachment();
            if (Objects.nonNull(run)) {
                run.run();
            }
        }

        class Acceptor implements Runnable {
            @Override
            public void run() {
                try {
                    SocketChannel channel = serverSocket.accept();
                    if (Objects.nonNull(channel)) {
                        new Handler(selector, channel);
                    }
                } catch (IOException e) {

                }
            }
        }

        class Handler implements Runnable {
            final int MAXIN = 1024;
            final int MAXOUT = 1024;
            static final int READING = 0, SENDING = 1;
            int state = READING;

            final SocketChannel socket;
            final SelectionKey selectionKey;
            ByteBuffer input = ByteBuffer.allocate(MAXIN);
            ByteBuffer output = ByteBuffer.allocate(MAXOUT);

            public Handler(Selector selector, SocketChannel channel) throws IOException {
                socket = channel;
                channel.configureBlocking(false);
                selectionKey = socket.register(selector, 0);
                selectionKey.attach(this);
                selectionKey.interestOps(SelectionKey.OP_READ);
                selector.wakeup();
            }

            @Override
            public void run() {

            }
        }
    }

}