package com.github.aureliano.defero;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;

import com.github.aureliano.defero.config.EventCollectorConfiguration;
import com.github.aureliano.defero.config.input.IConfigInput;
import com.github.aureliano.defero.config.input.StandardInputConfig;
import com.github.aureliano.defero.config.output.IConfigOutput;
import com.github.aureliano.defero.config.output.StandardOutputConfig;
import com.github.aureliano.defero.exception.DeferoException;
import com.github.aureliano.defero.helper.ConfigHelper;
import com.github.aureliano.defero.helper.LoggerHelper;
import com.github.aureliano.defero.profile.Profiler;
import com.github.aureliano.defero.reader.DataReaderFactory;
import com.github.aureliano.defero.reader.IDataReader;
import com.github.aureliano.defero.writer.DataWriterFactory;
import com.github.aureliano.defero.writer.IDataWriter;

public class AppEventsCollector {

	private static final Logger logger = Logger.getLogger(AppEventsCollector.class.getName());
	
	private EventCollectorConfiguration configuration;
	
	public AppEventsCollector() {
		super();
	}
	
	public void execute() {
		if (this.configuration == null) {
			this.configuration = new EventCollectorConfiguration();
			logger.info("Using default event collector configuration.");
		}
		
		Profiler profiler = new Profiler();
		profiler.start();
		
		this.prepareExecution();
		List<Map<String, Object>> executionLogs = this.executeCollectors();
		
		this.printLogToOutput(profiler, executionLogs);
	}
	
	private List<Map<String, Object>> executeCollectors() {
		if (this.configuration.getParser() == null) {
			throw new DeferoException("Parser must pe provided.");
		}
		
		List<Map<String, Object>> executionLogs = new ArrayList<Map<String,Object>>();
		
		for (short i = 0; i < this.configuration.getInputConfigs().size(); i++) {
			IConfigInput inputConfig = this.configuration.getInputConfigs().get(i);
			logger.info("Start execution for input " + inputConfig.getConfigurationId());
			ConfigHelper.inputConfigValidation(inputConfig);
			
			if (inputConfig.getConfigurationId() == null) {
				inputConfig.withConfigurationId("configuration_id_" + (i + 1));
			}
			
			for (IConfigOutput outputConfig : this.configuration.getOutputConfigs()) {
				ConfigHelper.outputConfigValidation(outputConfig);
				executionLogs.add(this.dataIteration(inputConfig, outputConfig));
			}
		}
		
		return executionLogs;
	}
	
	private Map<String, Object> dataIteration(IConfigInput inputConfig, IConfigOutput outputConfig) {
		IDataReader dataReader = DataReaderFactory
			.createDataReader(inputConfig)
				.withMatcher(this.configuration.getMatcher())
				.withParser(this.configuration.getParser())
				.withFilter(this.configuration.getFilter())
				.withListeners(this.configuration.getDataReadingListeners());
		IDataWriter dataWriter = DataWriterFactory
			.createDataWriter(outputConfig)
				.withOutputFormatter(this.configuration.getOutputFormatter())
				.withListeners(this.configuration.getDataWritingListeners());
		
		while (dataReader.keepReading()) {
			Object data = dataReader.nextData();
			if (data != null) {
				dataWriter.write(data);
			}
		}
		
		dataReader.endResources();
		dataWriter.endResources();
		
		return dataReader.executionLog();
	}
	
	private void printLogToOutput(Profiler profiler, List<Map<String, Object>> executionLogs) {
		Properties properties = Profiler.parse(Profiler.diff(profiler, profiler.stop()));
		
		for (Map<String, Object> executionLog : executionLogs) {
			for (String key : executionLog.keySet()) {
				properties.put(key, String.valueOf(executionLog.get(key)));
			}
		}
		
		File log = LoggerHelper.saveExecutionLog(properties, true);
		logger.info("Execution log output saved at " + log.getPath());		
		logger.info("Execution successful!");
	}
	
	private void prepareExecution() {
		if (this.configuration.getInputConfigs().isEmpty()) {
			this.configuration.addInputConfig(new StandardInputConfig());
		}
		
		if (this.configuration.getOutputConfigs().isEmpty()) {
			this.configuration.addOutputConfig(new StandardOutputConfig());
		}
	}
	
	public EventCollectorConfiguration getConfiguration() {
		return configuration;
	}
	
	public AppEventsCollector withConfiguration(EventCollectorConfiguration configuration) {
		this.configuration = configuration;
		return this;
	}
}
@@ -1166,16 +1166,54 @@
 ration;%0A
+%09private boolean persistExecutionLog;%0A
 %09%0A%09publi
@@ -1243,15 +1243,39 @@
 %7B%0A%09%09
-super()
+this.persistExecutionLog = true
 ;%0A%09%7D
@@ -1625,24 +1625,59 @@
 ctors();%0A%09%09%0A
+%09%09if (this.persistExecutionLog) %7B%0A%09
 %09%09this.print
@@ -1714,16 +1714,20 @@
 nLogs);%0A
+%09%09%7D%0A
 %09%7D%0A%09%0A%09pr
@@ -4547,9 +4547,238 @@
 his;%0A%09%7D%0A
+%09%0A%09public boolean isPersistExecutionLog() %7B%0A%09%09return persistExecutionLog;%0A%09%7D%0A%09%0A%09public AppEventsCollector withPersistExecutionLog(boolean persistExecutionLog) %7B%0A%09%09this.persistExecutionLog = persistExecutionLog;%0A%09%09return this;%0A%09%7D%0A
 %7D

Make log execution persistence an option.