package be.jeffcheasey88.peeratcode.parser.java;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CleanerPool{
	
	private List<Cleaner> cleaners;
	
	public CleanerPool(Cleaner... cleaners){
		this.cleaners = Arrays.asList(cleaners);
	}

	public String clean(String value){
		for(Cleaner cleaner : this.cleaners) value = cleaner.clean(value);
		return value;
	}
	

	public String unzip(String value, BiFunction<String,String, String> modifier){
		boolean edited = false;
		for(Cleaner cleaner : this.cleaners){
			Matcher matcher = cleaner.getMatcher(value);
			if(matcher.matches()){
				String key = matcher.group(2);
				String zip = cleaner.getConstant(key);
				String modified = modifier.apply(zip, cleaner.getPattern());
				if(modified == null) modified = zip;
				value = matcher.group(1)+cleaner.open+modified+cleaner.close+matcher.group(3);
				edited = true;
			}
		}
		if(edited) return unzip(value, modifier);
		return value;
	}
	
	public static class Cleaner{
		
		private Pattern rPattern;
		
		private String pattern;
		private char open;
		private char close;
		
		public List<String> constants;
		
		public Cleaner(String pattern, char open, char close){
			this.pattern = "$"+pattern;
			this.open = open;
			this.close = close;
			this.rPattern = Pattern.compile("^(.*)(\\$"+pattern+"\\d+)(.*)$");
			this.constants = new ArrayList<>();
		}
		
		public Matcher getMatcher(String value){
			return this.rPattern.matcher(value);
		}
		
		public String getPattern(){
			return this.pattern;
		}
		
		public String getConstant(String value){
			return this.constants.get(Integer.parseInt(value.replace(this.pattern, "")));
		}
		
		public String clean(String value){
			StringBuilder builder = new StringBuilder();
			for(int i = 0; i < value.length(); i++){
				char c = value.charAt(i);
				if(c == open){
					int cut = cutOpenable(value.substring(i+1), constants, pattern, open, close);
					if(cut < 0){
						builder.append(c);
						continue;
					}
					i+=cut;
					builder.append(pattern+(constants.size()-1));
				}else{
					builder.append(c);
				}
			}
			return builder.toString();
		}
		
		private int cutOpenable(String value, List<String> constants, String pattern, char open, char close){
			StringBuilder builder = new StringBuilder();
			int i = 0;
			for(;i < value.length(); i++){
				char c = value.charAt(i);
				if(c == close){
					constants.add(builder.toString());
					return i+1;
				}else if(c == open){
					int cut = cutOpenable(value.substring(i+1), constants, pattern, open, close);
					if(cut < 0){
						builder.append(c);
						continue;
					}
					i+=cut;
					builder.append(pattern+(constants.size()-1));
				}else{
					builder.append(c);
				}
			}
			
			return -1;
		}
		
	}
}