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

import com.jaamsim.basicsim.Entity;
import com.jaamsim.basicsim.ObjectType;
import com.jaamsim.input.ExpError;
import com.jaamsim.input.ExpEvaluator;
import com.jaamsim.input.ExpParser;
import com.jaamsim.input.ExpResult;
import com.jaamsim.input.ExpParser.Expression;
import com.jaamsim.units.Unit;

public class StringProvExpression implements StringProvider {

	private final Expression exp;
	private final Entity thisEnt;
	private final Class<? extends Unit> unitType;
	private final ExpEvaluator.EntityParseContext parseContext;

	public StringProvExpression(String expString, Entity ent, Class<? extends Unit> ut) throws ExpError {
		thisEnt = ent;
		unitType = ut;
		parseContext = ExpEvaluator.getParseContext(thisEnt, expString);
		exp = ExpParser.parseExpression(parseContext, expString);
	}

	@Override
	public String getNextString(double simTime, String fmt, double siFactor) {
		String ret = "";
		try {
			ExpResult result = ExpEvaluator.evaluateExpression(exp, simTime);
			switch (result.type) {
			case STRING:
				ret = String.format(fmt, result.stringVal);
				break;
			case ENTITY:
				ret = String.format(fmt, result.entVal.getName());
				break;
			case NUMBER:
				if (result.unitType != unitType) {
					thisEnt.error("Invalid unit returned by an expression: '%s'%n"
							+ "Received: %s, expected: %s",
							exp, ObjectType.getObjectTypeForClass(result.unitType),
							ObjectType.getObjectTypeForClass(unitType));
				}
				ret = String.format(fmt, result.value/siFactor);
				break;
			default:
				assert(false);
				ret = "???";
				break;
			}
		}
		catch(ExpError e) {
			thisEnt.error(e.toString());
		}
		return ret;
	}

	@Override
	public String toString() {
		return parseContext.getUpdatedSource();
	}

}

@@ -894,19 +894,141 @@
 t.ExpRes
-ult
+Type;%0Aimport com.jaamsim.input.ExpResult;%0Aimport com.jaamsim.input.ExpValResult;%0Aimport com.jaamsim.input.InputErrorException
 ;%0Aimport
@@ -1098,16 +1098,60 @@
 ts.Unit;
+%0Aimport com.jaamsim.units.UserSpecifiedUnit;
 %0A%0Apublic
@@ -1582,16 +1582,16 @@
 tring);%0A
-
 %09%09exp = 
@@ -1641,16 +1641,745 @@
 String);
+%0A%0A%09%09if (exp.validationResult.state == ExpValResult.State.VALID%0A%09%09%09%09&& exp.validationResult.type == ExpResType.NUMBER) %7B%0A%0A%09%09%09// Check that a unit type has been specified%0A%09%09%09if (unitType == UserSpecifiedUnit.class) %7B%0A%09%09%09%09throw new InputErrorException(%22Unit type has not been specified%22);%0A%09%09%09%7D%0A%0A%09%09%09// We know the returned unit type with certainty, so we can check it against what we expect%0A%09%09%09Class%3C? extends Unit%3E expUnitType = exp.validationResult.unitType;%0A%09%09%09if (expUnitType != unitType) %7B%0A%09%09%09%09throw new InputErrorException(%22Invalid unit returned by an expression: '%25s'%25n%22%0A%09%09%09%09%09%09+ %22Received: %25s, expected: %25s%22,%0A%09%09%09%09%09%09exp, ObjectType.getObjectTypeForClass(expUnitType),%0A%09%09%09%09%09%09ObjectType.getObjectTypeForClass(unitType));%0A%09%09%09%7D%0A%09%09%7D
 %0A%09%7D%0A%0A%09@O

check that the UnitType input is entered for a StringProvExpression that returns a number