package xdean.jex.util.task;

import static xdean.jex.util.function.FunctionAdapter.supplierToRunnable;

import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;

import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import rx.Observable;
import rx.schedulers.Schedulers;
import xdean.jex.extra.Either;
import xdean.jex.extra.Wrapper;
import xdean.jex.extra.function.RunnableThrow;
import xdean.jex.extra.function.SupplierThrow;

@Slf4j
@UtilityClass
public class TaskUtil {

  public void async(Runnable task) {
    Observable.just(task).observeOn(Schedulers.newThread()).subscribe(r -> r.run());
  }

  public void uncheck(RunnableThrow<?> task) {
    try {
      task.run();
    } catch (Throwable t) {
      throw new RuntimeException(t);
    }
  }

  public <T> T uncheck(SupplierThrow<T, ?> task) {
    return supplierToRunnable(task, r -> uncheck(r));
  }

  public boolean uncatch(RunnableThrow<?> task) {
    try {
      task.run();
      return true;
    } catch (Throwable t) {
      log.trace("Dont catch", t);
      return false;
    }
  }

  /**
   * 
   * @param task
   * @return can be null
   */
  public <T> T uncatch(SupplierThrow<T, ?> task) {
    return supplierToRunnable(task, r -> uncatch(r));
  }

  @SuppressWarnings("unchecked")
  public <E extends Throwable> Optional<E> throwToReturn(RunnableThrow<E> task) {
    try {
      task.run();
    } catch (Throwable t) {
      try {
        return Optional.of((E) t);
      } catch (ClassCastException cce) {
        throw new RuntimeException("An unexcepted exception thrown.", t);
      }
    }
    return Optional.empty();
  }

  public <T, E extends Exception> Either<T, E> throwToReturn(SupplierThrow<T, E> task) {
    Wrapper<T> w = new Wrapper<T>(null);
    return Either.rightOrDefault(throwToReturn(() -> w.set(task.get())), w.get());
  }

  public void todoAll(Runnable... tasks) {
    for (Runnable task : tasks) {
      task.run();
    }
  }

  /**
   * Return the first NonNull result of these tasks
   * 
   * @param tasks
   * @return can be null
   */
  @SafeVarargs
  public <T> T firstSuccess(SupplierThrow<T, ?>... tasks) {
    for (SupplierThrow<T, ?> task : tasks) {
      T result = uncatch(task);
      if (result != null) {
        return result;
      }
    }
    return null;
  }

  /**
   * Run the given tasks until any exception happen
   * 
   * @param tasks
   * @return the exception
   */
  @SuppressWarnings("unchecked")
  @SafeVarargs
  public <T extends Throwable> Optional<T> firstFail(RunnableThrow<T>... tasks) {
    for (RunnableThrow<T> task : tasks) {
      try {
        task.run();
      } catch (Throwable t) {
        try {
          return Optional.of((T) t);
        } catch (ClassCastException cce) {
          throw new RuntimeException("An unexcepted exception thrown.", t);
        }
      }
    }
    return Optional.empty();
  }

  public void andFinal(Runnable task, Runnable then) {
    try {
      task.run();
    } finally {
      then.run();
    }
  }

  public <T> T andFinal(Supplier<T> task, Consumer<T> then) {
    T t = null;
    try {
      return t = task.get();
    } finally {
      then.accept(t);
    }
  }

  public boolean ifTodo(boolean b, Runnable todo) {
    if (b) {
      todo.run();
    }
    return b;
  }

  public boolean ifTodo(boolean b, Runnable todo, Runnable elseTodo) {
    if (b) {
      todo.run();
    } else {
      elseTodo.run();
    }
    return b;
  }
}

@@ -202,24 +202,59 @@
 upplier;%0D%0A%0D%0A
+import lombok.AllArgsConstructor;%0D%0A
 import lombo
@@ -3387,41 +3387,235 @@
 %0D%0A  
-public boolean ifTodo(boolean b, 
+@AllArgsConstructor%0D%0A  public class IfWrapper %7B%0D%0A    boolean condition;%0D%0A%0D%0A    public IfWrapper todo(Runnable r) %7B%0D%0A      if (condition) %7B%0D%0A        r.run();%0D%0A      %7D%0D%0A      return this;%0D%0A    %7D%0D%0A%0D%0A    public IfWrapper otherwise(
 Runn
@@ -3619,20 +3619,17 @@
 unnable 
-todo
+r
 ) %7B%0D%0A   
@@ -3621,37 +3621,48 @@
 nable r) %7B%0D%0A    
+  
 if (
-b
+!condition
 ) %7B%0D%0A      todo.
@@ -3648,36 +3648,35 @@
 ition) %7B%0D%0A      
-todo
+  r
 .run();%0D%0A    %7D%0D%0A
@@ -3664,35 +3664,39 @@
   r.run();%0D%0A    
+ 
+ 
 %7D%0D%0A
+  
     return b;%0D%0A 
@@ -3694,19 +3694,26 @@
 urn 
-b
+this
 ;%0D%0A  
+ 
+ 
 %7D%0D%0A%0D%0A
+  
   pu
@@ -3725,16 +3725,179 @@
 boolean 
+toBoolean() %7B%0D%0A      return condition;%0D%0A    %7D%0D%0A  %7D%0D%0A%0D%0A  public IfWrapper ifThat(boolean b) %7B%0D%0A    return new IfWrapper(b);%0D%0A  %7D%0D%0A%0D%0A  @Deprecated%0D%0A  public boolean 
 ifTodo(b
@@ -3908,23 +3908,8 @@
 an b
-, Runnable todo
 , Ru
@@ -3915,21 +3915,17 @@
 unnable 
-elseT
+t
 odo) %7B%0D%0A
@@ -3932,93 +3932,207 @@
     
-if (b) %7B%0D%0A      todo.run();%0D%0A    %7D else %7B%0D%0A      elseTodo.run();%0D%0A    %7D%0D%0A    return b
+return ifThat(b).todo(todo).toBoolean();%0D%0A  %7D%0D%0A%0D%0A  @Deprecated%0D%0A  public boolean ifTodo(boolean b, Runnable todo, Runnable elseTodo) %7B%0D%0A    return ifThat(b).todo(todo).otherwise(elseTodo).toBoolean()
 ;%0D%0A 

add IfWrapper