/*
 * JaamSim Discrete Event Simulation
 * Copyright (C) 2013 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.input;

import com.sandwell.JavaSimulation.Entity;
import com.sandwell.JavaSimulation.InputErrorException;
import com.sandwell.JavaSimulation.StringVector;

public class OutputInput<T> extends Input<String> {

	private Class<T> klass;
	private Entity ent;  // The Entity against which to apply the first Output name
	private String outputName;  // The first Output name in the chain
	private OutputHandle out;  // The OutputHandle for the first Output in the chain
	private StringVector outputNameList;  // The names of the second, third, etc. Outputs in the chain.

	public OutputInput(Class<T> klass, String key, String cat, String def) {
		super(key, cat, def);
		this.klass = klass;
	}

	@Override
	public void parse(StringVector input) throws InputErrorException {

		if( input.isEmpty() ) {
			value = null;
			ent = null;
			outputName = "";
			out = null;
			outputNameList.clear();
			return;
		}

		Input.assertCountRange(input, 2, Integer.MAX_VALUE);
		Entity tmp = Input.parseEntity(input.get(0), Entity.class);
		String outName = input.get(1);
		if (!tmp.hasOutput(outName)) {
			throw new InputErrorException("Output named %s not found for Entity %s", outName, tmp.getName());
		}

		ent = tmp;
		outputName = outName;
		out = ent.getOutputHandle(outputName);

		outputNameList = new StringVector();
		if( input.size() > 2 )
			outputNameList = input.subString(2, input.size()-1);

		Class<?> retClass = out.getReturnType();
		if( input.size() == 2 ) {
			if ( klass != Object.class && !klass.isAssignableFrom(retClass) )
				throw new InputErrorException("OutputInput class mismatch. Expected: %s, got: %s", klass.toString(), retClass.toString());
		}
		else {
			if (!(Entity.class).isAssignableFrom(retClass))
				throw new InputErrorException("OutputInput class mismatch. The first output in the output chain must return an Entity");
		}

		value = String.format("%s.%s", ent.getInputName(), outputName);
		if( input.size() > 2 ) {
			for( String name: outputNameList ) {
				value += "." + name;
			}
		}
	}

	public OutputHandle getOutputHandle(double simTime) {
		OutputHandle o = out;
		for( String name : outputNameList ) {
			Entity e = o.getValue(simTime, Entity.class);
			if( e == null || !e.hasOutput(name) )
				return null;
			o = e.getOutputHandle(name);
		}
		return o;
	}

	public T getOutputValue(double simTime) {
		OutputHandle o = this.getOutputHandle(simTime);
		if( o == null )
			return null;
		return o.getValue(simTime, klass);
	}

	/**
	 * Returns the value of any numerical output converted to a double.
	 * @param simTime = present simulation time.
	 * @param def = the default value to return in case of null or a non-number.
	 * @return
	 */
	public double getOutputValueAsDouble(double simTime, double def) {
		OutputHandle o = this.getOutputHandle(simTime);
		if( o == null )
			return def;
		return o.getValueAsDouble(simTime, def);
	}

	@Override
	public String getValueString() {
		if (ent == null)
			return "";

		StringBuilder tmp = new StringBuilder();
		tmp.append(ent.getInputName());
		tmp.append(SEPARATOR);
		tmp.append(outputName);
		for( String name : outputNameList ) {
			tmp.append(SEPARATOR);
			tmp.append(name);
		}
		return tmp.toString();
	}

}

@@ -623,16 +623,45 @@
 input;%0A%0A
+import java.util.ArrayList;%0A%0A
 import c
@@ -754,57 +754,8 @@
 ion;
-%0Aimport com.sandwell.JavaSimulation.StringVector;
 %0A%0Apu
@@ -1069,28 +1069,33 @@
 private 
-StringVector
+ArrayList%3CString%3E
  outputN
@@ -1325,26 +1325,23 @@
 rse(
-StringVector input
+KeywordIndex kw
 ) th
@@ -1376,26 +1376,27 @@
 %09%09if
-( input.isEmpty() 
+ (kw.numArgs() == 0
 ) %7B%0A
@@ -1530,21 +1530,18 @@
 ntRange(
-input
+kw
 , 2, Int
@@ -1590,25 +1590,25 @@
 eEntity(
-input
+kw
 .get
+Arg
 (0), Ent
@@ -1642,17 +1642,17 @@
 e = 
-input
+kw
 .get
+Arg
 (1);
@@ -1897,102 +1897,186 @@
 new 
-StringVector();%0A%09%09if( input.size() %3E 2 )%0A%09%09%09outputNameList = input.subString(2, input.size()-1
+ArrayList%3CString%3E(kw.numArgs() - 2);%0A%09%09// grab any input strings after the first two, if there are any%0A%09%09for (int i = 2; i %3C kw.numArgs(); i++)%0A%09%09%09outputNameList.add(kw.getArg(i)
 );%0A%0A
@@ -2124,26 +2124,26 @@
 ;%0A%09%09if( 
-input.size
+kw.numArgs
 () == 2 
@@ -2612,18 +2612,18 @@
 if( 
-input.size
+kw.numArgs
 () %3E

convert OutputInput to use ArrayList<String> and KeywordIndex