/** Copyright © 2003 by Jean-Hugues de Raigniac <jhraigniac@workingfrog.org>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

package org.workingfrog.i18n.util;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;

/**
 * Analyse i18n stuff to find inconsistencies.
 *
 * @author Jean-Hugues de Raigniac
 */
public class Validator {

    private StringBuffer log = new StringBuffer();

    private String binding = null;
    private String i18nKey = null;
    private String localized = null;

    /** buffer to avoid redundant logs. */
    private HashMap logged = new HashMap();

    private HashSet bindings = new HashSet();

    private boolean name = false;
//    private boolean bundle = false;

    public Validator () {
        clearLogs();
    }

    public void clearLogs () {
        logged.clear();
        logged.put("bundles", new HashSet());
        logged.put("l10nKeys", new HashSet());
        //logged.put("listeners", new HashSet());
        logged.put("naming", new HashSet());
        logged.put("oldNaming", new HashSet());
    }

    private void clear () {
        log = new StringBuffer();
        binding = null;
        i18nKey = null;
        localized = null;
        name = false;
//        bundle = false;
    }

    public boolean isValidName () {
        return name;
    }

//    public boolean isValidBundle () {
//        return bundle;
//    }

    public void storeBundle (String binding) {
        bindings.add(binding);
    }

    public String getLocalizedValue () {
        return localized;
    }

    public boolean check (String key) {
        if (key != null) {
            this.i18nKey = key;
            clear();
            binding = getBinding(key);
            checkName(key);
            localized = checkValue(key);
        }

        return log.length() == 0;
    }

    public boolean checkName (String i18nKey) {
        if (!LogLevel.isChecking(LogLevel.NAMING)) {
            return true;
        }

        if (i18nKey == null) {
            return false;
        }

        StringBuffer result = new StringBuffer();

        if (!isLogged("naming", i18nKey)) {
            logElement("naming", i18nKey);

            if (i18nKey.indexOf(" ") != -1 || i18nKey.indexOf("\t") != -1) {

                if (i18nKey.indexOf(" ") != i18nKey.lastIndexOf(" ")
                    || i18nKey.indexOf("\t") != i18nKey.lastIndexOf("\t")) {
                    result.append("naming : key \"").append(i18nKey)
                          .append("\" contains white spaces.");
                } else {
                    result.append("naming : key \"").append(i18nKey)
                          .append("\" contains a white space.");
                }
            }
            if (!i18nKey.toLowerCase().equals(i18nKey)) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append("naming : key \"").append(i18nKey)
                      .append("\" contains upper case letter(s).");
            }

            if (binding != null) {
                checkBundle(binding);
            } else {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append("naming : key \"").append(i18nKey)
                      .append("\" must be prefixed with it's bundle name.");
            }
        }

        if (result.length() > 0) {
            log.append(result.toString());
            return false;
        } else {
            return true;
        }
    }

    public boolean checkBundle (String binding) {

        boolean result = bindings.contains(binding);

        if (!isLogged("bundles", binding)) {
            logElement("bundles", binding);

            if (!result) {
                log.append("i18n : bundle \"")
                   .append(binding)
                   .append("\" not loaded.");
            }
        }

        return result;
    }

    public String checkValue (String i18nKey) {

        if (!LogLevel.isChecking(LogLevel.L10N)) {
            return Localizer.localize(binding, i18nKey);
        }

        // new naming
        if (bindings.contains(binding)) {
            if (Localizer.containsKey(binding, i18nKey)) {
                return Localizer.localize(binding, i18nKey);
            } else {
                if (!isLogged("l10nKeys", i18nKey)) {
                    logElement("l10nKeys", i18nKey);
                    log.append("l10n : key \"")
                       .append(i18nKey)
                       .append("\" missing for ")
                       .append(Locale.getDefault().getDisplayName())
                       .append(".");
                }
                return
                    new StringBuffer("[ ").append(i18nKey).append(" ]")
                        .toString();
            }
        }

        // old naming
        if (!isLogged("oldNaming", i18nKey)) {
            logElement("oldNaming", i18nKey);
            System.out.println(i18nKey + " not parsed");
        }

        return new StringBuffer("[ ").append(i18nKey).append(" ]").toString();
    }

    public boolean isLocaleListener (Object object, Object source) {
        if (!LogLevel.isChecking(LogLevel.I18N)) {
            return (object instanceof LocaleListener);
        }

        if (!(object instanceof LocaleListener)) {
            Translator.log("i18n : source : "
                           + source.getClass().getName());
            /*Translator.log("    " + object.getClass().getName()
                           + " is not a LocaleListener");*/
//            if (!isLogged("listeners", object.getClass().getName())) {
//                logElement("listeners", object.getClass().getName());
                Translator.log("i18n : " + object.getClass().getName()
                               + " is not a LocaleListener");
//            }

            return false;
        }

        return true;
    }

    private boolean isLogged (String level, String element) {
        return ((HashSet) logged.get(level)).contains(element);
    }

    private void logElement (String level, String element) {
        ((HashSet) logged.get(level)).add(element);
    }

    public String getBinding (String key) {
        if (key != null) {
            return (key.indexOf(".") > 0)
                ? key.substring(0, key.indexOf("."))
                : null;
        } else {
            return null;
        }
    }

    public String getReport () {
        return log.toString();
    }
}
