package slacknotifications.teamcity;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static slacknotifications.teamcity.BuildStateEnum.*;

public class BuildState {

	Map<BuildStateEnum, BuildStateInterface> states = new HashMap<BuildStateEnum, BuildStateInterface>();
	
	public BuildState() {
		states.clear();
		states.put(BuildStateEnum.BUILD_STARTED, 			new SimpleBuildState(BuildStateEnum.BUILD_STARTED, 			false));
		//states.put(BuildStateEnum.BUILD_CHANGED_STATUS, 	new SimpleBuildState(BuildStateEnum.BUILD_CHANGED_STATUS, 	false)); 	
		states.put(BuildStateEnum.BEFORE_BUILD_FINISHED, 	new SimpleBuildState(BuildStateEnum.BEFORE_BUILD_FINISHED, 	false)); 
		states.put(BuildStateEnum.RESPONSIBILITY_CHANGED, 	new SimpleBuildState(BuildStateEnum.RESPONSIBILITY_CHANGED,	false));
		states.put(BuildStateEnum.BUILD_INTERRUPTED, 		new SimpleBuildState(BuildStateEnum.BUILD_INTERRUPTED, 		false));
		states.put(BuildStateEnum.BUILD_SUCCESSFUL, 		new SimpleBuildState(BuildStateEnum.BUILD_SUCCESSFUL, 		false));
		states.put(BuildStateEnum.BUILD_FAILED, 			new SimpleBuildState(BuildStateEnum.BUILD_FAILED, 			false));

		states.put(BuildStateEnum.BUILD_BROKEN, 			new SimpleBuildState(BuildStateEnum.BUILD_BROKEN, 			false));
		states.put(BuildStateEnum.BUILD_FIXED, 				new SimpleBuildState(BuildStateEnum.BUILD_FIXED, 			false));
		
		states.put(BuildStateEnum.BUILD_FINISHED, 			new SimpleBuildState(BuildStateEnum.BUILD_FINISHED, 		false)); 		
	}
	
	public Set<BuildStateEnum> getStateSet(){
		return states.keySet();
	}
	
    /**
     * Takes the currentBuildState, for which the SlackNotification is being triggered
     * and compares it against the build states for which this SlackNotification is configured
     * to notify.
     * 
     * @param currentBuildState
     * @param buildStatesToNotify
     * @return Whether or not the slacknotifications should trigger for the current build state.
     */
    public boolean enabled(BuildStateEnum currentBuildState) {
    	return states.get(currentBuildState).isEnabled();
	}
    
    public boolean enabled(BuildStateEnum currentBuildState, boolean success, boolean changed){
    	if (currentBuildState != BuildStateEnum.BUILD_FINISHED){
    		return enabled(currentBuildState);
    	} else {
    		if (enabled(BUILD_SUCCESSFUL) &&  enabled(BUILD_FIXED) && changed && success){
    			return true;
    		}
    		if (enabled(BUILD_SUCCESSFUL) && !enabled(BUILD_FIXED) && success){
    			return true;
    		}
    		if (enabled(BUILD_FAILED) && enabled(BUILD_BROKEN) && changed && !success){
    			return true;
    		}
    		if (enabled(BUILD_FAILED) && !enabled(BUILD_BROKEN) && !success){
    			return true;
    		}
    	}
    	return false;
    }
    
    public void setEnabled(BuildStateEnum currentBuildState, boolean enabled){
    	if (enabled)
    		enable(currentBuildState);
    	else
    		disable(currentBuildState);
    }
    
    /**
     * Enable all build events for notification
     * Note: BROKEN and FIXED restrict builds, so don't set those.
     */
    public BuildState setAllEnabled(){
    	for (BuildStateEnum state : states.keySet()){
    		switch (state){
    		case BUILD_BROKEN:
    			disable(state);
    			break;
    		case BUILD_FIXED:
    			disable(state);
    			break;
    		default:
    			enable(state);
    			break;
    		}
    	}
    	return this;
    }
    
    public void enable(BuildStateEnum currentBuildState){
    	states.get(currentBuildState).enable();
    }

    public void disable(BuildStateEnum currentBuildState){
    	states.get(currentBuildState).disable();
    }

    /**
     * Convert build state Integer into short string 
     * 
     * @param  Build state as an Integer constant.
     * @return A string representing the shortname of the state. Is used in messages.
     */
	public String getShortName(BuildStateEnum state) {
			return state.getShortName();
	}
	
	/**
	 * Convert build state Integer into descriptive string 
	 * 
	 * @param  Build state as an Integer constant.
	 * @return A string that fits into the sentence "The build has...<state>"
	 */
	public String getDescriptionSuffix(BuildStateEnum state) {
		return state.getDescriptionSuffix();
	}

	public boolean allEnabled() {
		boolean areAllEnbled = true;
		for (BuildStateEnum state : states.keySet()){
			if ((state.equals(BUILD_BROKEN) && states.get(BUILD_BROKEN).isEnabled()) || (state.equals(BUILD_FIXED) && states.get(BUILD_FIXED).isEnabled())){
				return false;
			}
			if (state.equals(BUILD_BROKEN) || state.equals(BUILD_FIXED)) {
				continue;
			}
			areAllEnbled = areAllEnbled && states.get(state).isEnabled();  
		}
		return areAllEnbled;
	}

	public boolean noneEnabled() {
		int enabled = 0;
		for (BuildStateEnum state : states.keySet()){
			if (state.equals(BUILD_FINISHED)){
				if (finishEnabled()){
					enabled++;
				}
			}
			if (state.equals(BUILD_FINISHED) || state.equals(BUILD_BROKEN) || state.equals(BUILD_FIXED) || state.equals(BUILD_SUCCESSFUL) || state.equals(BUILD_FAILED)){
				continue;
			}
			if (states.get(state).isEnabled()){
				enabled++;
			}
		}
		return enabled == 0;
	}
	
	
	private boolean finishEnabled(){
		// If finished is disabled, who cares what the other finish states are set to.
		if (! states.get(BUILD_FINISHED).isEnabled()){
			return false;
		}
		
		// If it's enabled, check its sub-settings.
		return (states.get(BUILD_FAILED).isEnabled() || states.get(BUILD_SUCCESSFUL).isEnabled());
	}
}
