package com.psc.dsg.di.bemis.infrastructure.logger;

import com.psc.dsg.di.bemis.vobject.VOUser;
import com.psc.dsg.di.bemis.interfaces.ICommon;

import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import org.apache.log4j.Category;
import org.apache.log4j.PropertyConfigurator;


/**
 * Class for BEMiS cleint side logging. All the loging will be done in an XML file.
 *
 * Copyright: 2002<br>
 * Company  : HCL Perot Systems<br>
 * @author    Saurobh Kumar Roy
 * @version   3
 * <pre>
 * $Log :: /com/psc/dsg/di/bemis/infrastructure/logger/ClientLogger.java $
 *
 * 3  25/02/2002  Pradeep.P
 * Added documentation for methods
 *
 * 2  10/04/2002  Saurobh Kumar Roy
 * Replaced configure(String) with configure(URL) so that it
 * also reads from a jar file.
 *
 * 1  05/02/2002  Saurobh Kumar Roy
 * ClientLogger Class
 * </pre>
 */

public class ClientLogger {

    private static ClientLogger   loggerFile = null;
    public static final String    ERR_DESC   = "ErrDesc";
    private static final String   NOTAVAIL   = "NOT AVAILABLE";
    Category                      oCategory  = null;


    /**
     * Default constructor
     * Configures the log4j configuration file.
     *
     * @param classname    Name of the class which invoked.
     */

    private ClientLogger(String classname) {

        oCategory = Category.getInstance(classname);

        try {

            ClassLoader loader = org.apache.log4j.Category.class.getClassLoader();

            URL url = loader.getResource(ICommon.LOG_FILE);

            /*
             * PropertyConfigurator to parse a configuration file and set
             * up logging accordingly.
             */

            PropertyConfigurator.configure(url);

        }
        catch (Exception e) { }

    }


    /**
     * Gets an instance of the class if it does not exists.
     *
     * @return               this class object if it does not exists.
     */

    public static ClientLogger getInstance(String classname) {

        if (loggerFile == null) {
            loggerFile = new ClientLogger(classname);
        }

    return loggerFile;
    }


   /**
    * Gets the current date and time in string format.
    *
    * @return                String containing the date and time when the
    *                        logging event occured.
    */

    private  String getLoggingDate() {

        SimpleDateFormat  oDateFormatter = null;
        SimpleDateFormat  oTimeFormatter = null;
        String            sDate          = null;
        Date              currentDate    = Calendar.getInstance().getTime();
        String            dateFormat     = null;

        /* Date format to be locale specific */

        if (Locale.getDefault().toString().equals(ICommon.DEFAULT_LOCALE)) {
            dateFormat = ICommon.US_DATE_FORMAT;
        }
        else {
            dateFormat = ICommon.NON_US_DATE_FORMAT;
        }

        try {

            oDateFormatter = new SimpleDateFormat(dateFormat);
            oTimeFormatter = new SimpleDateFormat("HH:mm:sssss");
            sDate = oDateFormatter.format(currentDate)
                    + "T" + oTimeFormatter.format(currentDate);
        }
        catch(Exception e) { }
        return sDate;
    }


   /**
    * Logs a message object with the INFO priority.
    *
    * @param  sClientLog     Message in string format to be logged.
    *
    */

    public void storeClientLog(String sClientLog) {

        if (sClientLog != null) {
            oCategory.info("CLIENT" + encodeMessage(sClientLog));
        }
    }


   /**
    * Logs a message object with the INFO priority.
    *
    * @param className       Name in String format of the class which has
    *                        called for logging.
    * @param message         Error description in string.
    * @param oVOUser         VOUser containing the userid and password.
    *
    */

    public void info(String className, String message, VOUser oVOUser) {

        if (oVOUser == null) {
            oVOUser = new VOUser();
            oVOUser.setUserID(NOTAVAIL);
        }

        message = encodeMessage(message);

        /*Log a message object with the INFO priority.*/

        oCategory.info(buildMessage(className, message, oVOUser, "INFORMATION"));

    }


   /**
    * Logs a message object with the WARN priority.
    *
    * @param className       Name in String format of the class which has
    *                        called for logging.
    * @param message         Error description in string.
    * @param oVOUser         VOUser containing the userid and password.
    *
    */

    public void warn(String className, String message, VOUser oVOUser) {

        if (oVOUser == null) {
            oVOUser = new VOUser();
            oVOUser.setUserID(NOTAVAIL);
        }

        message = encodeMessage(message);

        /*Log a message object with the WARN priority.*/

        oCategory.warn(buildMessage(className, message, oVOUser, "WARNING"));
    }


   /**
    * Logs a message object with the DEBUG priority.
    *
    * @param className       Name in String format of the class which has
    *                        called for logging.
    * @param message         Error description in string.
    * @param oVOUser         VOUser containing the userid and password.
    *
    */

    public void debug(String className, String message, VOUser oVOUser) {

        if (oVOUser == null) {
            oVOUser = new VOUser();
            oVOUser.setUserID(NOTAVAIL);
        }

        message = encodeMessage(message);

        /*Log a message object with the DEBUG priority.*/

        oCategory.debug(buildMessage(className, message, oVOUser, "DEBUG"));
    }


