package dk.itu.mario.level.grammar;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;

import dk.itu.mario.level.LevelComponent.TYPE;


public class LevelGrammar {
	private Variable start;
	private Map<Variable, ProductionRule> ruleMap = new HashMap<Variable, ProductionRule>();
	private Set<Variable> variables = new TreeSet<Variable>();

	public void addVariable(Variable var) {
		if (variables.contains(var)) {
			throw new IllegalArgumentException(
					"Grammar already contains variable: " + var);
		}
		variables.add(var);
	}

	public void addProductionRule(ProductionRule rule) {
		if (ruleMap.containsKey(rule.getLHS())) {
			throw new IllegalArgumentException(
					"Grammar already contains rule with LHS: " + rule.getLHS());
		}
		ruleMap.put(rule.getLHS(), rule);
	}

	public String generateRandom(long randomSeed) {
		System.out.println("Generating random level parameters using seed: "
				+ randomSeed);
		List<Variable> startRuleRHS = getRule(getStart()).getRHS();
		StringBuilder sb = new StringBuilder();
		for (Variable var : startRuleRHS) {
			sb.append(var.toString());
		}
		return sb.toString();
	}

	public String generateRandom() {
		System.out.println("Creating new random seed for LevelGrammar");
		return generateRandom(new Random().nextLong());
	}

	//TODO: refactor to recursively add children to the current node based on production rule
	public LevelParseTree generateRandomTree(long randomSeed, int width) {
		System.out.println("Generating random level parameters using seed: "
				+ randomSeed);
		List<Variable> startRuleRHS = getRule(getStart()).getRHS();
		
		ParseNode rootNode = new ParseNode(TYPE.FLAT,1.0);
		LevelParseTree parseTree = new LevelParseTree(rootNode, 0, width);
		
		for (Variable var : startRuleRHS) {
			if (var.isTerminal()) {
				rootNode.addChild(new ParseNode(TYPE.FLAT,0.5));
			}
		}
		
		return parseTree;
	}
	
	public ProductionRule getRule(Variable var) {
		return ruleMap.get(var);
	}

	public Variable getStart() {
		return start;
	}

	public void setStart(Variable start) {
		this.start = start;
	}
}
