//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package de.ugoe.cs.autoquest.usability.result;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.EnumMap;
import java.util.Map;
import java.util.Map.Entry;

import jodd.props.Props;

import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;

import de.ugoe.cs.util.console.Console;

/**
 * <p>
 * TODO comment
 * </p>
 * 
 * @author Alexander Deicke
 */
public class DefectDescriptionResolver {

    private final String defectDescriptionFile = "defects.props";

    /**
     * <p>
     * TODO: comment
     * </p>
     * 
     * @param name
     * @return
     */
    public UsabilityDefect descriptionFor(String usabilityRuleName) {
        Props allProperties = initProperties();
        Map<String, String> usabilityRuleProperties =
            allUsabilityRuleProperties(allProperties, usabilityRuleName);
        return createUsabilityDefect(usabilityRuleProperties);
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     * 
     * @return
     */
    private Props initProperties() {
        Optional<File> defectDescriptionFile = getDefectDescriptionFile();
        Props props = new Props();
        props.setEscapeNewLineValue("\n");
        props.setValueTrimLeft(true);
        if (defectDescriptionFile.isPresent()) {
            loadProperties(defectDescriptionFile, props);
        }
        return props;
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     * 
     * @return
     */
    private Optional<File> getDefectDescriptionFile() {
        try {
            return Optional.fromNullable(new File(ClassLoader
                .getSystemResource(defectDescriptionFile).toURI()));
        }
        catch (URISyntaxException e) {
            Console.printerr("Error while loading defect description file.");
            Console.logException(e);
            return Optional.absent();
        }
    }

    private void loadProperties(Optional<File> defectDescriptionFile, Props props) {
        try {
            props.load(defectDescriptionFile.get());
        }
        catch (IOException e) {
            Console.logException(e);
        }
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     * 
     * @return
     */
    private Map<String, String> allUsabilityRuleProperties(Props allProperties,
                                                                   String usabilityRuleName)
    {
        Map<String, String> usabilityRuleProperties = Maps.newHashMap();
        allProperties.extractSubProps(usabilityRuleProperties, usabilityRuleName + ".*");
        return usabilityRuleProperties;
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     * 
     * @param usabilityRuleProperties
     * @return
     */
    private UsabilityDefect createUsabilityDefect(Map<String, String> usabilityRuleProperties) {
        String description =
            Iterables.getOnlyElement(Maps.filterKeys(usabilityRuleProperties, descriptionProperty()).values());
        EnumMap<UsabilityDefectSeverityLevel, Double> severity =
            getSeverityMap(usabilityRuleProperties);
        return new UsabilityDefect(description, severity);
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     * 
     * @return
     */
    private Predicate<String> descriptionProperty() {
        return new Predicate<String>() {

            public boolean apply(String key) {
                return key.endsWith("description");
            }

        };
    }
    
    /**
     * <p>
     * TODO: comment
     * </p>
     * 
     * @param usabilityRuleProperties
     * @return
     */
    private EnumMap<UsabilityDefectSeverityLevel, Double> getSeverityMap(Map<String, String> usabilityRuleProperties)
    {
        EnumMap<UsabilityDefectSeverityLevel, Double> severityMap =
            Maps.newEnumMap(UsabilityDefectSeverityLevel.class);
        Map<String, String> allSeverityProperties =
            Maps.filterEntries(usabilityRuleProperties, allSeverityProperties());
        for (Entry<String, String> severityProperty : allSeverityProperties.entrySet()) {
            UsabilityDefectSeverityLevel severityLevel =
                getSeverityLevel(severityProperty.getKey());
            Double rule = Double.valueOf(severityProperty.getValue());
            severityMap.put(severityLevel, rule);
        }
        return severityMap;
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     * 
     * @param key
     * @return
     */
    private UsabilityDefectSeverityLevel getSeverityLevel(String severityProperty) {
        int startSeverityLevel = severityProperty.lastIndexOf(".") + 1;
        String severitLevelIdentifier =
            severityProperty.substring(startSeverityLevel).toUpperCase();
        return UsabilityDefectSeverityLevel.valueOf(severitLevelIdentifier);
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     * 
     * @return
     */
    private Predicate<Entry<String, String>> allSeverityProperties() {
        return new Predicate<Map.Entry<String, String>>() {

            public boolean apply(Map.Entry<String, String> entry) {
                return entry.getKey().contains("severity");
            }

        };
    }

}
