package com.jscape.util.event;

import java.util.*;

/**
 * @author Alex Usun
 */
public class Registry {

    private final Map<Class, List> handlers;

    /**
     * Default constructor.
     */
    public Registry() {
        this.handlers = new HashMap<Class, List>();
    }

    /**
     * @param handler    handler
     * @param eventClass eventClass
     * @param <H>        <H>
     */
    public <H extends Event.EventHandler> void bind(H handler, Class<? extends Event<H>> eventClass) {
        synchronized (this.handlers) {
            List<H> copy = copy(getHandlers(eventClass));

            if (!copy.contains(handler)) {
                copy.add(handler);
            }

            this.handlers.put(eventClass, copy);
        }
    }

    /**
     * @param handler    handler
     * @param eventClass eventClass
     * @param <H>        <H>
     */
    public <H extends Event.EventHandler> void unbind(H handler, Class<? extends Event<H>> eventClass) {
        synchronized (this.handlers) {
            List<H> copy = copy(getHandlers(eventClass));

            copy.remove(handler);

            if (!copy.isEmpty()) {
                this.handlers.put(eventClass, copy);
            } else {
                this.handlers.remove(eventClass);
            }
        }
    }

    /**
     * @param handler      handler
     * @param requestClass requestClass
     * @param <H>          <H>
     */
    public <H extends Request.RequestHandler> void bind(H handler, Class<? extends Request<H>> requestClass) {
        synchronized (this.handlers) {
            List<H> copy = copy(getHandlers(requestClass));

            if (!copy.contains(handler)) {
                copy.add(handler);
            }

            this.handlers.put(requestClass, copy);
        }
    }

    /**
     * @param handler      handler
     * @param requestClass requestClass
     * @param <H>          <H>
     */
    public <H extends Request.RequestHandler> void unbind(H handler, Class<? extends Request<H>> requestClass) {
        synchronized (this.handlers) {
            List<H> copy = copy(getHandlers(requestClass));

            copy.remove(handler);

            if (!copy.isEmpty()) {
                this.handlers.put(requestClass, copy);
            } else {
                this.handlers.remove(requestClass);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public void bind(EventHandler handler, Class aClass) {
        synchronized (this.handlers) {
            List copy = copy(getHandlers(aClass));

            if (!copy.contains(handler)) {
                copy.add(handler);
            }

            this.handlers.put(aClass, copy);
        }
    }

    /**
     * @param handler handler
     * @param aClass  aClass
     */
    public void unbind(EventHandler handler, Class aClass) {
        synchronized (this.handlers) {
            List copy = copy(getHandlers(aClass));

            copy.remove(handler);

            if (!copy.isEmpty()) {
                this.handlers.put(aClass, copy);
            } else {
                this.handlers.remove(aClass);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public <H> List<H> getHandlers(Class aClass) {
        return this.handlers.get(aClass);
    }

    /** */
    public void clear() {
        this.handlers.clear();
    }

    @SuppressWarnings("unchecked")
    private <H> List<H> copy(Collection handlers) {
        return handlers != null
               ? new ArrayList<H>(handlers)
               : new ArrayList<H>();
    }
}
