package com.github.rnowling.bps.datagenerator.generators.purchase;

import java.util.Map;

import com.github.rnowling.bps.datagenerator.datamodels.Product;
import com.github.rnowling.bps.datagenerator.datamodels.inputs.ProductCategory;
import com.github.rnowling.bps.datagenerator.framework.markovmodels.MarkovModel;
import com.github.rnowling.bps.datagenerator.framework.markovmodels.MarkovModelBuilder;
import com.github.rnowling.bps.datagenerator.framework.samplers.Sampler;
import com.google.common.collect.Maps;

public class ProductCategoryMarkovModelSampler implements Sampler<MarkovModel<Product>>
{
	final ProductCategory productCategory;
	final Sampler<Double> fieldWeightSampler;
	final Sampler<Double> fieldSimilarityWeightSampler;
	final Sampler<Double> loopbackWeightSampler;
	
	Map<String, Double> fieldWeights;
	Map<String, Double> fieldSimilarityWeights;
	double loopbackWeight;
	
	public ProductCategoryMarkovModelSampler(ProductCategory productCategory, 
			Sampler<Double> fieldWeightSampler, Sampler<Double> fieldSimilarityWeightSampler,
			Sampler<Double> loopbackWeightSampler)
	{
		this.productCategory = productCategory;
		
		this.fieldSimilarityWeightSampler = fieldSimilarityWeightSampler;
		this.fieldWeightSampler = fieldWeightSampler;
		this.loopbackWeightSampler = loopbackWeightSampler;
	}
	
	protected void generateWeights() throws Exception
	{
		fieldWeights = Maps.newHashMap();
		fieldSimilarityWeights = Maps.newHashMap();
		
		for(String fieldName : productCategory.getFieldNames())
		{
			fieldWeights.put(fieldName, fieldWeightSampler.sample());
			fieldSimilarityWeights.put(fieldName,fieldSimilarityWeightSampler.sample());
		}
		
		loopbackWeight = loopbackWeightSampler.sample();
	}
	
	protected <T> Map<T, Double> normalize(Map<T, Double> weights)
	{
		Map<T, Double> normalized = Maps.newHashMap();
		
		double weightSum = 0.0;
		for(double weight : weights.values())
		{
			weightSum += weight;
		}
		
		for(Map.Entry<T, Double> entry : weights.entrySet())
		{
			normalized.put(entry.getKey(), entry.getValue() / weightSum);
		}
		
		return normalized;
	}
	
	protected double productPairWeight(Product product1, Product product2)
	{
		double weightSum = 0.0;
		for(String fieldName : productCategory.getFieldNames())
		{
			double fieldWeight = this.fieldWeights.get(fieldName);
			
			if(product1.getFieldValue(fieldName).equals(product2.getFieldValue(fieldName)))
			{
				fieldWeight *= this.fieldSimilarityWeights.get(fieldName);
			}
			else
			{
				fieldWeight *= (1.0 - this.fieldSimilarityWeights.get(fieldName));
			}
			
			weightSum += fieldWeight;
		}
		return weightSum;
	}
	
	public MarkovModel<Product> sample() throws Exception
	{
		generateWeights();
		fieldWeights = normalize(fieldWeights);
		fieldSimilarityWeights = normalize(fieldSimilarityWeights);
		
		MarkovModelBuilder<Product> builder = new MarkovModelBuilder<Product>();
		
		for(Product product1 : productCategory.getProducts())
		{
			builder.addStartState(product1, 1.0);
			
			double weightSum = 0.0;
			for(Product product2 : productCategory.getProducts())
			{
				if(!product1.equals(product2))
				{
					weightSum += productPairWeight(product1, product2);
				}
			}
			
			for(Product product2 : productCategory.getProducts())
			{
				double weight = 0.0;
				if(!product1.equals(product2))
				{
					weight = (1.0 - loopbackWeight) * productPairWeight(product1, product2) / weightSum;
				}
				else
				{	weight = loopbackWeight;
					
				}
				
				builder.addTransition(product1, product2, weight);
			}
		}
		
		return builder.build();
	}
}

@@ -1726,385 +1726,8 @@
 %7D%0A%09%0A
-%09protected %3CT%3E Map%3CT, Double%3E normalize(Map%3CT, Double%3E weights)%0A%09%7B%0A%09%09Map%3CT, Double%3E normalized = Maps.newHashMap();%0A%09%09%0A%09%09double weightSum = 0.0;%0A%09%09for(double weight : weights.values())%0A%09%09%7B%0A%09%09%09weightSum += weight;%0A%09%09%7D%0A%09%09%0A%09%09for(Map.Entry%3CT, Double%3E entry : weights.entrySet())%0A%09%09%7B%0A%09%09%09normalized.put(entry.getKey(), entry.getValue() / weightSum);%0A%09%09%7D%0A%09%09%0A%09%09return normalized;%0A%09%7D%0A%09%0A
 %09pro
@@ -2337,112 +2337,8 @@
 ();%0A
-%09%09fieldWeights = normalize(fieldWeights);%0A%09%09fieldSimilarityWeights = normalize(fieldSimilarityWeights);%0A
 %09%09%0A%09

Remove incorrect normalizations in MarkovModelSampler