/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (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.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * 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 com.queplix.core.utils.dao;

import com.queplix.core.error.GenericSystemException;
import com.queplix.core.utils.log.AbstractLogger;
import com.queplix.core.utils.xml.XMLHelper;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * Abstract system property access factory
 *
 * @author [ALB] Baranov Andrey
 * @version $Revision: 1.4 $ $Date: 2006/04/03 06:47:56 $
 */

public abstract class AbstractPropertyFactory
        extends AbstractLogger {

    // ===================================================== Constants

    public final static String SYS_CONFIG_PREFIX = "com/queplix/config/sys/";
    public final static String APP_CONFIG_PREFIX = "com/queplix/config/app/";
    public static final String RESOURCES_PREFIX
            = "com/queplix/core/ejb/resources/";

    // ===================================================== Public methods

    /**
     * Loads system properties as InputStream.
     *
     * @param clazz      class loader's class
     * @param configFile local file name
     * @return InputStream
     */
    public static InputStream loadSysPropertiesAsStream(Class clazz,
                                                        String configFile) {
        String fullPath = SYS_CONFIG_PREFIX + configFile;
        InputStream is = clazz.getClassLoader().getResourceAsStream(fullPath);
        if(is == null) {
            throw new NullPointerException("System Property '" + configFile +
                    "' (path: '" + fullPath + "') not found!");
        }
        return is;
    }

    /**
     * Loads application specific properties as InputStream.
     *
     * @param clazz      class loader's class
     * @param configFile local file name
     * @return InputStream
     */
    public static InputStream loadAppPropertiesAsStream(Class clazz,
                                                        String configFile) {
        String fullPath = APP_CONFIG_PREFIX + configFile;
        InputStream is = clazz.getClassLoader().getResourceAsStream(fullPath);
        return is;
    }

    /**
     * Loads resource file as InputStream.
     *
     * @param clazz        class loader's class
     * @param resourceFile resource file path relative to the resources directory
     * @return InputStream
     * @see #RESOURCES_PREFIX
     */
    public static InputStream loadResourceAsStream(Class clazz,
                                                   String resourceFile) {
        String fullPath = RESOURCES_PREFIX + resourceFile;
        InputStream is = clazz.getClassLoader().getResourceAsStream(fullPath);
        if(is == null) {
            throw new NullPointerException("Resource File '" + resourceFile +
                    "' (path: '" + fullPath + "') not found!");
        }
        return is;
    }

    // ===================================================== Protected methods

    /**
     * Loads system properties as InputStream.
     *
     * @param configFile local file name
     * @return InputStream
     */
    protected InputStream loadSysPropertiesAsStream(String configFile) {
        return loadSysPropertiesAsStream(getClass(), configFile);
    }

    /**
     * Loads application specific properties as InputStream.
     *
     * @param configFile local file name
     * @return InputStream
     */
    protected InputStream loadAppPropertiesAsStream(String configFile) {
        return loadAppPropertiesAsStream(getClass(), configFile);
    }

    /**
     * Loads system properties as Properties.
     *
     * @param configFile local file name
     * @return Properties
     */
    public static Properties loadSysProperties(String configFile) {
        return loadSysProperties(AbstractPropertyFactory.class, configFile);
    }

    /**
     * Loads system properties as Properties.
     *
     * @param configFile local file name
     * @param clazz      class loader's class
     * @return Properties
     */
    public static Properties loadSysProperties(Class clazz,
                                               String configFile) {
        InputStream is = loadSysPropertiesAsStream(
                clazz,
                configFile);
        Properties res = new Properties();
        try {
            res.load(is);
        } catch (IOException ex) {
            throw new GenericSystemException(ex);
        }

        is = loadAppPropertiesAsStream(
                clazz,
                configFile);
        if (is != null) {
            try {
                res.load(is);
            } catch (IOException ex) {
                throw new GenericSystemException(ex);
            }
        }
        return res;
    }

    /**
     * Loads system properties as Object.
     *
     * @param configFile local file name
     * @param clazz      Class
     * @return Object
     */
    protected Object loadSysPropertiesAsObject(String configFile, Class clazz) {
        InputStream is = loadAppPropertiesAsStream(configFile);
        if (is != null) {
            return XMLHelper.getParsedObject(clazz, is);
            
        } else {            
            return XMLHelper.getParsedObject(clazz, loadSysPropertiesAsStream(
                configFile));
        }
    }

    /**
     * Loads class <code>className</code>.
     *
     * @param className class name
     * @return Class
     */
    protected Class initClass(String className) {

        if(getLogger().isDebugEnabled()) {
            DEBUG("Try to load class: " + className);
        }

        // Loading...
        try {
            return Class.forName(className);
        } catch (Exception ex) {
            ERROR(ex);
            throw new GenericSystemException(
                    "Can't load class '" + className + "'.", ex);
        }
    }

    /**
     * Generates a new InterfaceProperty and set config parameters <code>params</code>.
     *
     * @param className object class name
     * @param params    XML parameters
     * @return Object
     */
    protected InterfaceProperty initObject(String className, Object[] params) {
        return __initObject(className, __toProperties(params));
    }

    /**
     * Generates a new InterfaceProperty and set properties <code>Properties</code>.
     *
     * @param className  object class name
     * @param properties Properties
     * @return Object
     */
    protected InterfaceProperty __initObject(String className,
                                             Properties properties) {

        if(getLogger().isDebugEnabled()) {
            DEBUG("Try to instantiate class: " + className);
            DEBUG("        Properties: " + properties);
        }

        // Instantiating...
        InterfaceProperty ret;
        try {
            ret = (InterfaceProperty) Class.forName(className).newInstance();
        } catch (Exception ex) {
            ERROR(ex);
            throw new GenericSystemException(
                    "Can't get class '" + className + "' instance.", ex);
        }

        // Set properties.
        if(properties != null) {
            ret.setProperties(properties);
        }

        return ret;
    }

    /**
     * Converts XML parameters to java.util.Properties object.
     *
     * @param params XML parameters (must have 'name' and 'value' properties)
     * @return java.util.Properties
     */
    protected Properties __toProperties(Object[] params) {
        if(params == null) {
            return null;
        }

        // Methods to call:
        Method getNameMethod = null;
        Method getValueMethod = null;

        Properties ret = new Properties();
        for(int i = 0; i < params.length; i++) {
            Object param = params[i];
            try {
                if(i == 0) {
                    getNameMethod = param.getClass().getMethod("getName",
                            (Class[]) null);
                    getValueMethod = param.getClass().getMethod("getValue",
                            (Class[]) null);
                }
                ret.put(getNameMethod.invoke(param, (Object[]) null),
                        getValueMethod.invoke(param, (Object[]) null));

            } catch (Exception ex) {
                ERROR(ex);
                throw new GenericSystemException(ex);
            }
        }
        return ret;
    }
}