   /**
    * Logs a message object with the ERROR priority.
    *
    * @param className       Name in String format of the class which has
    *                        called for logging.
    * @param message         Error description in string.
    * @param oVOUser         VOUser containing the userid and password.
    *
    */

    public void error(String className, String message, VOUser oVOUser) {

        if (oVOUser == null) {
            oVOUser = new VOUser();
            oVOUser.setUserID(NOTAVAIL);
        }

        message = encodeMessage(message);

        /*Log a message object with the INFO priority.*/

        oCategory.error(buildMessage(className, message, oVOUser, "ERROR"));
    }


   /**
    * Logs a message object based on the priority with which it is called.
    *
    * @param className       Name in String format of the class which has
    *                        called for logging.
    * @param message         Error description in string.
    * @param oVOUser         VOUser containing the userid and password.
    * @param String          Type of priority
    */

    public String buildMessage(String className, String message, VOUser oVOUser,
                                                  String errorType) {

        String userMessage =

        "ErrType" + "=\"" + errorType + "\"^" + ERR_DESC +
        "=\"" + message + "\"^" + "ErrDt" + "=\"" +
        getLoggingDate() + "\"^" + "ApplName" + "=\"" +
        className + "\"^" + "UName" + "=\"" +
        ((oVOUser.getUserID() != null)?oVOUser.getUserID():NOTAVAIL) + "^\"";

        return userMessage;
    }


   /**
    * Logs a message object with the ERROR priority.
    *
    * @param className       Name in String format of the class which has
    *                        called for logging.
    * @param message         Error description in string.
    * @param oVOUser         VOUser containing the userid and password.
    * @param errorObj        Throwable object.
    *
    */

    public void error(String className, String message, VOUser oVOUser,
                                                        Throwable errorObj) {
        if (oVOUser == null) {
                oVOUser = new VOUser();
                oVOUser.setUserID(NOTAVAIL);
        }

        message = encodeMessage(message);

        /*Log a message object with the ERROR priority.*/

        oCategory.error( "ErrType" + "=\"" + "ERROR\"^" + ERR_DESC + "=\""+
        message + "^" + "\"^" + "ErrDt" +"=\""+ getLoggingDate() +"\"^"+
        "ApplName" + "=\"" + className +"\"^" + "UName" + "=\"" +
        ((oVOUser.getUserID() != null)?oVOUser.getUserID():NOTAVAIL) + "\"^"
        + "\"",errorObj);

    }


   /**
    * Formats the message after removing the special characters
    * like '&amp' which are special characters for XML.
    *
    * @param sMessage        String containing the message.
    * @return oString        String containing the formatted message.
    */

    static String encodeMessage(String sMessage) {

        String oString  = sMessage;

        if(oString == null)
            return " exception is null";


        if ((oString.indexOf("&amp;") < 0)) {
            oString = encodeString(oString); //remove amp
        }

        if ((oString.indexOf("&lt;") < 0)) {
            oString = encodeString(oString, "&lt;", '<');
        }

        if ((oString.indexOf("&gt;") < 0)) {
            oString = encodeString(oString, "&gt;", '>');
        }

    return oString;
    }


    /**
     * Checks for '&' in the given string and replaces
     * it with 'amp;'.
     *
     * @param oldString      String containing the message.
     * @return               String containing the formatted message.
     */

    public static String encodeString(String oldString) {

        int          ch          = '&';
        int          iLastAmpPos = 0;
        int          iAmpPos     = 0;
        StringBuffer sTempSBuf   = null;

        if (oldString == null) {
            return oldString;
        }
        iLastAmpPos = oldString.lastIndexOf(ch);
        sTempSBuf   = new StringBuffer(oldString);

        while (iLastAmpPos != -1) {

                if (iAmpPos == -1) {
                    break;
                }
            iAmpPos     = oldString.indexOf(ch, iAmpPos);
            oldString   = sTempSBuf.insert((iAmpPos+1), "amp;").toString();
            iLastAmpPos = oldString.lastIndexOf(ch);

                if (iAmpPos == iLastAmpPos ) {
                    break;
                }
                iAmpPos = iAmpPos + 1;
        }
        return sTempSBuf.toString();
    }


    /**
     * Checks for a character in the given string and replaces
     * it with replaceString.
     *
     * @param oldString      String containing the message.
     * @param replaceString  The string to be replaced.
     * @param searchChar     The character in the old string to be replaced.
     * @return               String containing the formatted message.
     */

    public static String encodeString(String oldString, String replaceString,
                                                            char searchChar) {
        int          ch          = searchChar;
        int          iLastAmpPos = 0;
        int          iAmpPos     = 0;
        StringBuffer sTempSBuf   = null;

        if (oldString == null) {
            return oldString;
        }

        iLastAmpPos = oldString.lastIndexOf(ch);
        sTempSBuf   = new StringBuffer(oldString);

        while (iLastAmpPos != -1) {

                if (iAmpPos == -1) {
                    break;
                }

            iAmpPos = oldString.indexOf(ch, iAmpPos);
            sTempSBuf.deleteCharAt(iAmpPos);
            oldString = sTempSBuf.insert((iAmpPos), replaceString).toString();
            iLastAmpPos = oldString.lastIndexOf(ch);

                if (iAmpPos == iLastAmpPos ) {
                    break;
                }
                iAmpPos = iAmpPos + 1;
        }
        return sTempSBuf.toString();
    }
}

