/*
 * License: FreeBSD (Berkeley Software Distribution)
 * Copyright (c) 2013, Sara Sheehan, Kelley Harris, Yun Song
 * 
 * based on:
 * Copyright (c) 2011, Joshua Paul, Matthias SteinrŸcken, Yun Song
 */

package edu.berkeley.utility;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import edu.berkeley.utility.Haplotype.GeneticTypeMultiplicity;

// NOTE: template parameters of this class are used in a HashMap, and so must either implement hashCode()
//  and equals() or otherwise ensure that hashing works properly (e.g. private constructor + factory)

// mutable configuration of FSAHaplotypes, which additionally caches information about the defined alleles at each locus
public class HapConfig<H extends Haplotype> implements Iterable<GeneticTypeMultiplicity<H>> {
	
	// the configuration
	private final Map<H, GeneticTypeMultiplicity<H>> typeConfiguration;
	
	// cached information about the configuration
	private int numGeneticTypes;
	private int distinctGeneticTypes;
	
	public HapConfig() {
		this.typeConfiguration = new LinkedHashMap<H, GeneticTypeMultiplicity<H>>();
		
		this.numGeneticTypes = 0;
		this.distinctGeneticTypes = 0;
	}

	public final int totalGeneticTypes() {
		return numGeneticTypes;
	}
	
	public final int distinctGeneticTypes() {
		return distinctGeneticTypes;
	}

	public final void adjustType(H type, int adjustment) {
		GeneticTypeMultiplicity<H> geneMultiplicity = typeConfiguration.get(type);
		
		if (geneMultiplicity == null) {
			if (adjustment != 0) {
				typeConfiguration.put(type, new GeneticTypeMultiplicity<H>(type, adjustment));
				distinctGeneticTypes++;
			}
		} else {
			int newMultiplicity = geneMultiplicity.multiplicity + adjustment;
			if (newMultiplicity != 0) {
				geneMultiplicity.multiplicity = newMultiplicity;
			} else {
				typeConfiguration.remove(type);
				distinctGeneticTypes--;
			}
		}
		
		numGeneticTypes += adjustment;
	}

	public final void addTypes(Iterable<GeneticTypeMultiplicity<H>> configuration) {
		for (GeneticTypeMultiplicity<H> typeMultiplicity : configuration) {
			adjustType(typeMultiplicity.geneticType, typeMultiplicity.multiplicity);
		}
	}

	public final int getMultiplicity(H type) {
		GeneticTypeMultiplicity<H> typeMultiplicity = typeConfiguration.get(type);
		return typeMultiplicity == null ? 0 : typeMultiplicity.multiplicity;
	}
	
	public final Iterator<GeneticTypeMultiplicity<H>> iterator() {
		return typeConfiguration.values().iterator();
	}
	
	public GeneticTypeMultiplicity<H>[] getHapIdxMap() {
		@SuppressWarnings("unchecked")
		GeneticTypeMultiplicity<H>[] hapIdxMap = new GeneticTypeMultiplicity[this.distinctGeneticTypes()];
		
		int hapIdx = 0;
		for (GeneticTypeMultiplicity<H> typeMult : this) {
			hapIdxMap[hapIdx++] = typeMult;
		}
		
		return hapIdxMap;
	}

	public String toString() {
		StringBuffer sb = new StringBuffer();
		
		for (GeneticTypeMultiplicity<H> typeMultiplicity : this) {
			sb.append(typeMultiplicity.geneticType + ":" + typeMultiplicity.multiplicity + "\n");
		}
		
		return sb.toString();
	}
}