/*
 * Copyright (c) 10/24/13 7:14 AM Romain Gilles
 *
 * 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.javabits.yar.guice;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import org.javabits.yar.RegistryHook;

import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.Futures.addCallback;

/**
 * Date: 10/24/13
 * @author Romain Gilles
 */
public abstract class AbstractExecutionStrategy implements ExecutionStrategy {
    private static final Logger LOG = Logger.getLogger(ExecutionStrategy.class.getName());

    private final Map<ListenableFuture<Void>, Callable<Void>> pendingTasks = Maps.newConcurrentMap();

    abstract ListeningExecutorService executorService();

    public boolean hasPendingTasks() {
        return !pendingTasks.isEmpty();
    }

    public void addEndOfListenerUpdateTasksListener(final RegistryHook.EndOfListenerUpdateTasksListener pendingTaskLister) {
        final Map<ListenableFuture<Void>, Callable<Void>> pendingTaskSnapshot = new ConcurrentHashMap<>(pendingTasks);
        if (pendingTaskSnapshot.isEmpty()) {
            pendingTaskLister.completed();
            return;
        }
        for (final ListenableFuture<Void> future : pendingTaskSnapshot.keySet()) {
            future.addListener(new Runnable() {
                @Override
                public void run() {
                    pendingTaskSnapshot.remove(future);
                    if (pendingTaskSnapshot.isEmpty())
                        pendingTaskLister.completed();
                }
            }, executorService());
        }
    }

    public void execute(final List<Callable<Void>> tasks, final long timeout, final TimeUnit unit) throws InterruptedException {
        for (final Callable<Void> task : tasks) {
            final ListenableFuture<Void> future = executorService().submit(task);
            pendingTasks.put(future, task);
            future.addListener(new Runnable() {
                @Override
                public void run() {
                    pendingTasks.remove(future);
                }
            }, executorService());

            addCallback(future, new FutureCallback<Void>() {
                @Override
                public void onSuccess(Void result) {
                    LOG.log(Level.FINE, String.format("Listener task succeeded : %s", task));
                }

                @Override
                public void onFailure(Throwable t) {
                    LOG.log(Level.SEVERE, String.format("Listener task failed: %s", task), t);
                }
            });
        }
    }

    private static class DaemonThreadFactory implements ThreadFactory {
        final ThreadGroup group;
        final AtomicInteger threadNumber = new AtomicInteger(1);
        final String namePrefix;
        static final String nameSuffix = "]";

        public DaemonThreadFactory(String poolName) {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
            namePrefix = "YAR " + poolName + " Pool [Thread-";
        }

        @Override
        public Thread newThread(@SuppressWarnings("NullableProblems") Runnable runnable) {
            checkNotNull(runnable, "runnable");
            Thread t = new Thread(group, runnable, namePrefix + threadNumber.getAndIncrement()
                    + nameSuffix, 0);
            t.setDaemon(true);
            // if (t.getPriority() != Thread.NORM_PRIORITY)
            // t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

    static ExecutionStrategy newExecutionStrategy(Type strategy) {
        switch (strategy) {
            case SAME_THREAD:
                return new SameThread();
            case PARALLEL:
                return new Parallel();
            case SERIALIZED:
                return new Serialized();
            default:
                throw new IllegalArgumentException("Unknown strategy type: " + strategy);
        }
    }

    @VisibleForTesting
    private static class SameThread extends AbstractExecutionStrategy {
        private final ListeningExecutorService executorService = MoreExecutors.sameThreadExecutor();

        @Override
        ListeningExecutorService executorService() {
            return executorService;
        }
    }

    private static class Parallel extends AbstractExecutionStrategy {
        private final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors
                .newCachedThreadPool(new DaemonThreadFactory("parallel-listener-handler")));

        @Override
        ListeningExecutorService executorService() {
            return executorService;
        }
    }

    private static class Serialized extends AbstractExecutionStrategy {
        private final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors
                .newSingleThreadExecutor(new DaemonThreadFactory("serialized-listener-handler")));

        @Override
        ListeningExecutorService executorService() {
            return executorService;
        }
    }
}

@@ -2289,91 +2289,16 @@
 ner(
-new Runnable() %7B%0A                @Override%0A                public void run() %7B%0A    
+() -%3E %7B%0A
     
@@ -2357,28 +2357,24 @@
             
-    
 if (pendingT
@@ -2408,36 +2408,32 @@
                 
-    
 pendingTaskListe
@@ -2447,34 +2447,16 @@
 eted();%0A
-                %7D%0A
         
@@ -2839,167 +2839,41 @@
 ner(
-new Runnable() %7B%0A                @Override%0A                public void run() %7B%0A                    pendingTasks.remove(future);%0A                %7D%0A            %7D
+() -%3E pendingTasks.remove(future)
 , ex
@@ -4984,26 +4984,25 @@
 ors.
-sameThread
+newDirect
 Executor
 ();%0A
@@ -4997,16 +4997,23 @@
 Executor
+Service
 ();%0A%0A   

improve code related to java 8