/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2006 Bull S.A.S.
 * Contact: jonas-team@objectweb.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.objectweb.jonas_ejb.genic;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

import org.objectweb.jonas_ejb.deployment.api.BeanDesc;
import org.objectweb.jonas_ejb.deployment.api.EntityDesc;
import org.objectweb.jonas_ejb.deployment.api.EntityJdbcCmp1Desc;
import org.objectweb.jonas_ejb.deployment.api.EntityJdbcCmp2Desc;
import org.objectweb.jonas_ejb.deployment.api.SessionStatefulDesc;
import org.objectweb.jonas_ejb.deployment.api.SessionStatelessDesc;

import org.objectweb.jonas.common.Log;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

/**
 * This class allows to generate the source of : the class that implements the
 * Enterprise bean's remote interface, the class that implements the Enterprise
 * bean's home interface, the class that implements the Enterprise bean's local
 * interface, the class that implements the Enterprise bean's localhome
 * interface, the class of the Entity Handle in case of entity, or the extended
 * class of the Bean for persistence in case of entity with CMP, via a Velocity
 * template of the given Enterprise Java Bean.
 * @author Helene Joanin : Initial developer
 */
class Source {

    /**
     * home source
     */
    static final int HOME = 0;
    /**
     * local home source
     */
    static final int LOCAL_HOME = 1;
    /**
     * remote source
     */
    static final int REMOTE = 2;
    /**
     * local source
     */
    static final int LOCAL = 3;
    /**
     * entity handle source
     */
    static final int ENTITY_HANDLE = 4;
    /**
     * CMP1 or CMP2 entity bean source
     */
    static final int ENTITY_CMP_JDBC = 5;
    /**
     * interface coherence source for CMP2 entity
     */
    static final int ITF_COH_CMP2_ENTITY = 7;

    /**
     * cluster home source
     */
    static final int CLUSTER_HOME = 8;
    /**
     * service endpoint source
     */
    static final int SERVICE_ENDPOINT = 9;
    /**
     * service endpoint home source
     */
    static final int SERVICE_ENDPOINT_HOME = 10;

    /**
     * cluster remote source
     */
    static final int CLUSTER_REMOTE = 11;

    /**
     * cluster home source for SFSB Fail Over
     */
    static final int CLUSTER_HOME_SFSB = 12;

    /**
     * cluster remote source for SFSB Fail Over
     */
    static final int CLUSTER_REMOTE_SFSB = 13;
    
    /**
     * cluster home source for Entity Fail Over
     */
    static final int CLUSTER_HOME_ENTITY = 14;

    /**
     * cluster remote source for Entity Fail Over
     */
    static final int CLUSTER_REMOTE_ENTITY = 15;

    /**
     * Disabled the clustering for this bean
     */
    static final String CLUSTER_DISABLED = "disabled";

    /**
     * Bean Deployment Description
     */
    private BeanDesc dd = null;

    /**
     * Name of the generated source file
     */
    private String srcFileName = null;

    /**
     * Generated source file type
     */
    private int srcType;

    /**
     * Velocity engine
     */
    private VelocityEngine vEngine = null;

    /**
     * Velocity context
     */
    private VelocityContext vctx = null;

    /**
     * logger
     */
    private Logger logger = null;

    /**
     * Source Constructor
     * @param beanDesc deployment descriptor of the bean
     * @param fileName name of the source file to generate
     * @param type source's type (HOME, LOCAL_HOME, REMOTE, ...)
     * @param ve Velocity engine to use
     * @exception GenICException In error case
     */
    Source(BeanDesc beanDesc, String fileName, int type, VelocityEngine ve) throws GenICException {
        dd = beanDesc;
        srcFileName = fileName;
        srcType = type;
        vctx = VContextFactory.create(dd, srcType);
        vEngine = ve;
        logger = Log.getLogger(Log.JONAS_GENIC_PREFIX);
        // Trace of the Velocity Context
        Logger vLogger = Log.getLogger(Log.JONAS_GENIC_VELOCITY_PREFIX);
        if (vLogger.getCurrentIntLevel() == BasicLevel.DEBUG) {
            vLogger.log(BasicLevel.DEBUG, "Source(..,fileName=" + fileName + ", type = " + type + ", ..)");
            vLogger.log(BasicLevel.DEBUG, "VELOCITY CONTEXT = \n(" + VContextFactory.toString(vctx) + "\n)");
        }
    }

