package tim.infobus.multilanguage;

/**
* Java API standard Imports
*/
import java.net.URL;

/**
* W3C Imports
*/
import org.w3c.dom.Node;

/**
* InfoBUS Imports
*/
import tim.infobus.configuration.XMLConfig;
import tim.infobus.configuration.XMLConfigException;

/**
 * <code>IBException</code> is the exception raised by IB mechanisms in
 * case of errors.<p>
 *
 * <p><b>History:</b>
 * <pre>
 * $Workfile: IBException.java $
 * $Archive: /infobus/ib/components/ib_commons/src/tim/infobus/data/IBException.java $
 * $Revision: 1.1 $
 * $Author: itsstefa $
 * $Date: 2004/09/22 15:39:50 $
 * $Modtime: 16/04/03 11.50 $
 *
 * HISTORY
 * -----------------------------------------------------------------------------
 * 10/05/2003  federica.v
 *             methods getMessage(); getAction(); getDescription(); inserted
 *             New exception organization with different catalogs error
 *             in different packages.
 *             (IBError.class not generated with XSL file)
 * -----------------------------------------------------------------------------
 * 05/11/2003  federica.v
 *             insert the language management with the locale@lang attribute
 * -----------------------------------------------------------------------------
 *
 */
public class IBException extends Exception
{
    //--------------------------------------------------------------------------
    // CONSTANTS
    //--------------------------------------------------------------------------

    public static final int DEFAULT_ERROR_CODE = -1;
    public static final String DEFAULT_LOCALE = "ENG";

    //--------------------------------------------------------------------------
    // FIELDS
    //--------------------------------------------------------------------------

    /**
     * This is the error identifier
     */
    private String idMessage;

    /**
     * This is the params to complete the error message
     */
    private String[][] params;

    /**
     * The error code to identify the message corrisponding
     */
    private int errorCode;

    /**
     * The error message (in the errors catalog)
     */
    private String message;

    /**
     * The actions to perform (in the errors catalog)
     */
    private String actions;

    /**
     * The cause of error (in the errors catalog)
     */
    private String causeDescription;

    /**
     * The class name where the error occurred
     */
    private String className;

    /**
     * The locale language (in the Locale.xml file)
     */
    private String lang;

    /**
     * The locale default language (in the Locale.xml file)
     */
    private String defaultLocale;

    /**
     * This flag indicates if the catalog has been accessed in order to
     * retreive error informations.
     */
    private boolean retrieved;

    //--------------------------------------------------------------------------
    // CONSTRUCTORS
    //--------------------------------------------------------------------------

    /**
     * Creates a new IBException with identifier message
     * @param idMessage identifier message associated to an error message in the reposistory error
     */
    public IBException(String idMessage) {
        this.idMessage = idMessage;
        this.params = null;
        errorCode = DEFAULT_ERROR_CODE;
        defaultLocale = DEFAULT_LOCALE;
        lang = readLocale();
        retrieved = false;
    }

    /**
     * Creates a new IBException with identifier message and
     * <code>params</code> used to complete the error message.
     *
     * @param idMessage identifier message associated to an error message in the reposistory error
     * @param params key/value array of params to be sobstituted in the error message.
     */
    public IBException(String idMessage, String[][] params) {
        this.idMessage = idMessage;
        this.params = params;
        errorCode = DEFAULT_ERROR_CODE;
        defaultLocale = DEFAULT_LOCALE;
        lang = readLocale();
        retrieved = false;
    }

    //--------------------------------------------------------------------------
    // METHODS
    //--------------------------------------------------------------------------

    /**
       * This method get the language to use in the Locale.xml file
       * and the default language to use if the language is not setted.
       * @return locale the language requested
       */
       public String readLocale() {

            String locale = "";
            String localeFile = "Locale.xml";
            String xPath = "/Locale/@lang";

            try {
                locale = XMLConfig.get(localeFile, xPath);

                String defaultLocaleXPath = "/Locale/@default_lang";
                defaultLocale = XMLConfig.get(localeFile, defaultLocaleXPath);
            }
            catch(XMLConfigException exc) {
               // Do nothing.
               // We assume that the node is not in the catalog.
               // Default language will be provided.
            }

            return locale;
       }

    /**
     * Return the error code.
     * @return error code found in the error catalog.
     */
    public int getErrorCode() {
        if(!retrieved) {
            retreiveErrorInformation();
        }
        return errorCode;
    }

    /**
     * Return the cause description of occurred error.
     * @return cause found in the error catalog.
     */
    public String getDescription() {
        if(!retrieved) {
            retreiveErrorInformation();
        }

        return causeDescription;
    }

    /**
     * Return the action.
     * @return action found in the error catalog
     */
    public String getActions() {
        if(!retrieved) {
            retreiveErrorInformation();
        }

        return actions;
    }

