package dfatool.strategy.elements;

import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import patterns.Observer;
import patterns.Subject;
import dfatool.expressions.Expression;
import dfatool.parser.ExpressionParser;
import dfatool.parser.LanguageUtil;
import dfatool.parser.ParseException;
import dfatool.values.ListValue;
import dfatool.values.SetValue;
import dfatool.values.Value;

public class Context extends Subject implements Observer {
	
	Map<String, List<Value>> channelMap;
	Map<String,Set<Value>> typeMap;
	Map<String,Value> varMap;
	private static Context instance;
	
	private Context(Description description) {
		channelMap = new HashMap<String, List<Value>>();
		typeMap = new HashMap<String, Set<Value>>();
		varMap = new HashMap<String, Value>();
		for(Datatype dt : description.getDatatypes()){
			Expression exp = LanguageUtil.parseString(dt.getType());
			putType(dt.getName(), (SetValue) exp.evaluate(this));
		}
		for(Var v : description.getVars()){
		  	Expression exp = LanguageUtil.parseString(v.getValue());
			putVar(v.getName(), exp.evaluate(this));
		}
		for(Channel ch : description.getChannels()){
		  	Expression exp = LanguageUtil.parseString(ch.getType());
		  	Value v = exp.evaluate(this);
		  	ListValue l = null;
		  	if(v instanceof SetValue){
		  		l = new ListValue();
		  		l.add(v);
		  	}else if(v instanceof ListValue){
		  		l = (ListValue) v;
		  	}
			putChannel(ch.getName(), l);
		}
	}
	
	public static Context getInstance() {
		if(instance == null)
			instance = new Context();
		return instance;
	}
	
	private Context(Map<String, List<Value>> channelMap, Map<String, Set<Value>> typeMap,
			Map<String, Value> varMap) {
		Description.createDescription().register(this);
		this.channelMap = new HashMap<String,List<Value>>(channelMap);
		this.typeMap = new HashMap<String,Set<Value>>(typeMap);
		this.varMap = new HashMap<String,Value>(varMap);
	}
	
	public Context(Context c) {
		this.channelMap = new HashMap<String,List<Value>>(c.channelMap);
		this.typeMap = new HashMap<String,Set<Value>>(c.typeMap);
		this.varMap = new HashMap<String,Value>(c.varMap);
	}

	public void putChannel(String name, List<Value> s){
		channelMap.put(name, s);
	}
	
	public void putType(String name, Set<Value> s){
		typeMap.put(name, s);
	}
	
	public void putVar(String name, Value v){
		varMap.put(name, v);
	}
	
	
	
	public Map<String, List<Value>> getChannelMap() {
		return channelMap;
	}

	public Map<String, Set<Value>> getTypeMap() {
		return typeMap;
	}

	public Map<String, Value> getVarMap() {
		return varMap;
	}

	private void freshContext(){
		channelMap = new HashMap<String, List<Value>>();
		typeMap = new HashMap<String, Set<Value>>();
		varMap = new HashMap<String, Value>();
	}
	
	private void generateContext(){
		Description desc = Description.createDescription();
		freshContext();
		for(Datatype dt : desc.getDatatypes()){
			Expression exp = LanguageUtil.parseString(dt.getType());
			putType(dt.getName(), (SetValue) exp.evaluate(this));
		}
		for(Var v : desc.getVars()){
		  	Expression exp = LanguageUtil.parseString(v.getValue());
			putVar(v.getName(), exp.evaluate(this));
		}
		for(Channel ch : desc.getChannels()){
		  	Expression exp = LanguageUtil.parseString(ch.getType());
		  	Value v = exp.evaluate(this);
		  	ListValue l = null;
		  	if(v instanceof SetValue){
		  		l = new ListValue();
		  		l.add(v);
		  	}else if(v instanceof ListValue){
		  		l = (ListValue) v;
		  	}
			putChannel(ch.getName(), l);
		}
	}
	
	
	public Context test(){
		Context c = new Context();
		return c;
	}
	
	@Override
	public void update() {
		generateContext();
		alertAll();
	}
	
	public void dispose(){
		instance = null;
	}
	
//	public static void main(java.lang.String[] args) {
//		
//		Map<String,Set> s = new HashMap<String,Set>();
//		s.put("abo", new Set());
//		Context c = new Context(s, new HashMap<String,Set>(), new HashMap<String,Value>());
//		System.out.println(c.channelMap.toString());
//		s.put("abp", new Set());
//		System.out.println(c.channelMap.toString());
//	}

}
