package com.eactive.eai.transformer.transform;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

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

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.jdbc.TransformJdbcDao;
import com.eactive.eai.transformer.util.Constants;
import com.eactive.eai.transformer.util.Logger;
import com.eactive.eai.transformer.util.MemoryLogger;

@Component
public class TransformManager implements Lifecycle {
    private static Logger logger = Logger.getLogger("transformer");
    private LifecycleSupport lifecycleSupport = null;
    private boolean started;
    private Map<String, Transform> transformMap;
    
    @Autowired
    private TransformJdbcDao transformJdbcDao;

    protected TransformManager() {
        transformMap = new HashMap<>();
        lifecycleSupport = new LifecycleSupport(this);
    }

    public static TransformManager getManager() {
        return ApplicationContextProvider.getContext().getBean(TransformManager.class);
    }

     public synchronized void reload() {
         init();
     	 logger.info("TransformManager | all transforms are reloaded");
     	 MemoryLogger.txlog("TransformManager | all transforms are reloaded");
     }
     
     public synchronized void reload(String transformName) {
         Transform transform = transformJdbcDao.findTransformByName(transformName);
         if ("1".equals(transform.getUseYn())){
        	 transformMap.put(transformName, transform);
         }else{
        	 logger.warn("reload Transrform["+transformName+"] useYn["+transform.getUseYn()+"] removed");
        	 transformMap.remove(transformName);
         }
         
         logger.info("TransformManager | reloaded - " + transformName);
     	 MemoryLogger.txlog("transform reloaded - " + transform.toString());        
     }
     
     public synchronized void remove(String transformName) {
    	 transformMap.remove(transformName);
     }
     
     public String[] getAllTransformName() {
         Iterator<String> it = transformMap.keySet().iterator();
         
         String[] keys = new String [transformMap.size()];
         
         for (int i=0; i<keys.length; i++) {
             keys[i] = it.next();   
         }
         Arrays.sort(keys);
         return keys;
     }
     
    
    public Map<String, Transform> getTransforms() {
    	return transformMap;
    }
    
    public Transform getTransform(String transformName) {
    	if(transformName == null) {
    		return null;
    	}
        return transformMap.get(transformName);
    }

    public Set<String> getTransformNameSet() {
        return new TreeSet<>(transformMap.keySet());
    }

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

    private void init() {
    	try {
    		logger.info("TransformManager | init() start");
	        
	        logger.info("TransformManager | dao.load() start");
	        
	        transformMap = transformJdbcDao.load();
	        
	        logger.info("TransformManager | dao.load() done");
	        
	        ExecutorService executor = new ThreadPoolExecutor(10, 10, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
	        final CountDownLatch latch  = new CountDownLatch(transformMap.size());

	        for (final Iterator<String> it = transformMap.keySet().iterator(); it.hasNext();) {
	            final String transformName = it.next();
	            
	            Runnable runnableTask = () -> {
	        		Transform transform = null;
		        	try {
		        		transform = transformMap.get(transformName);
		        		TransformValidator.validate(transform,false);
		        	} catch(Exception e) {
		    			logger.error("TransformManager | validate failed - " + transformName, e);
		        		MemoryLogger.error("TransformManager | validate failed - " + transformName, e);
		        	} finally {
		        		latch.countDown();
		        	}
	            };
	            executor.execute(runnableTask);
	            
	        }
	        latch.await();
	        executor.shutdown();
	
	    	logger.info("TransformManager | all transfroms are initialized - transformMap size[" 
	    			+ transformMap.size()+"]");
	    	MemoryLogger.txlog("TransformManager | all transfroms are initialized");
    	} catch (InterruptedException e) {
    		logger.error("TransformManager | transforms initialization failed", e);
	    	Thread.currentThread().interrupt();
    	} catch(Exception e) {
	    	logger.error("TransformManager | transforms initialization failed", e);
	    	MemoryLogger.error("TransformManager | transforms initialization failed", e);
    	}
    }
    
    public void init(Map<String, Transform> transformMap) {
    	this.transformMap = transformMap;
    	TransformValidator.validate(transformMap);
    }

    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 toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("TransformManager | toString() - \n");

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

        for (int i = 0; i < transformMap.size(); i++) {
        	Transform t = transformMap.get(keys[i]);
        	if(t != null)
        		sb.append("\n" + t.toString(Constants.TAB));
        }

        return sb.toString();
    }
}
