package com.eactive.eai.adapter.websocket;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.lifecycle.Lifecycle;
import com.eactive.eai.common.lifecycle.LifecycleException;
import com.eactive.eai.common.lifecycle.LifecycleListener;
import com.eactive.eai.common.util.Logger;

public class WebSocketAdapterManager implements Lifecycle {
	static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);

	private static WebSocketAdapterManager instance = new WebSocketAdapterManager();
	private ConcurrentHashMap<String, ConcurrentHashMap<String, WebSocketHandler>> adapterGroups;

	public WebSocketAdapterManager() {
		try {
			adapterGroups = new ConcurrentHashMap<>();
		} catch (Exception e) {
			String code = ExceptionUtil.getErrorCode(e, "RECEAIOIA002");
			throw new RuntimeException(code);
		}
	}

	public static WebSocketAdapterManager getInstance() {
		if (instance == null) {
			instance = new WebSocketAdapterManager();
		}
		return instance;
	}

	public void stop() {
		for (Enumeration<ConcurrentHashMap<String, WebSocketHandler>> e = adapterGroups.elements(); e
				.hasMoreElements();) {
			ConcurrentHashMap<String, WebSocketHandler> group = e.nextElement();
			for (Enumeration<WebSocketHandler> ee = group.elements(); ee.hasMoreElements();) {
				WebSocketHandler adapter = ee.nextElement();
				try {
					adapter.stop();
				} catch (Exception ex) {
					logger.error(adapter.getAdapterVO().getName() + " | " + ex.getMessage(), ex);
				}
			}
		}
	}

	public ConcurrentMap<String, ConcurrentHashMap<String, WebSocketHandler>> getAdapterGroups() {
		return this.adapterGroups;
	}

	public WebSocketHandler[] getAdapter(String adapterFullName) {
		String adapterGroupName = adapterFullName;
		String adapterName = null;
		if (adapterFullName.indexOf('.') != -1) {
			adapterGroupName = adapterFullName.substring(0, adapterFullName.indexOf('.'));
			adapterName = adapterFullName.substring(adapterFullName.indexOf('.') + 1);
		}
		AdapterVO vo = new AdapterVO();
		vo.setAdapterGroupName(adapterGroupName);
		vo.setName(adapterName);
		ConcurrentHashMap<String, WebSocketHandler> adapters = adapterGroups.get(vo.getAdapterGroupName());
		Vector<WebSocketHandler> started = new Vector<>();
		for (Iterator<WebSocketHandler> iter = adapters.values().iterator(); iter.hasNext();) {
			WebSocketHandler adapter = iter.next();
			started.add(adapter);
		}
		WebSocketHandler[] array = (WebSocketHandler[]) started.toArray();
		if (array == null || array.length == 0)
			return null;
		return array;

	}

	public WebSocketHandler getAdapter(AdapterVO vo) {
		ConcurrentHashMap<String, WebSocketHandler> adapters = adapterGroups.get(vo.getAdapterGroupName());
		if (adapters == null)
			return null;

		if (vo.getName() != null) {
			return adapters.get(vo.getName());
		}
		return null;
	}

	public void addAdapter(AdapterVO vo, WebSocketHandler adapter) throws Exception {
		ConcurrentHashMap<String, WebSocketHandler> adapters = adapterGroups.get(vo.getAdapterGroupName());
		if (adapters == null) {
			adapterGroups.put(vo.getAdapterGroupName(), new ConcurrentHashMap<>());
		} else {
			if (adapters.get(vo.getName()) != null) {
				if (logger.isWarn())
					logger.warn(vo.getName() + " | WebSocketAdapterManager - socket adapter already exist -> replace ["
							+ vo.getAdapterGroupName() + ", " + vo.getName() + "]");
				try {
					adapters.get(vo.getName()).stop();
				} catch (Exception e) {
					// adapter.addRecentError(adapter.getContext().getAdapterName() + " | old
					// adapter stop error.", e);
				} finally {
					// sleep 1 second to avoid BindException
					try {
						Thread.sleep(1000L);
					} catch (Exception e) {
						if (logger.isWarnEnabled())
							logger.warn("sleep error", e);
					}
				}
			}
		}
		adapterGroups.get(vo.getAdapterGroupName()).put(vo.getName(), adapter);
	}

	@Override
	public void addLifecycleListener(LifecycleListener listener) {
		// empty
	}

	@Override
	public LifecycleListener[] findLifecycleListeners() {
		return null;
	}

	@Override
	public void removeLifecycleListener(LifecycleListener listener) {
		// empty
	}

	@Override
	public void start() throws LifecycleException {
		// empty
	}

	@Override
	public boolean isStarted() {
		return false;
	}
}
