/*
 * @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.util.Objects;

/**
 * This class represents binding between event type and event listener.
 *
 * Event listeners can subscribe for specific types of events.
 * Every subscription is represented by this class and can be retrieved from EventRouter
 * using {@link EventRouter#getEventListeners()}
 *
 * @author ranides
 * @param <T> T
 */
public interface EventBinding<T extends Event> {

    /**
     * Type of accepted events. All subclasses will be accepted too.
     *
     * @return type
     */
    Class<T> event();

    /**
     * EventListener processing received events.
     *
     * Plase note, that listener could be much more sophisticated, than ordinary function.
     * It could be for example EventRouter or EventObserver.
     *
     * @return listener
     */
    EventListener<? super T> listener();

    /**
     * Creates new immutable object represeting specified binding.
     *
     * @param event event
     * @param listener listener
     * @param <TT> TT
     * @return binding
     */
    static <TT extends Event> EventBinding<TT> bind(Class<TT> event, EventListener<? super TT> listener) {
        return new Immutable<>(event, listener);
    }

    /**
     * Default immutable implementation of EventBinding.
     *
     * @param <TT> TT
     */
    final class Immutable<TT extends Event> implements EventBinding<TT> {

        private final Class<TT> event;
        private final EventListener<? super TT> listener;

        /**
         * Creates new immutable object represeting specified binding.
         *
         * @param event event
         * @param listener listener
         */
        public Immutable(Class<TT> event, EventListener<? super TT> listener) {
            this.event = event;
            this.listener = listener;
        }

        @Override
        public Class<TT> event() {
            return event;
        }

        @Override
        public EventListener<? super TT> listener() {
            return listener;
        }

        @Override
        public int hashCode() {
            return event.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if(!(obj instanceof EventBinding<?>)) {
                return false;
            }
            final EventBinding<?> other = (EventBinding<?>) obj;
            return 
                Objects.equals(event(), other.event()) &&
                Objects.equals(listener(), other.listener());
        }

        @Override
        public String toString() {
            return "{ event=" + event.getSimpleName() + " listener=" + listener + " }";
        }

    }

}
