/*
 * JBoss, Home of Professional Open Source.
 *
 * Copyright 2013 Red Hat, Inc. and/or its affiliates, and individual
 * contributors as indicated by the @author tags.
 *
 * 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 org.xnio.mock;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.xnio.ChannelListener;
import org.xnio.ChannelListeners;
import org.xnio.ManagementRegistration;
import org.xnio.LocalSocketAddress;
import org.xnio.OptionMap;
import org.xnio.StreamConnection;
import org.xnio.Xnio;
import org.xnio.XnioWorker;
import org.xnio.channels.AcceptingChannel;
import org.xnio.channels.MulticastMessageChannel;
import org.xnio.management.XnioServerMXBean;
import org.xnio.management.XnioWorkerMXBean;

/**
 * {@link XnioWorker} mock.
 * 
 * @author <a href="mailto:frainone@redhat.com">Flavia Rainone</a>
 */
public class XnioWorkerMock extends XnioWorker {

    /**
     * Extra info set on created channel mocks if the channel mock was created as a tcp channel.
     * @see Mock#getInfo()
     */
    public static final String TCP_CHANNEL_INFO = "tcp";

    /**
     * Extra info set on created channel mocks if the channel mock was created as a udp channel.
     * @see Mock#getInfo()
     */
    public static final String UDP_CHANNEL_INFO = "udp";

    /**
     * Extra info set on created channel mocks if the channel mock was created as a local channel.
     * @see Mock#getInfo()
     */
    public static final String LOCAL_CHANNEL_INFO = "local";

    /**
     * The thread mock.
     */
    private final XnioIoThreadMock mockThread = new XnioIoThreadMock(this);

    // indicates which action should be taken if a request to connect is performed
    enum ConnectBehavior {SUCCEED, FAIL, CANCEL}

    // by default, every request to connectil will succeed
    private ConnectBehavior connectBehavior = ConnectBehavior.SUCCEED;

    // has this worker been shut down
    private boolean shutdown;

    public XnioWorkerMock() {
        this(Xnio.getInstance(), null, OptionMap.EMPTY, null);
    }

    protected XnioWorkerMock(Xnio xnio, ThreadGroup threadGroup, OptionMap optionMap, Runnable terminationTask) {
        this(getBuilderWith(xnio, threadGroup, optionMap, terminationTask));
    }

    public XnioWorkerMock(final Builder builder) {
        super(builder);
    }

    private static Builder getBuilderWith(final Xnio xnio, final ThreadGroup threadGroup, final OptionMap optionMap, final Runnable terminationTask) {
        final Builder builder = xnio.createWorkerBuilder();
        builder.setThreadGroup(threadGroup);
        builder.setTerminationTask(terminationTask);
        builder.populateFromOptions(optionMap);
        return builder;
    }

    @Override
    public int getIoThreadCount() {
        return 0;
    }

    @Override
    public XnioIoThreadMock chooseThread() {
        return mockThread;
    }

    @Override
    public XnioIoThreadMock getIoThread(final int hashCode) {
        return mockThread;
    }

    /**
     * Returns the connect behavior of this worker mock.
     */
    ConnectBehavior getConnectBehavior() {
        return this.connectBehavior;
    }

    /**
     * Sets this worker mock to fail every request to connect. Used for emulating failure to connect behavior.
     */
    public void failConnection() {
        connectBehavior = ConnectBehavior.FAIL;
    }

    /**
     * Sets this worker mock to behave as if every request to connect has been cancelled by a third party, whenever
     * applicable. Used for emulating cancellation of connection futures. 
     */
    public void cancelConnection() {
        connectBehavior = ConnectBehavior.CANCEL;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private AcceptingChannel<StreamConnection> internalCreateStreamServer(SocketAddress bindAddress, ChannelListener<? super AcceptingChannel<StreamConnection>> acceptListener, OptionMap optionMap, String channelInfo) throws IOException {
        AcceptingChannelMock channel = new AcceptingChannelMock();
        channel.setLocalAddress(bindAddress);
        channel.setOptionMap(optionMap);
        channel.setWorker(this);
        channel.setInfo(channelInfo);
        if (connectBehavior == ConnectBehavior.FAIL) channel.enableAcceptance(false);
        ((AcceptingChannel)channel).getAcceptSetter().set(acceptListener);
        return channel;
    }

    @Override
    protected AcceptingChannel<StreamConnection> createTcpConnectionServer(InetSocketAddress bindAddress, ChannelListener<? super AcceptingChannel<StreamConnection>> acceptListener, OptionMap optionMap) throws IOException {
        return internalCreateStreamServer(bindAddress, acceptListener, optionMap, TCP_CHANNEL_INFO);
    }

    @Override
    protected AcceptingChannel<StreamConnection> createLocalStreamConnectionServer(LocalSocketAddress bindAddress, ChannelListener<? super AcceptingChannel<StreamConnection>> acceptListener, OptionMap optionMap) throws IOException {
        return internalCreateStreamServer(bindAddress, acceptListener, optionMap, LOCAL_CHANNEL_INFO);
    }

    @Override
    public MulticastMessageChannel createUdpServer(InetSocketAddress bindAddress, ChannelListener<? super MulticastMessageChannel> bindListener, OptionMap optionMap) throws IOException {
        MulticastMessageChannel channel = new MulticastMessageChannelMock(bindAddress, optionMap);
        ChannelListeners.invokeChannelListener(channel, bindListener);
        return channel;
    }

    @Override
    public void shutdown() {
        shutdown = true;
    }

    @Override
    public List<Runnable> shutdownNow() {
        throw new RuntimeException("not implemented");
    }

    @Override
    public boolean isShutdown() {
        throw new RuntimeException("not implemented");
    }

    @Override
    public boolean isTerminated() {
        throw new RuntimeException("not implemented");
    }

    @Override
    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        if (shutdown) {
            return true;
        }
        return false;
    }

    @Override
    public void awaitTermination() throws InterruptedException {
    }

    @Override
    public XnioWorkerMXBean getMXBean(){
        return null;
    }

    @Override
    protected ManagementRegistration registerServerMXBean(XnioServerMXBean metrics) {
        return null;
    }
}
