package burlap.behavior.singleagent.vfa.common;

import burlap.behavior.singleagent.vfa.StateToFeatureVectorGenerator;
import burlap.mdp.core.state.State;
import burlap.mdp.core.state.vardomain.StateDomain;
import burlap.mdp.core.state.vardomain.VariableDomain;

import java.util.HashMap;
import java.util.Map;

/**
 * This class is will construct a double array from states by iterating over numeric state variables and setting values
 * in the double array to the variables normalized value. To set a normalized value, you must specify the domain of
 * each variable key that is used in the produced vector. Set these with the {@link #variableDomain(Object, VariableDomain)}
 * method. If you do not set a range for a variable, then that variable will not have a corresponding entry in the
 * constructed double array. If a state implements {@link StateDomain}, then you can give an example state to the
 * {@link #useAllDomains(StateDomain)} method, and it will automatically define the ranges for all variables that have
 * corresponding {@link VariableDomain} entries defined in teh {@link StateDomain}.
 * @author James MacGlashan.
 */
public class NormalizedVariablesVectorGenerator implements StateToFeatureVectorGenerator{

	protected Map<Object, VariableDomain> ranges = new HashMap<Object, VariableDomain>();

	/**
	 * Sets the variable range for the given variable.
	 * @param key the variable key
	 * @param range the range of the variable.
	 * @return this object, so a builder design pattern may be used.
	 */
	public NormalizedVariablesVectorGenerator variableDomain(Object key, VariableDomain range){
		ranges.put(key, range);
		return this;
	}

	/**
	 * Goes through the state and sets the ranges for all variables that have a {@link VariableDomain} set. If
	 * the {@link StateDomain#domain(Object)} method returns null for a state variable key, then a range will not be set
	 * and the variable will be skipped (unless a range is then manually added with the {@link #variableDomain(Object, VariableDomain)}
	 * method).
	 * @param state the {@link StateDomain} to query for the the state ranges.
	 * @return this object, so a builder design pattern may be used
	 */
	public NormalizedVariablesVectorGenerator useAllDomains(StateDomain state){
		for(Object key : state.variableKeys()){
			VariableDomain range = state.domain(key);
			if(range != null){
				this.ranges.put(key, range);
			}
		}
		return this;
	}

	@Override
	public double[] generateFeatureVectorFrom(State s) {
		double [] vals = new double[ranges.size()];
		int i = 0;
		for(Map.Entry<Object, VariableDomain> e : ranges.entrySet()){

			double d = ((Number)s.get(e.getKey())).doubleValue();
			VariableDomain r = e.getValue();

			double norm = r.norm(d);
			vals[i] = d;

			i++;
		}

		return vals;
	}
}

@@ -282,16 +282,39 @@
 ashMap;%0A
+import java.util.List;%0A
 import j
@@ -1288,21 +1288,22 @@
 Domain%3E 
-range
+domain
 s = new 
@@ -1637,21 +1637,22 @@
 nge)%7B%0A%09%09
-range
+domain
 s.put(ke
@@ -2399,21 +2399,22 @@
 %09%09%09this.
-range
+domain
 s.put(ke
@@ -2547,21 +2547,22 @@
  double%5B
-range
+domain
 s.size()
@@ -2583,69 +2583,155 @@
 ;%0A%09%09
-for(Map.Entry%3CObject, VariableDomain%3E e : ranges.entrySet())%7B
+List%3CObject%3E keys = s.variableKeys();%0A%09%09for(Object key : keys)%7B%0A%09%09%09VariableDomain vd = this.domains.get(key);%0A%09%09%09if(vd == null)%7B%0A%09%09%09%09continue;%0A%09%09%09%7D
 %0A%0A%09%09
@@ -2761,18 +2761,11 @@
 get(
-e.getKey()
+key
 )).d
@@ -2782,45 +2782,8 @@
 ();%0A
-%09%09%09VariableDomain r = e.getValue();%0A%0A
 %09%09%09d
@@ -2795,17 +2795,18 @@
  norm = 
-r
+vd
 .norm(d)
@@ -2820,17 +2820,20 @@
 ls%5Bi%5D = 
-d
+norm
 ;%0A%0A%09%09%09i+

fix assignment bug