/*
 * Copyright (c) 2020. Lemon Healthcare
 *
 *
 */

package com.lemonhc.mplus.barcode;

import android.util.Log;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BarcodeEventHandler {
    private static final int MAX_THREAD_POOL = 5;

    private static final Logger LOGGER = LoggerFactory.getLogger(BarcodeEventHandler.class);

    /**
     * Note : ArrayList may occur ConcurrentModificationException so using
     * CopyOnWriteArrayList for prevent Exception based on multi thread. Do not
     * use below source code. private static List<ChatBotEventListener> listeners = new ArrayList<ChatBotEventListener>();
     */
    private static List<BarcodeEventListener> listeners = new CopyOnWriteArrayList<BarcodeEventListener>();

    private static synchronized List<BarcodeEventListener> getListeners() {
        return listeners;
    }

    public static synchronized void addListener(BarcodeEventListener eventListener) {
        if (getListeners().indexOf(eventListener) == -1) {
            listeners.add(eventListener);
        }
    }

    public static synchronized void removeListener(BarcodeEventListener eventListener) {
        if (getListeners().indexOf(eventListener) != -1) {
            listeners.remove(eventListener);
        }
    }

    public static synchronized void callEvent(final Class<?> caller, String event) {
        callEvent(caller, event, true);
    }

    public static synchronized void callEvent(final Class<?> caller, String event, boolean doAsync) {
        if (doAsync) {
            callEventByAsync(caller, event);
        } else {
            callEventBySync(caller, event);
        }
    }

    private static synchronized void callEventByAsync(final Class<?> caller, final String event) {
        ExecutorService executorService = Executors.newFixedThreadPool(MAX_THREAD_POOL);

        LOGGER.info("[Event occur : <{}> by <{}>]", new Object[] { event, caller.getName() });

        for (final BarcodeEventListener listener : listeners) {
            executorService.execute(new Runnable() {
                public void run() {
                    if (listener.getClass().getName().equals(caller.getName())) {
                        LOGGER.info("[Event skip : <{}> by self <{}>]", new Object[] { event, caller.getName() });
                    } else {
                        LOGGER.info("[Event catch : <{}> by <{}>]", new Object[] { event, listener.getClass().getName() });
                        Log.d("ChatBotEventListener", "event: " + event);
                        listener.onEvent(event);
                    }
                }
            });
        }

        executorService.shutdown();
    }

    private static synchronized void callEventBySync(final Class<?> caller, final String event) {
        LOGGER.info("[Event occur : <{}> by <{}>]", new Object[] { event, caller.getName() });

        for (final BarcodeEventListener listener : listeners) {
            if (listener.getClass().getName().equals(caller.getName())) {
                LOGGER.info("[Event skip : <{}> by self <{}>]", new Object[] { event, caller.getName() });
            } else {
                LOGGER.info("[Event catch : <{}> by <{}>]", new Object[] { event, listener.getClass().getName() });

                listener.onEvent(event);
            }
        }
    }
}
