package org.musetest.core.resultstorage;

import org.jetbrains.annotations.*;
import org.musetest.core.*;
import org.musetest.core.context.*;
import org.musetest.core.datacollection.*;
import org.musetest.core.events.*;
import org.musetest.core.plugins.*;
import org.musetest.core.test.*;
import org.slf4j.*;

import java.io.*;

/**
 * @author Christopher L Merrill (see LICENSE.txt for license details)
 */
public class SaveTestResultsToDisk extends GenericConfigurableTestPlugin implements Shuttable
	{
	SaveTestResultsToDisk(SaveTestResultsToDiskConfiguration configuration)
		{
		super(configuration);
		_configuration = configuration;
		}

	@Override
	public void initialize(MuseExecutionContext context)
		{
		_save_at_end = _configuration.isSaveResultsAtEnd(context);
		_save_immediate = _configuration.isSaveResultsImmmediately(context);
		_delete_data = _configuration.isDeleteDataAfterImmedateSave(context);

		_location_provider = Plugins.findType(LocalStorageLocationProvider.class, context);
		if (_location_provider == null)
			{
			final String message = "No plugin was found providing LocalStorageLocationProvider services. Don't know where to store results to disk. Results will NOT be saved.";
			context.raiseEvent(MessageEventType.create(message));
			LOG.error(message);
			return;
			}
		context.registerShuttable(this);
		if (_save_immediate)
			context.addEventListener(event ->
				{
				if (TestResultStoredEventType.TYPE_ID.equals(event.getTypeId()))
					{
					final String var_name = event.getAttributeAsString(TestResultStoredEventType.VARIABLE_NAME);
					Object data = _context.getVariable(var_name);
					if (data instanceof TestResultData)
						{
						if (storeResult((TestResultData)data) && _delete_data)
							_context.setVariable(var_name, null);
						}
					else
						LOG.error(String.format("Unable to save test result. Variable %s is not a TestResultData", var_name));
					}
				});
		_context = context;
		}

	/**
	 * Do the saving in here, instead of as a END_TEST event listener...because the Collectors may not have received all events, yet.
	 */
	@Override
	public void shutdown()
		{
		if (_save_at_end)
			for (DataCollector collector : DataCollectors.list(_context))
				{
				for (TestResultData data : collector.getData())
					storeResult(data);
				}
		}

	private boolean storeResult(TestResultData data)
		{
		final File data_file = getResultFile(getOutputFolder(), data);
		try (FileOutputStream outstream = new FileOutputStream(data_file))
			{
			data.write(outstream);
			return true;
			}
		catch (IOException e)
			{
			LOG.error(String.format("Unable to store results of test in %s due to: %s", data_file.getAbsolutePath(), e.getMessage()));
			return false;
			}
		}

	private File getOutputFolder()
		{
		if (_output_folder == null)
			{
			if (_context instanceof TestExecutionContext)
				_output_folder = _location_provider.getTestFolder((TestExecutionContext) _context);
			else
				_output_folder = _location_provider.getBaseFolder();
			}
		return _output_folder;
		}

	@NotNull
	private File getResultFile(File output_folder, TestResultData data)
		{
		File file = new File(output_folder, data.suggestFilename());
		int index = 2;
		while (file.exists())
			file = new File(output_folder, createIndexedFilename(data.suggestFilename(), index++));
		return file;
		}

	private String createIndexedFilename(String starting_name, int index)
		{
		int period = starting_name.lastIndexOf('.');
		return starting_name.substring(0, period) + index + starting_name.substring(period, starting_name.length());
		}

	private SaveTestResultsToDiskConfiguration _configuration;
	private MuseExecutionContext _context;
	private LocalStorageLocationProvider _location_provider;
	private File _output_folder = null;
	private boolean _save_immediate = true;
	private boolean _save_at_end = true;
	private boolean _delete_data = true;

	public final static String TYPE_ID = "save-testresult-to-disk";

	public final static String OUTPUT_FOLDER_VARIABLE_NAME = "__output_folder__";

	private final static Logger LOG = LoggerFactory.getLogger(SaveTestResultsToDisk.class);
	}
@@ -770,17 +770,24 @@
 ext);%0A%09%09
-_
+boolean 
 save_imm
@@ -1345,25 +1345,24 @@
 his);%0A%09%09if (
-_
 save_immedia
@@ -1724,16 +1724,32 @@
 ata)data
+, %22$%22 + var_name
 ) && _de
@@ -1883,19 +1883,18 @@
 sult
-. Variabl
+ becaus
 e 
+$
 %25s i
@@ -2312,24 +2312,62 @@
 eResult(data
+, collector.getClass().getSimpleName()
 );%0A%09%09%09%09%7D%0A%09%09%7D
@@ -2408,32 +2408,57 @@
 tResultData data
+, String from_description
 )%0A%09%09%7B%0A%09%09final Fi
@@ -2619,54 +2619,379 @@
 %0A%09%09%09
-return true;%0A%09%09%09%7D%0A%09%09catch (IOException e)%0A%09%09%09%7B
+_context.raiseEvent(MessageEventType.create(String.format(%22Stored %25s from %25s to %25s%22, data, from_description, data_file.getAbsolutePath())));%0A%09%09%09return true;%0A%09%09%09%7D%0A%09%09catch (IOException e)%0A%09%09%09%7B%0A%09%09%09_context.raiseEvent(MessageEventType.create(String.format(%22Unable to store %25s from %25s to %25s because: %25s%22, data, from_description, data_file.getAbsolutePath(), e.getMessage())));
 %0A%09%09%09
@@ -4183,49 +4183,8 @@
 ll;%0A
-%09private boolean _save_immediate = true;%0A
 %09pri

Raise a message event when storing a result to disk.