package com.eactive.eai.transformer.function;

import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

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.lifecycle.LifecycleSupport;
import com.eactive.eai.common.util.ApplicationContextProvider;
import com.eactive.eai.transformer.dao.FunctionDao;
import com.eactive.eai.transformer.util.Logger;
import com.eactive.eai.transformer.util.MemoryLogger;

@Component
public class FunctionManager implements Lifecycle {

	protected static Logger logger = Logger.getLogger("transformer");
	private Map<String, FunctionDefinition> functionMap;
	private LifecycleSupport lifecycleSupport = null;
	private boolean started;
	
	@Autowired
	private FunctionDao dao;

	private FunctionManager() {
		functionMap = new TreeMap<>();
		lifecycleSupport = new LifecycleSupport(this);
	}

	public static FunctionManager getInstance() {
		return ApplicationContextProvider.getContext().getBean(FunctionManager.class);
	}

	public synchronized void reload() {
		init();
		if (logger.isDebugEnabled()) {
			logger.debug("FunctionManager | reloaded all functions");
		}
	}

	public synchronized void reload(String functionName) {
		FunctionDefinition functionDefinition = dao.findFunctionByName(functionName);
		functionMap.put(functionName, functionDefinition);
		if (logger.isDebugEnabled()) {
			logger.debug("FunctionManager | reloaded function - " + functionName);
		}
	}

	private void clear() {
		functionMap.clear();
	}

	public FunctionDefinition getFunctionDefinition(String name) {
		return functionMap.get(name);
	}

	public void remove(String functionName) {
		functionMap.remove(functionName);
	}

	private void init() {
		functionMap = dao.load();

		logger.info("FunctionManager | all functions are initialized");
		MemoryLogger.txlog("FunctionManager | all functions are initialized");
	}

	public void init(Map<String, FunctionDefinition> functionMap) {
		this.functionMap = functionMap;
	}

	public void addLifecycleListener(LifecycleListener listener) {
		lifecycleSupport.addLifecycleListener(listener);
	}

	public LifecycleListener[] findLifecycleListeners() {
		return lifecycleSupport.findLifecycleListeners();
	}

	public void removeLifecycleListener(LifecycleListener listener) {
		lifecycleSupport.removeLifecycleListener(listener);
	}

	public synchronized void start() throws LifecycleException {
		try {
			init();
			this.started = true;
		} catch (Exception e) {
			throw new LifecycleException(e);
		}
	}

	public synchronized void stop() throws LifecycleException {
		try {
			clear();
			this.started = false;
		} catch (Exception e) {
			throw new LifecycleException(e);
		}
	}

	public boolean isStarted() {
		return this.started;
	}

	public String[] getAllFunctionNames() {
		Iterator<String> it = this.functionMap.keySet().iterator();
		String[] funcNames = new String[this.functionMap.size()];
		for (int i = 0; it.hasNext(); i++) {
			funcNames[i] = it.next();
		}
		Arrays.sort(funcNames);
		return funcNames;
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("[FunctionManager] toString()");

		Object[] keys = functionMap.keySet().toArray();

		for (int i = 0; i < functionMap.size(); i++) {
			sb.append("\n    " + keys[i] + " : " + functionMap.get(keys[i]));
		}

		return sb.toString();
	}
}
