/* JFox, the OpenSource J2EE Application Server
 *
 * Distributable under GNU LGPL license by gun.org
 * more details please visit http://www.huihoo.org/jfox
 */

package org.jfox.framework;

import java.util.ArrayList;
import java.util.List;

import org.jfox.framework.extra.Managable;
import org.jfox.framework.extra.ManagementInfo;
import org.jfox.framework.extra.Singleton;
import org.jfox.framework.logger.Logger;

/**
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public class ComponentMeta implements Comparable {
    private static Logger logger = Logger.getLogger(ComponentMeta.class.getName());

    protected String description = "";

    /**
     * only one instance
     */
    protected boolean singleton = true;

    /**
     * this type component implementation only has one registry, it means that a component can register to Registry only once
     */
    protected boolean typeSingleton = true;

    /**
     * small priority number, high priority
     */
    protected int priority = 0;

    protected ComponentFactory compFactory;

    protected Component component = null;


    /**
     * component names that the component reference
     */
    protected List refComponentNames = new ArrayList();

    /**
     * description which module the component in
     */
    private String module = "Default Module";

    private String moduleDir = null;

    /**
     * return a new ComponentMeta with typeSingleton true, instanceSingleton true
     *
     * @param compFactory
     */
    protected ComponentMeta(ComponentFactory compFactory) {
        this.compFactory = compFactory;
    }

    /**
     * is sington component
     * if register by instance, must be singleton
     * if implements Singlton interface, ignore deploy descriptor, must be singlton
     *
     * @return
     */
    public boolean isSingleton() {
        return Singleton.class.isAssignableFrom(getComponentImplementation()) || singleton;
    }

    public void setSingleton(boolean singleton) {
        this.singleton = singleton;
    }

    public boolean isTypeSingleton() {
        return typeSingleton;
    }

    public void setTypeSingleton(boolean typeSingleton) {
        this.typeSingleton = typeSingleton;
    }

    public int getPriority() {
        return priority;
    }

    public void setPriority(int priority) {
        this.priority = priority;
    }

    /**
     * get component instace by meta
     * attention: will not init & start component by Registry!!!
     *
     * @return
     * @throws ComponentException
     */
    public synchronized Component getComponentInstance() throws ComponentException {
        logger.debug("get component instance " + getComponentFactory().getComponentName());
        if(isSingleton()) {
            if(component == null) {
                logger.debug("make new component instance with compnent name " + getComponentFactory().getComponentName() + " by component factory " + getComponentFactory());
                component = getComponentFactory().makeComponent();
                //set component ctx
                component.setComponentContext(getComponentContext());

                buildRefComponentNames();

                // try to int & start only when created
                if(getRegistry().isStarted()) {
                    getRegistry().initComponent(this, component);
                    getRegistry().startComponent(this, component);
                }
            }
        }
        else {
            logger.debug("make new component instance with compnent name " + getComponentFactory().getComponentName() + " by component factory " + getComponentFactory());
            Component comp = getComponentFactory().makeComponent();
            // only build at the first time
            if(component == null) {
                buildRefComponentNames();
            }
            component = comp;
            //set component ctx
            component.setComponentContext(getComponentContext());

            // try to int & start only when created
            if(getRegistry().isStarted()) {
                getRegistry().initComponent(this, component);
                getRegistry().startComponent(this, component);
            }

        }
        return component;
    }

    // find the depency component ref
    private void buildRefComponentNames() {
        // properties ref
        if(compFactory instanceof PropertyComponentFactory) {
            ParameterPack[] packs = ((PropertyComponentFactory) compFactory).getParameterPacks();
            for(int i = 0; packs != null && i < packs.length; i++) {
                Parameter param = packs[i].getParameter();
                if(param instanceof RefParameter) {
                    refComponentNames.add(((RefParameter) param).getRefComponentName());
                }
            }
        }

        // constructor ref
        Parameter[] params = getComponentFactory().getParameters();
        for(int i = 0; params != null && i < params.length; i++) {
            Parameter param = params[i];
            if(param instanceof RefParameter) {
                refComponentNames.add(((RefParameter) param).getRefComponentName());
            }
        }

    }

    public ComponentName getComponentName() {
        return getComponentFactory().getComponentName();
    }

    public Class getComponentImplementation() {
        return getComponentFactory().getImplementation();
    }

    public ComponentFactory getComponentFactory() {
        return compFactory;
    }

    public ComponentName[] getRefComponentNames() {
        return (ComponentName[]) refComponentNames.toArray(new ComponentName[refComponentNames.size()]);
    }


    /**
     * return the Registry
     * return null if this Component is not registry yet
     *
     * @return
     */
    public Registry getRegistry() {
        return getComponentFactory().getRegistry();
    }

    void setRegistry(Registry registry) {
        getComponentFactory().setRegistry(registry);
    }

    public int compareTo(Object o) {
        if(!(o instanceof ComponentMeta)) return 0;
        return new Integer(priority).compareTo(new Integer(((ComponentMeta) o).priority));
    }

    public ManagementInfo getManagementInfo() {
        if(!isSingleton()) {
            return null;
        }
        else if(component == null) {
            return null;
        }
        else {
            if(component instanceof Managable) {
                return ((Managable) component).getManagementInfo();
            }
            else {
                return null;
            }
        }
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getModule() {
        return module;
    }

    public void setModule(String module) {
        this.module = module;
    }

    public String getModuleDir() {
        return moduleDir;
    }

    public void setModuleDir(String moduleDir) {
        this.moduleDir = moduleDir;
    }

    private ComponentContext getComponentContext(){
        ComponentContext ctx = new ComponentContext();
        if(moduleDir == null) {
            // if register by manual, may not set moduleDir, so use the classpath as
            moduleDir = component.getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
        }
        ctx.setModule(module);
        ctx.setModuleDir(moduleDir);
        ctx.setSingleton(singleton);
        ctx.setTypeSingleton(typeSingleton);
        return ctx;
    }

    public String toString() {
        return "ComponentMeta{" +
                "component=" + (component == null ? "NOT INSTANTIATE" : component.toString()) +
                ", compFactory=" + compFactory.toString() +
                ", singleton=" + singleton +
                ", typeSingleton=" + typeSingleton +
                "}";
    }


}