package dk.itu.mario.level.grammar;

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

import dk.itu.mario.level.LevelComponent;
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 LevelParseTree generateRandomTree(long randomSeed, int width) {
		System.out.println("Generating random level parameters using seed: "
				+ randomSeed);
		Variable startRuleLHS = getStart();

		ParseNode rootNode = new ParseNode(startRuleLHS.getType(), 1.0);
		generateRecursive(rootNode, getRule(startRuleLHS).getRHS(), 1.0);
		LevelParseTree parseTree = new LevelParseTree(rootNode, 0, width);

		return parseTree;
	}

	private ParseNode generateRecursive(ParseNode parseNode, Clause clause,
			double relativeWidth) {
		if (clause.isVariable()) {
			if (clause.isTerminal()) {
				parseNode.addChild(new ParseNode(((Variable) clause).getType(),
						relativeWidth));
			} else {
				generateRecursive(parseNode, getRule((Variable) clause)
						.getRHS(), relativeWidth);
			}
		} else {
			if (clause.isChoice()) {
				generateRecursive(parseNode, clause.makeRandomChoice(),
						relativeWidth);
			} else {
				int numSubClauses = clause.getNumSubClauses();
				for (int i = 0; i < numSubClauses; i++) {
					generateRecursive(parseNode, clause.getSubClause(i),
							relativeWidth / numSubClauses);
				}
			}
		}
		return parseNode;
	}
	
	public Variable getVariable(String varName) {
		for (Variable var : variables) {
			if (varName.equals(var.getValue())) {
				return var;
			}
		}
		return null;
	}

	public ProductionRule getRule(Variable var) {
		return ruleMap.get(var);
	}

	public Variable getStart() {
		return start;
	}

	public void setStart(String startVar) {
		for (Variable var : variables) {
			if (startVar.equals(var.getValue())) {
				start = var;
				return;
			}
		}
	}
	
	public void setStart(Variable start) {
		this.start = start;
	}
	
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("Variables:\n");
		for (Variable var : variables) {
			sb.append("VAR ");
			sb.append(var.getValue());
			sb.append(" = ");
			sb.append(var.getType());
			sb.append("\n");
		}
		sb.append("\n");
		for (Entry<Variable,ProductionRule> entry : ruleMap.entrySet()) {
			//could as easily have used LHS instead of getKey().getValue()
			sb.append("RULE " + entry.getKey().getValue() + " -> " + entry.getValue().getRHS());
			sb.append("\n");
		}
		sb.append("\n");
		sb.append("START = " + start.getValue());
		return sb.toString();
	}
}
