package de.dhbwstuttgart.bytecode.utilities;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import de.dhbwstuttgart.bytecode.constraint.TPHConstraint;
import de.dhbwstuttgart.bytecode.genericsGeneratorTypes.NameReplacementResult;
import de.dhbwstuttgart.syntaxtree.factory.NameGenerator;

public class NameReplacer {
	//TODO rename
	private List<TPHConstraint> constraints;
	private List<TPHConstraint> allConstraints;
	private List<MethodAndTPH> methodAndTPHs;
	// TODO rename into tphClass
	private List<String> tphs;
	private List<String> localTphs;
	
	public NameReplacer(List<TPHConstraint> constraints, List<TPHConstraint> allConstraints,List<String> tphs, ArrayList<String> localTphs) {
		super();
		this.constraints = constraints;
		this.allConstraints = allConstraints;
		this.tphs = tphs;
		this.localTphs = localTphs;
	}
	
	public NameReplacer(List<TPHConstraint> constraints, List<TPHConstraint> allConstraints,List<String> tphs) {
		super();
		this.constraints = constraints;
		this.allConstraints = allConstraints;
		this.tphs = tphs;
	}
	
	public NameReplacer(List<TPHConstraint> constraints, List<TPHConstraint> allConstraints) {
		this.constraints = constraints;
		this.allConstraints = allConstraints;
	}

	public NameReplacer(List<TPHConstraint> constraints, List<TPHConstraint> allConstraints, List<MethodAndTPH> methodAndTPHs,
			List<String> tphsClass) {
		this.constraints = constraints;
		this.allConstraints = allConstraints;
		this.methodAndTPHs = methodAndTPHs;
		this.tphs = tphsClass;
	}

	public NameReplacementResult replaceNames() {
		String newName = NameGenerator.makeNewName();
		List<String> names = new ArrayList<>();
		substituteRightSidesWithNewName(newName, names);
		
		substituteNamesInAllConstraints(newName, names);
		Stream<ArrayList<String>> tphsOfMethods = methodAndTPHs.stream().map(m->m.getTphs());
		Stream<ArrayList<String>> localTphsOfMethods = methodAndTPHs.stream().map(m->m.getLocalTphs());
		
		replaceOldNames(newName, names, tphsOfMethods);
		replaceOldNames(newName, names, localTphsOfMethods);
		
		if(tphs.removeAll(names))
			tphs.add(newName);
		
		NameReplacementResult res = new NameReplacementResult(newName, names);
		
		return res;
	}

	/**
	 * @param newName
	 * @param names
	 * @param tphsOfMethods
	 */
	public void replaceOldNames(final String newName, final List<String> names, Stream<ArrayList<String>> tphsOfMethods) {
		tphsOfMethods.forEach(tphsMethod->{
			if(tphsMethod.removeAll(names))
				tphsMethod.add(newName);
		});
	}
	
	public NameReplacementResult replaceNamesLocal() {
		String newName = NameGenerator.makeNewName();
		List<String> names = new ArrayList<>();
		substituteRightSidesWithNewName(newName, names);
		
		substituteNamesInAllConstraints(newName, names);
		
		tphs.removeAll(names);
		tphs.add(newName);
		
		NameReplacementResult res = new NameReplacementResult(newName, names);
		
		return res;
	}

	/**
	 * @param newName
	 * @param names
	 */
	public void substituteNamesInAllConstraints(String newName, List<String> names) {
		for(TPHConstraint cons : allConstraints) {
			if(names.contains(cons.getLeft()))
				cons.setLeft(newName);
			if(names.contains(cons.getRight()))
				cons.setRight(newName);
		}
	}

	/**
	 * @param newName
	 * @param names
	 */
	public void substituteRightSidesWithNewName(String newName, List<String> names) {
		for(TPHConstraint cons : constraints) {
			names.add(cons.getRight());
			cons.setRight(newName);
		}
	}
	
	public Map<String, List<String>> replaceNamesWithLocals() {
		String newName = NameGenerator.makeNewName();
		ArrayList<String> names = new ArrayList<>();
		for(TPHConstraint cons : constraints) {
			names.add(cons.getRight());
			cons.setRight(newName);
		}
		
		for(TPHConstraint cons : allConstraints) {
			if(names.contains(cons.getLeft()))
				cons.setLeft(newName);
			if(names.contains(cons.getRight()))
				cons.setRight(newName);
		}
		
		tphs.removeAll(names);
		tphs.add(newName);
		localTphs.removeAll(names);
		localTphs.add(newName);
		
		HashMap<String, List<String>> res = new HashMap<>();
		res.put(newName, names);
		return res;
	}
}
