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

import java.io.IOException;
import java.io.Serializable;

import lombok.experimental.UtilityClass;
import net.ranides.assira.io.IOEvent;

/**
 * Intrinsic event types, used by all routers for lifetime management.
 *
 * @author ranides
 */
@UtilityClass
public class Events {

    /**
     * Creates {@link Dispose} event for specified router.
     *
     * @param source source
     * @return event
     */
    public static Dispose dispose(EventRouter source) {
        return new Dispose(source);
    }

    /**
     * Creates {@link Stop} event for specified router.
     *
     * @param source source
     * @return event
     */
    public static Stop stop(EventRouter source) {
        return new Stop(source);
    }

    /**
     * Creates {@link Shutdown} event for specified router.
     *
     * @param source source
     * @return event
     */
    public static Shutdown shutdown(EventRouter source) {
        return new Shutdown(source);
    }

    /**
     * Creates {@link Interrupt} event for specified router.
     *
     * @param source source
     * @return event
     */
    public static Interrupt interrupt(EventRouter source) {
        return new Interrupt(source);
    }

    /**
     * Creates {@link Failure} event for specified exception.
     *
     * @param cause cause
     * @return event
     */
    public static Failure failure(Throwable cause) {
        if(cause instanceof IOException) {
            return IOEvent.failure((IOException)cause);
        }
        return new Failure(cause);
    }

    /**
     * Dispose event.
     *
     * It is emitted by router which is about to be disposed.
     * All registered listeners are informed about end of lifetime by this event.
     *
     * Please note, that this event is processed in unique way: routers could decide to
     * process it using simplest strategies available at time of disposal: for example run
     * all code inside current thread, instead of using message queues, thread pools etc
     */
    public static class Dispose implements Event {
		
        protected final EventRouter router;

        protected Dispose(EventRouter router) {
            this.router = router;
        }

        /**
         * Router emitted event
         *
         * @return router
         */
        public final EventRouter router() {
            return router;
        }
        
        @Override
        public String toString() {
            return "CoreEvent:Dispose<" + router + ">";
        }
        
    }

    /**
     * Stop event.
     *
     * It is supported by some routers which use background threads.
     *
     * This event signals, that router should terminate all working threads.
     * Event is propagated to all event listeners, as other event types.
     *
     * It stops router only if "router" property is equal to "this".
     */
    public static class Stop extends Dispose {
		
		private static final long serialVersionUID = 1L;
        
        protected Stop(EventRouter router) {
            super(router);
        }
        
        @Override
        public String toString() {
            return "CoreEvent:Stop<" + router + ">";
        }
        
    }

    /**
     * Shutdown event.
     *
     * It is emitted by router which terminated all working threads and released internal resources.
     *
     * Event is propagated to all event listeners.
     *
     * Please note, that this event is processed in unique way: routers could decide to
     * process it using simplest strategies available at time of disposal: for example run
     * all code inside current thread, instead of using message queues, thread pools etc
     *
     * Please note:
     * If router is terminated forcibly, it will emit descendant class {@link Interrupt}.
     */
    public static class Shutdown implements Event {
		
        protected final EventRouter router;

        protected Shutdown(EventRouter router) {
            this.router = router;
        }

        /**
         * Router emitted event
         *
         * @return router
         */
        public final EventRouter router() {
            return router;
        }
        
        @Override
        public String toString() {
            return "CoreEvent:Shutdown<" + router + ">";
        }

    }

    /**
     * Interrupt event.
     *
     * It is emitted by router which terminated all working threads and released internal resources.
     *
     * Event is propagated to all event listeners.
     *
     * Please note, that this event is processed in unique way: routers could decide to
     * process it using simplest strategies available at time of disposal: for example run
     * all code inside current thread, instead of using message queues, thread pools etc
     *
     * Please note:
     * This event is generated if shutdown procedure was caused by {@link InterruptedException}.
     */
    public static class Interrupt extends Shutdown {
		
		private static final long serialVersionUID = 1L;
        
        protected Interrupt(EventRouter name) {
            super(name);
        }
        
        @Override
        public String toString() {
            return "CoreEvent:Interrupt<" + router + ">";
        }
        
    }

    /**
     * This event is used to transport information about exceptions.
     */
    public static class Failure implements Event, Serializable {
		
		private static final long serialVersionUID = 1L;

        private final Throwable cause;

        protected Failure(Throwable cause) {
            this.cause = cause;
        }

        /**
         * Exception thrown by some code
         * @return Exception
         */
        public Throwable cause() {
            return cause;
        }
        
        @Override
        public String toString() {
            return "CoreEvent:Failure: " + cause;
        }
    }

}
