package co.touchlab.android.threading.tasks;

import android.app.Application;
import android.content.Context;
import de.greenrobot.event.EventBus;

import java.util.Iterator;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Relatively simple queue implementation.  Supports removing tasks by type.  Simple method of
 * preventing odd results when old messages are put on the queue.
 *
 * Created by kgalligan on 7/5/14.
 */
public class TaskQueue
{
    private static LinkedBlockingQueue<Task> tasks = new LinkedBlockingQueue<Task>();
    private static QueueThread queueThread;
    private static Task currentTask;
    private static Application application;

    static {
        queueThread = new QueueThread();
        queueThread.start();
        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run()
            {
                shutdown();
            }
        });
    }

    public interface Task
    {
        void run(Context context) throws Exception;

        /**
         * Handle Exception that occurred during processing.  Return true if handled, false if not.  If not handled, app will throw and probably crash.
         *
         * @param e
         * @return true if handled, false if not (which will throw it)
         */
        boolean handleError(Exception e);
    }

    private static class QueueThread extends Thread
    {
        @Override
        public void run()
        {
            try
            {
                while (true)
                {

                    Task task = tasks.take();

                    setCurrentTask(task);

                    try
                    {
                        task.run(application);

                        setCurrentTask(null);
                    }
                    catch (Exception e)
                    {
                        boolean handled = task.handleError(e);
                        if (!handled)
                            throw new RuntimeException(e);
                    }

                    if (Thread.interrupted())
                    {
                        throw new InterruptedException();
                    }
                }
            }
            catch (InterruptedException e)
            {
                //
            }
            finally
            {
                killThread();
            }
        }
    }

    private synchronized static Task getCurrentTask()
    {
        return currentTask;
    }

    private synchronized static void setCurrentTask(Task currentTask)
    {
        TaskQueue.currentTask = currentTask;
    }

    private static synchronized void shutdown()
    {
        if(queueThread != null)
            queueThread.interrupt();
    }

    /**
     * Puts a task on the queue.
     *
     * @param context
     * @param task
     */
    public static synchronized void execute(Context context, Task task)
    {
        //repeatedly assigning seems ugly, but should work.
        application = (Application)context.getApplicationContext();
        tasks.add(task);
    }

    /**
     * Makes sure only one task of a type on the queue.  This would be useful on a search screen,
     * for example.  If the first search was processing, and the user clicked search again, the
     * first task shouldn't do anything to the screen when it returns.
     *
     * @param context
     * @param task
     */
    public static synchronized void executeSingleByType(Context context, Task task)
    {
        removeTasksByType(task.getClass());
        execute(context, task);
    }

    private static synchronized void killThread()
    {
        queueThread = null;
    }

    /**
     * Removes tasks of a type from the queue.  Useful if you're worried about old tasks returning unexpectedly,
     * or only want one task of a type running (more precisely, finishing).
     *
     * There is a potential here for issues.  We can't easily block the take portion of the loop,
     * so this may result in unexpected issues.
     * @param c
     */
    public static void removeTasksByType(Class c)
    {
        checkTasksQueue(c);
        checkCurrentTaskForRemoval(c);
    }

    private static synchronized void checkTasksQueue(Class c)
    {
        Iterator<Task> taskIterator = tasks.iterator();
        while (taskIterator.hasNext())
        {
            Task next = taskIterator.next();
            if(c.equals(next.getClass()))
                taskIterator.remove();
        }
    }

    private static synchronized void checkCurrentTaskForRemoval(Class c)
    {
        Task currentTask = getCurrentTask();
        if(currentTask.getClass().equals(c))
            setCurrentTask(null);
    }
}

@@ -139,16 +139,69 @@
 ventBus;
+%0Aimport de.greenrobot.event.SubscriberExceptionEvent;
 %0A%0Aimport
@@ -515,16 +515,21 @@
 ueue.%0A *
+ %3Cp/%3E
 %0A * Crea
@@ -791,24 +791,62 @@
 application;
+%0A    private static EventBus eventBus;
 %0A%0A    static
@@ -845,16 +845,20 @@
   static
+%0A   
  %7B%0A     
@@ -918,24 +918,108 @@
 ad.start();%0A
+        eventBus = new EventBus();%0A        eventBus.register(new ErrorListener());%0A%0A
         Runt
@@ -1063,16 +1063,25 @@
 Thread()
+%0A        
 %7B%0A      
@@ -1605,32 +1605,784 @@
 tion e);%0A    %7D%0A%0A
+    public static class ErrorListener%0A    %7B%0A        public void onEvent(SubscriberExceptionEvent exceptionEvent)%0A        %7B%0A            final Throwable throwable = exceptionEvent.throwable;%0A%0A            //EventBus will just log this.  Would prefer to blow up.%0A            new Thread()%0A            %7B%0A                @Override%0A                public void run()%0A                %7B%0A                    if (throwable instanceof RuntimeException)%0A                        throw (RuntimeException) throwable;%0A                    else if (throwable instanceof Error)%0A                        throw (Error) throwable;%0A                    else%0A                        throw new RuntimeException(throwable);%0A                %7D%0A            %7D.start();%0A        %7D%0A    %7D%0A%0A%0A
     private stat
@@ -3704,16 +3704,17 @@
       if
+ 
 (queueTh
@@ -4041,16 +4041,17 @@
 ication)
+ 
 context.
@@ -4601,32 +4601,196 @@
 , task);%0A    %7D%0A%0A
+    public static void post(Object event)%0A    %7B%0A        eventBus.post(event);%0A    %7D%0A%0A    public static EventBus getEventBus()%0A    %7B%0A        return eventBus;%0A    %7D%0A%0A
     private stat
@@ -5059,32 +5059,37 @@
 nishing).%0A     *
+ %3Cp/%3E
 %0A     * There is
@@ -5219,16 +5219,23 @@
 issues.%0A
+     *%0A
      * @
@@ -5612,16 +5612,17 @@
       if
+ 
 (c.equal
@@ -5831,16 +5831,17 @@
       if
+ 
 (current

Save before trashing