/*
 * #%L
 * Wildfly Camel :: Testsuite
 * %%
 * Copyright (C) 2013 - 2015 RedHat
 * %%
 * 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.
 * #L%
 */

package org.wildfly.camel.test.threading.subA;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;
import javax.enterprise.concurrent.ManagedThreadFactory;

import org.apache.camel.spi.ThreadPoolFactory;
import org.apache.camel.spi.ThreadPoolProfile;
import org.apache.camel.util.concurrent.RejectableScheduledThreadPoolExecutor;
import org.apache.camel.util.concurrent.RejectableThreadPoolExecutor;
import org.apache.camel.util.concurrent.SizedScheduledExecutorService;

/**
 * Custom ThreadPoolFactory.
 *
 * Implementation is mostly identical to the Camel DefaultThreadPoolFactory
 * but this class uses the container ManagedThreadFactory
 */
public class WildFlyCamelThreadPoolFactory implements ThreadPoolFactory {

    @Resource
    private ManagedThreadFactory managedThreadFactory;

    public ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return Executors.newCachedThreadPool(managedThreadFactory);
    }

    @Override
    public ExecutorService newThreadPool(ThreadPoolProfile profile, ThreadFactory factory) {
        boolean allow = profile.getAllowCoreThreadTimeOut() != null?profile.getAllowCoreThreadTimeOut().booleanValue():false;
        return this.newThreadPool(profile.getPoolSize().intValue(),
                                  profile.getMaxPoolSize().intValue(),
                                  profile.getKeepAliveTime().longValue(),
                                  profile.getTimeUnit(),
                                  profile.getMaxQueueSize().intValue(),
                                  allow,
                                  profile.getRejectedExecutionHandler(),
                                  managedThreadFactory);
    }

    public ExecutorService newThreadPool(int corePoolSize, int maxPoolSize,
                 long keepAliveTime, TimeUnit timeUnit, int maxQueueSize, boolean allowCoreThreadTimeOut, RejectedExecutionHandler
                 rejectedExecutionHandler, ThreadFactory threadFactory) throws IllegalArgumentException {
        if(corePoolSize < 0) {
            throw new IllegalArgumentException("CorePoolSize must be >= 0, was " + corePoolSize);
        } else if(maxPoolSize < corePoolSize) {
            throw new IllegalArgumentException("MaxPoolSize must be >= corePoolSize, was " + maxPoolSize + " >= " + corePoolSize);
        } else {
            Object workQueue;
            if(corePoolSize == 0 && maxQueueSize <= 0) {
                workQueue = new SynchronousQueue();
                corePoolSize = 1;
                maxPoolSize = 1;
            } else if(maxQueueSize <= 0) {
                workQueue = new SynchronousQueue();
            } else {
                workQueue = new LinkedBlockingQueue(maxQueueSize);
            }

            RejectableThreadPoolExecutor answer = new RejectableThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, timeUnit, (BlockingQueue)workQueue);
            answer.setThreadFactory(managedThreadFactory);
            answer.allowCoreThreadTimeOut(allowCoreThreadTimeOut);
            if(rejectedExecutionHandler == null) {
                rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
            }

            answer.setRejectedExecutionHandler(rejectedExecutionHandler);
            return answer;
        }
    }
    @Override
    public ScheduledExecutorService newScheduledThreadPool(ThreadPoolProfile profile, ThreadFactory threadFactory) {
        RejectedExecutionHandler rejectedExecutionHandler = profile.getRejectedExecutionHandler();
        if (rejectedExecutionHandler == null) {
            rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
        }

        ScheduledThreadPoolExecutor answer = new RejectableScheduledThreadPoolExecutor(
            profile.getPoolSize(), managedThreadFactory, rejectedExecutionHandler);

        if (profile.getMaxPoolSize() > 0) {
            return new SizedScheduledExecutorService(answer, profile.getMaxQueueSize());
        } else {
            return answer;
        }
    }
}