    /**
     * Return the error message.
     * @return message found in the error catalog
     */
    public String getMessage() {
        if(!retrieved) {
            retreiveErrorInformation();
        }

        return message;
    }

    /**
     * This method read the error informations of xml error catalog.
     */
    private void retreiveErrorInformation() {

        retrieved = true;

        Node errorNode = findIBErrorNode();
        if(errorNode == null) {
            appliesDefaults();
            return;
        }

        appliesNode(errorNode);
    }

    /**
     * This method applies a default error message
     * in case of no error was found in the catalogs.
     */
    private void appliesDefaults() {

        errorCode = DEFAULT_ERROR_CODE;
        message = calculateDefaultMessage();
        actions = "N/A";
        causeDescription = "N/A";
    }

    /**
     * This method calcules the default error message
     * @return msg the default message
     */
    private String calculateDefaultMessage() {
        StringBuffer msg = new StringBuffer();

        msg.append(idMessage).append(" [").append(className).append("]: ");
        if (params != null) {
            for(int i = 0; i < params.length; ++i) {
                msg.append(params[i][0]).append("=").append(params[i][1]);
                if(i < params.length - 1) {
                    msg.append(", ");
                }
            }
        }
        return msg.toString();
    }

    /**
     * This method find the Error message requested with the correct language setted
     * @param errorNode
     */
    private void appliesNode(Node errorNode) {

        Node localeNode = null;
        errorCode = XMLConfig.getInteger(errorNode, "@code", DEFAULT_ERROR_CODE);
        try {
            localeNode = XMLConfig.getNode(errorNode, "locale[@lang = '" + lang + "']");
            if (localeNode == null) {
                System.out.println("locale is null");
                localeNode = XMLConfig.getNode(errorNode, "locale[@lang = '" + defaultLocale + "']");
                if (localeNode == null) {
                    System.out.println("Default Locale is null");
                    appliesDefaults();
                    return;
                }
            }
        } catch (XMLConfigException xmlConfExc) {
            appliesDefaults();
            return;
        }
        message = XMLConfig.get(localeNode, "message", "N/A");
        message = translateMsg(message, params);
        message = idMessage + " [" + className + "]: " + message;
        actions = XMLConfig.get(localeNode, "action", "N/A");
        causeDescription = XMLConfig.get(localeNode, "cause", "N/A");
    }

    /**
     * find catalog where the message id is present.
     * @return ibErrorNode IBErrorNode in the catalog error
     */
    private Node findIBErrorNode() {

        String xPath = "/IBErrorCatalog/IBError[@id='" + idMessage + "']";
        Class cls = this.getClass();
        className = cls.getName();

        Package previousPackage = null;
        while(cls != null) {
            ClassLoader loader = cls.getClassLoader();
            Package currentPackage = cls.getPackage();
            if(previousPackage != currentPackage) {
                previousPackage = currentPackage;
                String catalogName = getCatalogName(currentPackage);
                System.out.println("*** " + catalogName);
                URL url;
                if(loader != null) {
                    url = loader.getResource(catalogName);
                }
                else {
                    url = ClassLoader.getSystemResource(catalogName);
                }
                try {
                    if(url != null) {
                        Node node = XMLConfig.getNode(catalogName, xPath);
                        if(node != null) {
                            return node;
                        }
                    }
                }
                catch(XMLConfigException exc) {
                    // Do nothing.
                    // We assume that the node is not in the catalog.
                    // Default message will be provided.
                }
            }
            cls = cls.getSuperclass();
        }

        return null;
    }

    /**
    * create the package name of Errors.xml file
    * @param className name of class to create the package
    * @return catalogName fully qualified name of Errors.xml file
    */

    private String getCatalogName(Package pkg) {
        String packageName = pkg.getName();
        String catalogName = "catalogs/" + packageName + ".Errors.xml";
        return catalogName;
    }

    /**
     * Translates the message identified by <code>errorId</code> using the values
     * contained in <code>params</code>.
     *
     * @return the cause, or <code>null</code> if there are not a cause.
     */
    protected String translateMsg(String message, String[][] params)
    {
        if(params!=null){

            String beginParam = "${";
            String endParam = "}";

            for(int i=0; i<params.length; i++){
                String paramStr = beginParam + params[i][0] + endParam;
                if (message.indexOf(paramStr) != -1){
                    message = replaceParam(message, paramStr, params[i][1]);
                }
            }
        }

        return message;
    }

    /**
     * This method complete the error message with the params value
     * @param msg the message to complete
     * @param param the params to value
     * @param value params value
     * @return retString the message completed
     */
    private String replaceParam(String msg, String param, String value)
    {
        int beginIndex = msg.indexOf(param);

        String retString;

        if(beginIndex==-1){
            retString = msg;
        }
        else{
            int endIndex = beginIndex + param.length();
            retString = msg.substring(0, beginIndex) + value + msg.substring(endIndex);
        }

        return retString;
    }
}