//   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.evaluation.result;

import static java.lang.String.format;

import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import de.ugoe.cs.autoquest.usability.DefectDescription;
import de.ugoe.cs.autoquest.usability.ParameterFragment;

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

    private UsabilityDefectSeverityLevel severityLevel;

    private DefectDescription defectDescription;

    private Map<String, String> descriptionParametersValues;

    public UsabilityDefect(UsabilityDefectSeverityLevel severityLevel,
                           DefectDescription defectDescription,
                           Map<String, String> descriptionParametersValues)
    {
        super();
        this.severityLevel = severityLevel;
        this.defectDescription = defectDescription;
        this.descriptionParametersValues = descriptionParametersValues;
    }

    public String defectDescription() {
        if (containsParameterFragments(defectDescription)) {
            return assembleDefectDescription();
        }
        else {
            return Joiner.on(" ").skipNulls()
                .join(defectDescription.getTextFragmentOrParameterFragment());
        }
    }

    private boolean containsParameterFragments(DefectDescription defectDescription) {
        return Iterables.any(defectDescription.getTextFragmentOrParameterFragment(),
                             new Predicate<Object>() {

                                 @Override
                                 public boolean apply(Object fragment) {
                                     return fragment instanceof ParameterFragment;
                                 }

                             });
    }

    private String assembleDefectDescription() {
        List<String> descriptionParts =
            Lists.newArrayListWithCapacity(defectDescription.getTextFragmentOrParameterFragment()
                .size());

        for (Object fragment : defectDescription.getTextFragmentOrParameterFragment()) {
            if (isParameterFragment(fragment)) {
                descriptionParts.add(parameterFragmentAsString((ParameterFragment) fragment));
            }
            else {
                descriptionParts.add(CharMatcher.WHITESPACE.collapseFrom((String) fragment, ' ')
                    .trim());
            }
        }

        return Joiner.on(' ').join(descriptionParts);
    }

    private boolean isParameterFragment(Object object) {
        return object instanceof ParameterFragment;
    }

    private String parameterFragmentAsString(ParameterFragment fragment) {
        String value =
            getValueOrEmptyString(descriptionParametersValues, fragment.getParameterName());
        if (StringUtils.isNotEmpty(value)) {
            return value;
        }
        else {
            throw new IllegalArgumentException(
                                               format("required parameter \"%s\" for usability defect description not provided",
                                                      fragment.getParameterName()));
        }
    }

    private String getValueOrEmptyString(Map<String, String> stringKeyValueMap, String key) {
        return stringKeyValueMap != null && stringKeyValueMap.containsKey(key) ? stringKeyValueMap
            .get(key) : StringUtils.EMPTY;
    }

    public UsabilityDefectSeverityLevel getSeverityLevel() {
        return severityLevel;
    }

}
