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

import java.util.ArrayList;

import com.jaamsim.input.BooleanInput;
import com.jaamsim.input.Input;
import com.jaamsim.input.InputAgent;
import com.jaamsim.input.InputErrorException;
import com.jaamsim.input.Keyword;
import com.jaamsim.input.KeywordIndex;

/**
 * Group class - for storing a list of objects
 *
 * For input of the form <object> <keyword> <value>:
 * If the group appears as the object in a line of input, then the keyword and value applies to each member of the group.
 * If the group appears as the value in a line of input, then the list of objects is used as the value.
 */
public class Group extends Entity {
	@Keyword(description = "If TRUE show the members of the group as a seperate table in the output " +
	                "reports, including an entry for \"Total\"",
	         example = "Group1 Reportable { TRUE }")
	private final BooleanInput reportable;

	private Class<?> type;
	private final ArrayList<KeywordIndex> groupKeywordValues;

	private final ArrayList<Entity> list; // list of objects in group

	{
		this.addInput(new Group.GroupListInput());
		this.addInput(new Group.GroupAppendListInput());
		this.addInput(new Group.GroupTypeInput());

		reportable = new BooleanInput("Reportable", "Key Inputs", true);
		this.addInput(reportable);
	}

	public Group() {
		list = new ArrayList<Entity>();
		type = null;
		groupKeywordValues = new ArrayList<KeywordIndex>();
	}

	private class GroupListInput extends Input<String> {
		public GroupListInput() {
			super("List", "Key Inputs", null);
		}

		@Override
		public void parse(KeywordIndex kw) {
			ArrayList<Entity> temp = Input.parseEntityList(kw, Entity.class, true);
			list.clear();
			list.addAll(temp);
			Group.this.checkType();
		}
	}

	private class GroupAppendListInput extends Input<String> {
		public GroupAppendListInput() {
			super("AppendList", "Key Inputs", null);
		}

		@Override
		public void parse(KeywordIndex kw) {
			int originalListSize = list.size();
			ArrayList<Entity> temp = Input.parseEntityList(kw, Entity.class, true);
			for (Entity each : temp) {
				if (!list.contains(each))
					list.add(each);
			}
			Group.this.checkType();
			// set values of appended objects to the group values
			if ( type != null ) {
				for ( int i = originalListSize; i < list.size(); i ++ ) {
					Entity ent = list.get( i );
					for ( int j = 0; j < groupKeywordValues.size(); j++  ) {
						KeywordIndex grpkw = groupKeywordValues.get(j);
						InputAgent.apply(ent, grpkw);
					}
				}
			}
		}
	}

	private class GroupTypeInput extends Input<String> {
		public GroupTypeInput() {
			super("GroupType", "Key Inputs", null);
		}

		@Override
		public void parse(KeywordIndex kw) {
			Input.assertCount(kw, 1);
			type = Input.parseEntityType(kw.getArg(0));
			Group.this.checkType();
		}
	}


	public void saveGroupKeyword(KeywordIndex kw) {
		ArrayList<String> toks = new ArrayList<String>(kw.numArgs() + 4);
		for (int i = 0; i < kw.numArgs(); i++)
			toks.add(kw.getArg(i));

		KeywordIndex saved = new KeywordIndex(toks, kw.keyword, 0, toks.size(), kw.context);
		groupKeywordValues.add(saved);

		// If there can never be elements in the group, throw a warning
		if( type == null && list.size() == 0 ) {
			InputAgent.logWarning("The group %s has no elements to apply keyword: %s", this, kw.keyword);
		}
	}

	private void checkType() {
		if (type == null)
			return;

		for (Entity each : this.getList()) {
			if (!type.isInstance(each))
				throw new InputErrorException("The Entity: %s is not of Type: %s", each, type.getSimpleName());
		}
	}

	// ******************************************************************************************
	// ACCESSING
	// ******************************************************************************************

	public ArrayList<Entity> getList() {
		return list;
	}

	public boolean isReportable() {
		return reportable.getValue();
	}
}

@@ -664,47 +664,8 @@
 t;%0A%0A
-import com.jaamsim.input.BooleanInput;%0A
 impo
@@ -692,16 +692,16 @@
 .Input;%0A
+
 import c
@@ -779,42 +779,8 @@
 on;%0A
-import com.jaamsim.input.Keyword;%0A
 impo
@@ -1192,262 +1192,8 @@
 y %7B%0A
-%09@Keyword(description = %22If TRUE show the members of the group as a seperate table in the output %22 +%0A%09                %22reports, including an entry for %5C%22Total%5C%22%22,%0A%09         example = %22Group1 Reportable %7B TRUE %7D%22)%0A%09private final BooleanInput reportable;%0A%0A
 %09pri
@@ -1431,32 +1431,32 @@
 ndListInput());%0A
+
 %09%09this.addInput(
@@ -1487,105 +1487,8 @@
 ());
-%0A%0A%09%09reportable = new BooleanInput(%22Reportable%22, %22Key Inputs%22, true);%0A%09%09this.addInput(reportable);
 %0A%09%7D%0A
@@ -4030,76 +4030,7 @@
 ;%0A%09%7D
-%0A%0A%09public boolean isReportable() %7B%0A%09%09return reportable.getValue();%0A%09%7D
 %0A%7D%0A

remove a leftover keyword 'Reportable' from Group