package com.n2bb.util;

import java.sql.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;

/**
 * Gets/sets UI properties from database.
 * <p>
 * This class is installed to tomcat/common so that
 * <code>N2bbDataSourceRealm</code> can look up password expiration
 * without having to know the details of how it's stored.
 *
 * @author kmatsuoka
 * @version $Id: PropertyManager.java,v 1.1 2006/06/17 00:48:48 rao Exp $
 */
public class PropertyManager { 
    public static final String UI_DATA_SOURCE = "java:/uiDataSource";

    private static Log log = LogFactory.getLog(PropertyManager.class);

    private static PropertyManager instance;

    // database table/column names
    public static final String PROPERTIES_TABLE = "UI_PROPERTIES";
    public static final String VALUE_COLUMN = "VALUE";
    public static final String NAME_COLUMN = "NAME";

    // Property names
    public static final String PASSWORD_LIFE_PROPERTY = "PASSWORD_LIFE";
    public static final String SESSION_TIMEOUT_PROPERTY = "SESSION_TIMEOUT";
    public static final String PAGE_SIZE_PROPERTY = "PAGE_SIZE";
    public static final String SITE_LOCATION_PROPERTY = "SITE_LOCATION";
    public static final String SHOW_USER_PROPERTY = "SHOW_USER";
    public static final String SHOW_DATE_PROPERTY = "SHOW_DATE";

    // Default property values
    public static final int DEFAULT_PASSWORD_LIFE = 0;
    public static final int DEFAULT_SESSION_TIMEOUT = 30;
    public static final int DEFAULT_PAGE_SIZE = 10;
    public static final String DEFAULT_SITE_LOCATION = "Default Site Name";
    public static final String DEFAULT_SHOW_USER = "true";
    public static final String DEFAULT_SHOW_DATE = "true";

    /**
     * Constructor.
     */
    private PropertyManager() {
    }

    /** Shared data source for whole UI */
    private static DataSource dataSource;

    /**
     * Gets UI's data source.
     *
     * @return UI's data source
     */
    public synchronized static DataSource getUIDataSource() {
        if (dataSource == null) {
            try {
                Context envContext = (Context)
                        new InitialContext();
                dataSource = (DataSource) envContext.lookup(UI_DATA_SOURCE);
                               
            }
            catch (NamingException e) {
                log.warn("Unable to find data source using JNDI", e);
                return null;
            }
        }
        return dataSource;
    }

    /** Gets the singleton instance of this class. */
    public static synchronized PropertyManager getInstance() {
        if (instance == null) {
            instance = new PropertyManager();
        }
        return instance;
    }

    /**
     * Gets a property value as an integer, or a default value
     * if value not found or unparseable.
     *
     * @param name          propery name
     * @param defaultValue  default value
     * @return property value as integer, or default value
     * if value not found or unparseable.
     */
    public int getInteger(String name, int defaultValue) {
        Integer value = null;
        try {
            value = getInteger(name);
        }
        catch (NumberFormatException ex) {
            log.error("Value for property " + name + " is not a parseable integer");
        }
        if (value == null) {
            return defaultValue;
        }
        else {
            return value.intValue();
        }
    }

    /**
     * Gets an integer value for a property.
     *
     * @param name  name of property
     * @return value of property parsed as integer, or null if no value found
     * @throws NumberFormatException if value can't be parsed as integer
     */
    public Integer getInteger(String name) throws NumberFormatException {
        String value = getValue(name);
        if (value != null) {
            return new Integer(value);
        }
        else {
            return null;
        }
    }


    /**
     * Gets a property value,
     * with default if none found
     *           OR if value is empty
     *           OR if value is all white space.
     *
     * @param name      property name
     * @param default   default to use if none found
     * @return          property value
     */
    public String getValue(String name, String defaultValue) {
        String value = getValue(name);
        if (value == null || value.trim().equals("")) {
            return defaultValue;
        }
        return value;
    }

    /**
     * Gets a property value.
     *
     * @param name  property name
     * @return      property value
     */
    public String getValue(String name) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = getUIDataSource().getConnection();
            ps = conn.prepareStatement(
                    "select " + VALUE_COLUMN +
                    " from  " + PROPERTIES_TABLE +
                    " where " + NAME_COLUMN + " = ?"
            );
            ps.setString(1, name);
            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                return rs.getString(1);
            }
        }
        catch (Exception ex) {
            log.error("Error getting value for property with name = " + name, ex);
        }
        finally {
            closeStatement(ps);
            closeConnection(conn);
        }
        return null;
    }

    /**
     * Sets a property.
     *
     * @param name  property name
     * @param value property value
     */
    public void setValue(String name, String value) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = getUIDataSource().getConnection();
            ps = conn.prepareStatement(
                    "delete " +
                    " from  " + PROPERTIES_TABLE +
                    " where " + NAME_COLUMN + " = ?"
            );
            ps.setString(1, name);
            ps.executeUpdate();
            ps = conn.prepareStatement(
                    "insert into " + PROPERTIES_TABLE +
                    " values (?, ?)"
            );
            ps.setString(1, name);
            ps.setString(2, value);
            int count = ps.executeUpdate();
            if (count != 1) {
                log.error("Unable to set value for property with name = " + name);
            }
        }
        catch (SQLException ex) {
            log.error("Unable to set value for property with name = " + name, ex);
        }
        finally {
            closeStatement(ps);
            closeConnection(conn);
        }
    }

    private void closeConnection(Connection con) {
        if (con != null) {
            try {
                con.close();
            }
            catch (SQLException e) {
            }
        }
    }

    private void closeStatement(Statement statement) {
        if (statement != null) {
            try {
                statement.close();
            }
            catch (SQLException e) {
            }
        }
    }

}
