//   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;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;

/**
 * TODO comment
 * 
 * @version $Revision: $ $Date: 18.07.2012$
 * @author 2012, last modified by $Author: pharms$
 */
public enum UsabilitySmellDescription implements Serializable {
    
    INEFFICIENT_ACTIONS,
    TEXT_FIELD_INPUT_RATIO,
    TEXT_FIELD_INPUT_REPETITIONS,
    TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO,
    COOCCURENCE_SUCCEED,
    COOCCURENCE_PRECED,
    HIGH_EVENT_COVERAGE,
    HIGH_TARGET_DISTANCE,
    MISSING_FEEDBACK,
    TASK_RETRIED,
    UNUSED_GUI_ELEMENTS,
    DATA_ENTRY_METHOD_CHANGE,
    GOOD_DEFAULTS,
    CHECK_BOX_SINGLE_SELECTION,
    COMMON_TASK_RATE,
    MISLEADING_CLICK_CUE,
    MOST_OFTEN_DONE_FIRST;

    /** */
    private static final String DEFAULT_MESSAGES_FILE = "smellDescriptions_en.xml";

    /** */
    private static SmellDescriptions sSmellDescriptions;

    /** */
    private SmellDescription smellDescription;

    /**
     *
     */
    private UsabilitySmellDescription() {
        init();
    }

    /**
     *
     */
    @SuppressWarnings("unchecked")
    private void init() {
        synchronized (this.getClass()) {
            if (sSmellDescriptions == null) {
                InputStream inputStream =
                    ClassLoader.getSystemResourceAsStream(DEFAULT_MESSAGES_FILE);

                try {
                    String packageName = SmellDescriptions.class.getPackage().getName();
                    JAXBContext jaxbContext = JAXBContext.newInstance(packageName);
                    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

                    sSmellDescriptions =
                        ((JAXBElement<SmellDescriptions>) unmarshaller.unmarshal(inputStream))
                            .getValue();
                }
                catch (Exception e) {
                    throw new RuntimeException
                        ("error while initializing usability smell descriptions", e);
                }
                finally {
                    if (inputStream != null) {
                        try {
                            inputStream.close();
                        }
                        catch (IOException e) {
                            // ignore
                        }
                    }
                }
            }
        }

        for (SmellDescription description : sSmellDescriptions.getSmellDescription()) {
            if (this.name().equals(description.getSmellId())) {
                smellDescription = description;
                break;
            }
        }

        if (smellDescription == null) {
            throw new RuntimeException
                ("error while initializing usability smell descriptions. No " +
                 "description text available for description " + this.name());
        }
    }

    /**
     * 
     */
    public String[] getDescriptionParameters() {
        List<String> parameters = new ArrayList<String>();

        for (Object fragment : smellDescription.getTextFragmentOrParameterFragment()) {
            if (fragment instanceof ParameterFragment) {
                parameters.add(((ParameterFragment) fragment).getParameterName());
            }
        }

        return parameters.toArray(new String[parameters.size()]);
    }

    /**
     * 
     */
    public String toString(Map<String, Object> parameters) throws IllegalArgumentException {
        StringBuffer result = new StringBuffer();

        for (Object fragment : smellDescription.getTextFragmentOrParameterFragment()) {
            if (result.length() > 0) {
                result.append(" ");
            }

            if (fragment instanceof ParameterFragment) {
                Object value = null;
                if (parameters != null) {
                    value = parameters.get(((ParameterFragment) fragment).getParameterName());
                }

                if (value != null) {
                    if (value instanceof Collection<?>) {
                        int counter = 1;
                        for (Object elem : ((Collection<?>) value)) {
                            result.append('\n');
                            result.append(counter++);
                            result.append(".: ");
                            result.append(elem);
                        }
                    }
                    else {
                        result.append(value.toString());
                    }
                }
                else {
                    throw new IllegalArgumentException
                        ("required parameter \"" +
                         ((ParameterFragment) fragment).getParameterName() +
                         "\" for usability smell description " + this.name() + " not provided");
                }
            }
            else {
                result.append(getFragmentString(fragment));
            }
        }

        return result.toString();
    }

    /**
     * 
     */
    public List<Object> toFragmentList(Map<String, Object> parameters)
        throws IllegalArgumentException
    {
        List<Object> result = new ArrayList<Object>();

        for (Object fragment : smellDescription.getTextFragmentOrParameterFragment()) {
            if (fragment instanceof ParameterFragment) {
                Object value = null;
                if (parameters != null) {
                    value = parameters.get(((ParameterFragment) fragment).getParameterName());
                }

                if (value != null) {
                    result.add(value);
                }
                else {
                    throw new IllegalArgumentException
                        ("required parameter \"" +
                         ((ParameterFragment) fragment).getParameterName() +
                         "\" for usability smell description " + this.name() + " not provided");
                }
            }
            else {
                result.add(getFragmentString(fragment));
            }
        }

        return result;
    }
    
    /**
     * 
     */
    public String getBriefDescription() {
        return smellDescription.briefText;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Enum#toString()
     */
    @Override
    public String toString() {
        StringBuffer result = new StringBuffer();

        int paramCount = 1;
        for (Object fragment : smellDescription.getTextFragmentOrParameterFragment()) {
            if (result.length() > 0) {
                result.append(" ");
            }

            if (fragment instanceof ParameterFragment) {
                result.append("<parameter");
                result.append(paramCount++);
                result.append(">");
            }
            else {
                result.append(getFragmentString(fragment));
            }
        }

        return result.toString();
    }

    /**
     *
     */
    private String getFragmentString(Object fragment) {
        String fragmentStr = fragment.toString().trim();

        fragmentStr = fragmentStr.replaceAll("\n", " ");

        while (fragmentStr.indexOf("  ") > -1) {
            fragmentStr = fragmentStr.replaceAll("  ", " ");
        }

        return fragmentStr;
    }

}
