package logbook.data;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

/**
 * イベントをリスナーに送信するクラスです
 *
 */
public final class EventSender {

    private final Map<DataType, List<EventListener>> listenerMap = new EnumMap<>(DataType.class);

    /**
     * リスナーを追加します
     * 
     * @param listener
     */
    public void addEventListener(EventListener listener) {
        DataType[] types = this.getTypes(listener);
        for (DataType key : types) {
            List<EventListener> listeners = this.listenerMap.get(key);

            if (listeners == null) {
                listeners = new ArrayList<>();
                this.listenerMap.put(key, listeners);
            }
            synchronized (listeners) {
                listeners.add(listener);
            }
        }
    }

    /**
     * リスナーを除去します
     * 
     * @param listener
     */
    public void removeEventListener(EventListener listener) {
        DataType[] types = this.getTypes(listener);
        for (DataType key : types) {
            List<EventListener> listeners = this.listenerMap.get(key);

            if (listeners != null) {
                synchronized (listeners) {
                    while (!listeners.remove(listener)) {
                    }
                }
            }
        }
    }

    /**
     * イベントを送信します(同期)
     * 
     * @param type 
     * @param data
     */
    public void syncSendEvent(DataType type, Data data) {
        List<EventListener> listeners = this.listenerMap.get(type);
        if (listeners != null) {
            synchronized (listeners) {
                for (EventListener listener : listeners) {
                    listener.update(type, data);
                }
            }
        }
    }

    /**
     * リスナーのターゲット注釈からデータの種類を取得します
     * 
     * @param listener
     * @return
     */
    private DataType[] getTypes(EventListener listener) {
        EventTarget target = listener.getClass().getAnnotation(EventTarget.class);
        if (target == null) {
            throw new IllegalArgumentException("リスナーにEventTarget注釈がありません");
        }
        return target.value();
    }
}
