/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.concurrent;

import lombok.experimental.UtilityClass;

import net.ranides.assira.events.EventListener;
import net.ranides.assira.events.Events;
import net.ranides.assira.trace.LoggerUtils;

import javax.swing.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.InvocationTargetException;


/**
 * Utility method for AWT and Swing applications
 */
@UtilityClass
public final class SwingInvoker {
    
    private static final org.slf4j.Logger LOGGER = LoggerUtils.getLogger();

    /**
     * Executes action inside AWT dispatch thread.
     * If method throws exception, information is passed into event handler.
     * Optionally, method can block current thread until action is finished.
     *
     * It can be used inside any thread, including AWT event loop, without risk of deadlock.
     *
     * Please note, that handler is called inside unspecified thread.
     *
     * @param sync if method should block until action is finished
     * @param action runnable
     * @param handler error handler
     */
    public static void run(boolean sync, Runnable action, EventListener<? super Events.Failure> handler) {
        Runnable runnable = () -> {
            try {
                action.run();
            } catch(Exception e) {
                handler.handleEvent(Events.failure(e));
            }
        };
        try {
            if(sync) {
                if( EventQueue.isDispatchThread() ) {
                    runnable.run();
                } else {
                    SwingUtilities.invokeAndWait(runnable);
                }
            } else {
                SwingUtilities.invokeLater(runnable);
            }
        } catch(InterruptedException cause) {
            handler.handleEvent(Events.failure(cause));
        } catch (InvocationTargetException cause) {
            throw new AssertionError("FATAL ERROR", cause);
        }
    }

    /**
     * Executes action inside AWT dispatch thread.
     * If method throws exception, information is logged using slf4j.
     * Optionally, method can block current thread until action is finished.
     *
     * It can be used inside any thread, including AWT event loop, without risk of deadlock.
     *
     * @param sync if method should block until action is finished
     * @param action runnable
     */
    public static void run(boolean sync, Runnable action) {
        run(sync, action, event -> {
            LOGGER.error("Unexpected exception in AWT thread. Action: "+action, event.cause());
        });
    }

}