    /**
     * Generates the java source
     * @throws GenICException in error case
     */
    void generate() throws GenICException {

        String tmplName = null;
        Template tmpl = null;
        FileWriter fwriter = null;

        switch (srcType) {
            case CLUSTER_HOME:
                if (dd.getClusterHomeDistributor() != null) {
                    tmplName = dd.getClusterHomeDistributor();
                } else {
                    if (dd.isClusterReplicated()) {
                        tmplName = "ClusterHomeSLSBRepDistributor.vm";
                    } else {
                        tmplName = "ClusterHomeDistributor.vm";
                    }
                }
                break;

            case CLUSTER_REMOTE:
                if (dd.getClusterRemoteDistributor() != null) {
                    tmplName = dd.getClusterRemoteDistributor();
                } else {
                    if (dd.isClusterReplicated()) {
                        tmplName = "ClusterRemoteSLSBRepDistributor.vm";
                    } else {
                        tmplName = "ClusterRemoteSLSBDistributor.vm";
                    }
                }
                break;

            case CLUSTER_HOME_SFSB:

                // If the replication is enabled, use by default the right velocity template
                if (dd.getClusterHomeDistributor() != null) {
                    tmplName = dd.getClusterHomeDistributor();
                } else {
                    if (dd.isClusterReplicated()) {
                        tmplName = "ClusterHomeSFSBRepDistributor.vm";
                    } else {
                        tmplName = "ClusterHomeDistributor.vm";
                    }
                }
                break;

            case CLUSTER_REMOTE_SFSB:
                // if the replication is not enabled, the LB/FO are disabled at the remote interface
                // for the SFSB (sticky session)
                if (dd.isClusterReplicated()) {
                    if (dd.getClusterRemoteDistributor() != null) {
                        tmplName = dd.getClusterRemoteDistributor();
                    } else {
                        tmplName = "ClusterRemoteSFSBRepDistributor.vm";
                    }
                } else {
                    tmplName = "";
                }

                break;

            case CLUSTER_HOME_ENTITY:

                // If the replication is enabled, use by default the right velocity template
                if (dd.getClusterHomeDistributor() != null) {
                    tmplName = dd.getClusterHomeDistributor();
                } else {
                    if (dd.isClusterReplicated()) {
                        tmplName = "ClusterHomeEntityRepDistributor.vm";
                    } else {
                        tmplName = "ClusterHomeDistributor.vm";
                    }
                }
                break;

            case CLUSTER_REMOTE_ENTITY:
                // if the replication is not enabled, the LB/FO are disabled at the remote interface
                // for the SFSB (sticky session)
                if (dd.isClusterReplicated()) {
                    if (dd.getClusterRemoteDistributor() != null) {
                        tmplName = dd.getClusterRemoteDistributor();
                    } else {
                        tmplName = "ClusterRemoteEntityRepDistributor.vm";
                    }
                } else {
                    tmplName = "";
                }

                break;
                
            case HOME:
                if (dd instanceof EntityDesc) {
                    tmplName = "JEntityHome.vm";
                } else if (dd instanceof SessionStatefulDesc) {
                    tmplName = "JStatefulHome.vm";
                } else if (dd instanceof SessionStatelessDesc) {
                    tmplName = "JStatelessHome.vm";
                }
                break;
            case LOCAL_HOME:
                if (dd instanceof EntityDesc) {
                    tmplName = "JEntityLocalHome.vm";
                } else if (dd instanceof SessionStatefulDesc) {
                    tmplName = "JStatefulLocalHome.vm";
                } else if (dd instanceof SessionStatelessDesc) {
                    tmplName = "JStatelessLocalHome.vm";
                }
                break;
            case REMOTE:
                if (dd instanceof EntityDesc) {
                    tmplName = "JEntityRemote.vm";
                } else if (dd instanceof SessionStatefulDesc) {
                    tmplName = "JStatefulRemote.vm";
                } else if (dd instanceof SessionStatelessDesc) {
                    tmplName = "JStatelessRemote.vm";
                }
                break;
            case LOCAL:
                if (dd instanceof EntityDesc) {
                    tmplName = "JEntityLocal.vm";
                } else if (dd instanceof SessionStatefulDesc) {
                    tmplName = "JStatefulLocal.vm";
                } else if (dd instanceof SessionStatelessDesc) {
                    tmplName = "JStatelessLocal.vm";
                }
                break;
            case SERVICE_ENDPOINT:
                if (dd instanceof SessionStatelessDesc) {
                    tmplName = "JServiceEndpoint.vm";
                }
                break;
            case SERVICE_ENDPOINT_HOME:
                if (dd instanceof SessionStatelessDesc) {
                    tmplName = "JServiceEndpointHome.vm";
                }
                break;
            case ENTITY_HANDLE:
                if (dd instanceof EntityDesc) {
                    tmplName = "JEntityHandle.vm";
                }
                break;
            case ENTITY_CMP_JDBC:
                if (dd instanceof EntityJdbcCmp1Desc) {
                    tmplName = "JEntityCmpJdbc.vm";
                }
                if (dd instanceof EntityJdbcCmp2Desc) {
                    tmplName = "JEntityCmp2.vm";
                }
                break;
            case ITF_COH_CMP2_ENTITY:
                if (dd instanceof EntityJdbcCmp2Desc) {
                    tmplName = "JEntityCmp2CoherenceItf.vm";
                }
                break;
            default:
                break;
        }
        if (tmplName == null) {
            throw new GenICException("No template for  '" + srcFileName + " !!!'");
        }

        try {
            tmpl = vEngine.getTemplate(tmplName);
        } catch (Exception e) {
            throw new GenICException("Cannot get the '" + tmplName + "' template file", e);
        }

        try {
            // Create before the parent directory
            File fs = new File(srcFileName);
            File fdir = fs.getParentFile();
            if (fdir != null) {
                if (!fdir.exists()) {
                    if (!fdir.mkdirs()) {
                        throw new IOException("Cannot create the directory '" + fdir.getPath() + "'");
                    }
                }
            }
            fwriter = new FileWriter(srcFileName);
        } catch (IOException e) {
            e.printStackTrace();
            throw new GenICException("Cannot create the '" + srcFileName + "' source file", e);
        }
        logger.log(BasicLevel.DEBUG, "Generate the file " + srcFileName);
        try {
            tmpl.merge(vctx, fwriter);
        } catch (Exception e) {
            throw new GenICException("Cannot generate the '" + srcFileName + "' source from the '" + tmplName
                    + "' template", e);
        }

        try {
            fwriter.flush();
            fwriter.close();
        } catch (IOException e) {
            throw new GenICException("Cannot close the '" + srcFileName + "' source file", e);
        }
    }

}