/*
 * JaamSim Discrete Event Simulation
 * Copyright (C) 2015 Ausenco Engineering Canada Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
package com.jaamsim.BasicObjects;

import java.util.ArrayList;

import com.jaamsim.Graphics.DisplayEntity;
import com.jaamsim.StringProviders.StringProvListInput;
import com.jaamsim.StringProviders.StringProvider;
import com.jaamsim.basicsim.EntityTarget;
import com.jaamsim.basicsim.FileEntity;
import com.jaamsim.events.ProcessTarget;
import com.jaamsim.input.Input;
import com.jaamsim.input.InputAgent;
import com.jaamsim.input.Keyword;
import com.jaamsim.input.UnitTypeListInput;
import com.jaamsim.input.ValueInput;
import com.jaamsim.units.TimeUnit;
import com.jaamsim.units.Unit;
import com.jaamsim.units.UserSpecifiedUnit;

public class ExpressionLogger extends DisplayEntity {
	private FileEntity file;
	private ArrayList<String> columnFormats;

	@Keyword(description = "The unit types for the quantities being logged. "
			+ "Use DimensionlessUnit for text entries.",
	         exampleList = {"DistanceUnit  SpeedUnit"})
	private final UnitTypeListInput unitTypeListInput;

	@Keyword(description = "One or more sources of data to be logged.\n"
			+ "Each source is specified by an Expression. Also acceptable are: "
			+ "a constant value, a Probability Distribution, TimeSeries, or a "
			+ "Calculation Object.",
	         exampleList = {"{ [Entity1].Output1 } { [Entity2].Output2 }"})
	protected final StringProvListInput dataSource;

	@Keyword(description = "The interval between entries in the log file.",
	         exampleList = { "24.0 h" })
	private final ValueInput interval;

	{
		unitTypeListInput = new UnitTypeListInput("UnitTypeList", "Key Inputs", null);
		unitTypeListInput.setRequired(true);
		this.addInput(unitTypeListInput);

		dataSource = new StringProvListInput("DataSource", "Key Inputs", null);
		dataSource.setUnitType(UserSpecifiedUnit.class);
		dataSource.setEntity(this);
		dataSource.setRequired(true);
		this.addInput(dataSource);

		interval = new ValueInput("Interval", "Key Inputs", null);
		interval.setUnitType(TimeUnit.class);
		interval.setValidRange(1.0e-10, Double.POSITIVE_INFINITY);
		interval.setRequired(true);
		this.addInput(interval);
	}

	public ExpressionLogger() {
		columnFormats = new ArrayList<>();
	}

	@Override
	public void updateForInput(Input<?> in) {
		super.updateForInput(in);

		if (in == unitTypeListInput) {
			dataSource.setUnitTypeList(unitTypeListInput.getUnitTypeList());
			return;
		}
	}

	@Override
	public void earlyInit() {
		super.earlyInit();

		columnFormats.clear();

		// Close the file if it is already open
		if (file != null) {
			file.close();
			file = null;
		}

		// Create the report file
		StringBuilder tmp = new StringBuilder(InputAgent.getReportFileName(InputAgent.getRunName()));
		tmp.append("-").append(this.getName());
		tmp.append(".log");
		file = new FileEntity(tmp.toString());

		// Write the header line
		file.format("%n%20s", "SimTime");
		ArrayList<String> toks = new ArrayList<>();
		dataSource.getValueTokens(toks);
		for (String str : toks) {
			if (str.equals("{") || str.equals("}"))
				continue;
			int length = Math.max(20, str.length());
			StringBuilder fmt = new StringBuilder();
			fmt.append("\t%").append(length).append("s");
			columnFormats.add(fmt.toString());
			file.format(fmt.toString(), str);
		}

		// Write the units line
		String unit = Unit.getDisplayedUnit(TimeUnit.class);
		file.format("%n%20s", unit);
		for (int i=0; i<dataSource.getListSize(); i++) {
			unit = Unit.getDisplayedUnit(dataSource.getUnitType(i));
			file.format(columnFormats.get(i), unit);
		}

		// Empty the output buffer
		file.flush();

	}

	@Override
	public void startUp() {
		super.startUp();

		this.startAction();
	}

	private void startAction() {

		// Schedule the next time an entry in the log file will be written
		this.scheduleProcess(interval.getValue(), 5, endActionTarget);
	}

	private void endAction() {

		double simTime = getSimTime();
		double factor = Unit.getDisplayedUnitFactor(TimeUnit.class);
		file.format("%n%20s", simTime/factor);

		// Write the entry in the log file
		try {
			for (int i=0; i<dataSource.getListSize(); i++) {
				StringProvider samp = dataSource.getValue().get(i);
				factor = Unit.getDisplayedUnitFactor(dataSource.getUnitType(i));
				file.format(columnFormats.get(i), samp.getNextString(simTime, "%s", factor));
			}
		}
		catch (Exception e) {
			error(e.getMessage());
		}

		// Empty the output buffer
		file.flush();

		// Get ready for the next entry
		this.startAction();
	}

	protected final ProcessTarget endActionTarget = new EndActionTarget(this);

	private static class EndActionTarget extends EntityTarget<ExpressionLogger> {
		EndActionTarget(ExpressionLogger ent) {
			super(ent, "endAction");
		}

		@Override
		public void process() {
			ent.endAction();
		}
	}

}

@@ -1310,50 +1310,8 @@
 ile;
-%0A%09private ArrayList%3CString%3E columnFormats;
 %0A%0A%09@
@@ -2681,47 +2681,8 @@
 () %7B
-%0A%09%09columnFormats = new ArrayList%3C%3E();%0A%09
 %7D%0A%0A%09
@@ -2947,34 +2947,8 @@
 );%0A%0A
-%09%09columnFormats.clear();%0A%0A
 %09%09//
@@ -3316,26 +3316,24 @@
 .format(%22%25n%25
-20
 s%22, %22SimTime
@@ -3509,209 +3509,26 @@
 %0A%09%09%09
-int length = Math.max(20, str.length());%0A%09%09%09StringBuilder fmt = new StringBuilder();%0A%09%09%09fmt.append(%22%5Ct%25%22).append(length).append(%22s%22);%0A%09%09%09columnFormats.add(fmt.toString());%0A%09%09%09file.format(fmt.toString()
+file.format(%22%5Ct%25s%22
 , st
@@ -3631,26 +3631,24 @@
 .format(%22%25n%25
-20
 s%22, unit);%0A%09
@@ -3772,36 +3772,22 @@
 .format(
-columnFormats.get(i)
+%22%5Ct%25s%22
 , unit);
@@ -4236,18 +4236,16 @@
 mat(%22%25n%25
-20
 s%22, simT
@@ -4500,28 +4500,14 @@
 mat(
-columnFormats.get(i)
+%22%5Ct%25s%22
 , sa

simplify the ExpressionLogger output by removing leading blanks