/*
 * JaamSim Discrete Event Simulation
 * Copyright (C) 2002-2011 Ausenco Engineering Canada Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jaamsim.basicsim;

import java.util.ArrayList;

import com.jaamsim.events.EventManager;
import com.jaamsim.events.ProcessTarget;
import com.jaamsim.input.FileInput;
import com.jaamsim.input.InputAgent;
import com.jaamsim.input.Keyword;
import com.jaamsim.input.ValueInput;
import com.jaamsim.units.TimeUnit;

public class ScriptEntity extends Entity {
	@Keyword(description = "The name of the script file for the script entity.",
	         example = "ScriptEntity Script { test.scr }")
	private final FileInput scriptFileName;

	@Keyword(description = "The Time keyword appears inside the script file. The value represents the simulation " +
	                "time at which the next set of commands in the script are implemented.",
	         example = "ScriptEntity Time { 24.0 h }")
	private final ValueInput scriptTime; // the time that has been read in the script

	private ArrayList<ArrayList<String>> tokens;
	private int lastTokenIdx;

	{
		scriptFileName = new FileInput( "Script", "Key Inputs", null );
		this.addInput( scriptFileName );

		scriptTime = new ValueInput("Time", "Key Inputs", 0.0d);
		scriptTime.setUnitType(TimeUnit.class);
		scriptTime.setValidRange(0.0d, Double.POSITIVE_INFINITY);
		this.addInput(scriptTime);
	}

	public ScriptEntity() {
		tokens = new ArrayList<>();
	}

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

		tokens.clear();
		lastTokenIdx = -1;

		// If there is no script file, do nothing
		if (scriptFileName.getValue() == null)
			return;

		// If the script file exists, open it
		tokens = FileInput.getTokensFromURI(scriptFileName.getValue());
		boolean record = InputAgent.recordEdits();
		InputAgent.setRecordEdits(false);
		// Read records until a Time record is read
		// Restarts will work for simple scripts with a record at Time 0
		// Restarts should work for all scripts provided the script has initial inputs before the first Time record
		for (lastTokenIdx++; lastTokenIdx < tokens.size(); lastTokenIdx++) {
			InputAgent.processKeywordRecord(tokens.get(lastTokenIdx), null);
			if( tokens.get(lastTokenIdx).get( 0 ).equals( this.getName() ) ) {
				if( tokens.get( lastTokenIdx ).get( 1 ).equals( "Time" ) ) {
					lastTokenIdx--;
					InputAgent.setRecordEdits(record);
					return;
				}
			}
		}
	}

	private static class ScriptTarget extends ProcessTarget {
		final ScriptEntity script;

		ScriptTarget(ScriptEntity script) {
			this.script = script;
		}

		@Override
		public String getDescription() {
			return script.getName() + ".doScript";
		}

		@Override
		public void process() {
			script.doScript();
		}
	}
	private final ProcessTarget targ = new ScriptTarget(this);

	@Override
	public void startUp() {
		doScript();
	}

	/**
	 * Read the script
	 */
	public void doScript() {
		boolean record = InputAgent.recordEdits();
		InputAgent.setRecordEdits(false);
		for (lastTokenIdx++; lastTokenIdx < tokens.size(); lastTokenIdx++) {
			InputAgent.processKeywordRecord(tokens.get(lastTokenIdx), null);
			// If a "Time" record was read, then wait until the time
			long delayTicks = EventManager.current().secondsToNearestTick(scriptTime.getValue()) - getSimTicks();
			if (delayTicks > 0) {
				scheduleProcessTicks(delayTicks, 0, targ);
				break;
			}
		}
		InputAgent.setRecordEdits(record);
	}
}

@@ -1677,16 +1677,52 @@
 null );%0A
+%09%09scriptFileName.setRequired(true);%0A
 %09%09this.a
@@ -2111,139 +2111,28 @@
 %09// 
-If there is no script file, do nothing%0A%09%09if (scriptFileName.getValue() == null)%0A%09%09%09return;%0A%0A%09%09// If the script file exists, open it
+Open the script file
 %0A%09%09t

Make ScriptEntity keyword Script required