/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */

package com.sun.enterprise.tools.upgrade;

import java.util.*;
import java.io.*;
import java.util.logging.*;

import com.sun.enterprise.tools.upgrade.common.*;
import com.sun.enterprise.tools.upgrade.logging.*;
import com.sun.enterprise.cli.framework.*;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.tools.upgrade.cluster.*;

public class UpgradeHarness {
    private static Logger logger = LogService.getLogger(LogService.UPGRADE_LOGGER);
    private StringManager stringManager = StringManager.getManager(LogService.UPGRADE_LOGGER);
    private List moduleList;
    private CommonInfoModel commonInfo;

    public UpgradeHarness() {
        moduleList = new LinkedList();
    }
    public void setCommonInfoModel(CommonInfoModel commonInfo){
        this.commonInfo=commonInfo;
    }
    public void startUpgrade(){
        if(commonInfo.checkSourceInputAsDomainRoot(commonInfo.getSourceInstallDir()))
            commonInfo.setSourceDomainRoot(commonInfo.getSourceInstallDir());
        loadModules();
        System.setProperty("com.sun.aas.configRoot", commonInfo.getTargetInstallDir() + File.separator + "config");
        try {
            //new DomainsProcessor(this.commonInfo,false);
            if(commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS81_EE)){
                ClustersInfoManager.getClusterInfoManager().gatherClusterInfo(this.commonInfo);
            }
            DomainsProcessor dProcessor = new DomainsProcessor(this.commonInfo);
            dProcessor.processTargetDomains();
            // If domains are created then 50% done
            UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(30);
            // This if statement is added for AS9.0 uprade scenario form source AS8.x
            if(commonInfo.getSourceVersion().equals(UpgradeConstants.VERSION_7X)){
                if(!commonInfo.getSourceEdition().equals(UpgradeConstants.EDITION_PE)){
                    // This need to be done only for SE and EE.
                    if(commonInfo.getSourceEdition().equals(UpgradeConstants.EDITION_EE)){
                        // Its EE, process clusters.
                        if(dProcessor.processClusters()){
                            this.upgradeEESource();
                            return;
                        }else{
                            // No clinstance files are provided, so just create stand alone instances.
                            dProcessor.processDomainInstances();
                        }                        
                    }else{
                        // Its SE. need to process only instances, no clusters
                        dProcessor.processDomainInstances();
                    }
                }else{
                    if((commonInfo.getSourceVersion().equals(UpgradeConstants.VERSION_7X)) && (!commonInfo.getTargetEdition().equals(UpgradeConstants.EDITION_PE))){
                        // In this case AS7.x has server1.  Will crete server1 instance in AS8.1 SE/EE
                        dProcessor.processDomainInstances();
                    }
                }
            }else{
                // Now its AS8.x to AS 9.0 upgrade.
                if(commonInfo.getSourceEdition().equals(UpgradeConstants.EDITION_EE)){
                    // Its EE, process clusters.
                    dProcessor.processClusters();                        
                }                
            }            
        }catch(HarnessException he) {
            logger.log(Level.SEVERE,stringManager.getString("enterprise.tools.upgrade.generalException" ,he.getMessage()));
            UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(-1);
            return;
        }
        // If domains are processed then 50% done
        UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(50);
        
        int totalIterations = this.getTotalIterationsFromDomainMapping();
        int currentIteration =0;
        for(java.util.Iterator dItr = commonInfo.getDomainMapping().keySet().iterator(); dItr.hasNext();){
            DomainInfo dInfo = (DomainInfo)commonInfo.getDomainMapping().get(dItr.next());
            commonInfo.setCurrentDomain(dInfo.getDomainName());
            List instanceList = this.getProcessableInstanceList(dInfo);
            logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.currentlyProcessingDomain",dInfo.getDomainName()));
            for(int instIndex = 0; instIndex < instanceList.size(); instIndex++){
                currentIteration++;
                String currentInstanceName = (String)instanceList.get(instIndex);
                commonInfo.setCurrentSourceInstance(currentInstanceName);
                logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.currentlyProcessingInstance",currentInstanceName));
                if(!invokeModules(currentIteration,totalIterations))
                    return;
            }
        }
        logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.finishedUpgrade"));
        //logger.log(Level.CONFIG, Integer.toString(100));
        UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(100);
    }
    private int getTotalIterationsFromDomainMapping(){
        int totalIt = 1;
        for(java.util.Iterator dItr = commonInfo.getDomainMapping().keySet().iterator(); dItr.hasNext();){
            DomainInfo dInfo = (DomainInfo)commonInfo.getDomainMapping().get(dItr.next());
            List instanceList = this.getProcessableInstanceList(dInfo);
            totalIt += instanceList.size();
        }
        return totalIt;
    }
    private boolean invokeModules(int currentIteration, int totalIterations){
        String moduleName = "Default";
        List successfulModuleList = new ArrayList();
        int moduleSize = moduleList.size();
        int nthModule = 0;
        int progress =0 ;
        for(int i=0; i<moduleSize; i++){            
            nthModule++;
            if(!UpdateProgressManager.getProgressManager().canContinueUpgrade()){
                // If any of the module failed to complete, would have updated this status.
                // In that case should not proceed further.  Should exit out of upgrade.
                // This would be a good place for rollback ..... TBD
                logger.log(Level.SEVERE, stringManager.getString("enterprise.tools.upgrade.didNotfinishUpgrade", commonInfo.getTargetDomainRoot()));
                UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(-1);
                return false;
            }
            //logger.log(Level.FINE, "processing module :"+moduleName);
            BaseModule baseModule = (BaseModule)this.moduleList.get(i);
            moduleName = baseModule.getName();
            if(baseModule.upgrade(commonInfo))
                successfulModuleList.add(baseModule);
            else { // Rollback all previous successful operations.
                logger.log(Level.INFO,stringManager.getString("enterprise.tools.upgrade.module_upgrade_failed",moduleName));
                UpdateProgressManager.getProgressManager().setContinueUpgrade(false);
                baseModule.recovery(commonInfo);
                for(int k=0; k<successfulModuleList.size(); k++){
                    BaseModule successModule = (BaseModule)successfulModuleList.get(k);
                    logger.log(Level.INFO,stringManager.getString("enterprise.tools.upgrade.module_rollback",successModule.getName(),commonInfo.getCurrentDomain()));
                    successModule.recovery(commonInfo);
                }
            }
            logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.finishedModule")+moduleName);
            //logger.log(Level.CONFIG, Integer.toString((nthModule/size)*100));
            //progress = 50 + (((currentIteration * 50)/totalIterations)  - ((50-(50*nthModule)/moduleSize)));
            int pFirst = (50 * currentIteration) / totalIterations ;
            int pLast = (50/totalIterations) - (50*nthModule)/(moduleSize*totalIterations);
            progress = 50 + pFirst - pLast;
            UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(progress);
        }
        return true;
    }
    private void upgradeEESource(){        
        // Add 1 for post updateMiscellaneousClusterInfo
        int totalIterations = ClustersInfoManager.getClusterInfoManager().getClusterInfoList().size()+
                                UpgradeUtils.getUpgradeUtils(commonInfo).getStandAloneInstances(commonInfo.getDomainMapping()).size()+1;
        int currentIteration =0;
        // Start with upgrading clustered instances
        for(java.util.Iterator dItr = ClustersInfoManager.getClusterInfoManager().getClusterInfoList().iterator(); dItr.hasNext();){
            ClusterInfo cInfo = (ClusterInfo)dItr.next();
            ClusteredInstance clInstance = cInfo.getMasterInstance();
            if(clInstance == null){
                clInstance = (ClusteredInstance)cInfo.getClusteredInstanceList().get(0);
            }
            commonInfo.setCurrentCluster(cInfo.getClusterName());
            commonInfo.setCurrentDomain(clInstance.getDomain());
            commonInfo.setCurrentSourceInstance(clInstance.getInstanceName());
            currentIteration++;
            logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.currentlyProcessingCLUSTER",cInfo.getClusterName()));
            if(!invokeModules(currentIteration,totalIterations))
                return;            
        }
        commonInfo.setCurrentCluster(null);
        // now upgrade stand alone instances
        for(java.util.Iterator sItr = UpgradeUtils.getUpgradeUtils(commonInfo).getStandAloneInstances(commonInfo.getDomainMapping()).iterator(); sItr.hasNext();){
            Vector instDInfo = (Vector)sItr.next();
            commonInfo.setCurrentDomain(((DomainInfo)instDInfo.elementAt(1)).getDomainName());
            commonInfo.setCurrentSourceInstance((String)instDInfo.elementAt(0));
            currentIteration++;
            logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.currentlyProcessingInstance",(String)instDInfo.elementAt(0)));
            if(!invokeModules(currentIteration,totalIterations))
                return;            
        }
        // Migrate iiop-cluster info saved in clustered info manager.
        this.updateMiscellaneousClusterInfo();
        logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.finishedUpgrade"));
        //logger.log(Level.CONFIG, Integer.toString(100));
        UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(100);
    }
    private void updateMiscellaneousClusterInfo(){
        // Get IIOPClusters from clusterinfo manager and process them
        Hashtable iiopMapping = ClustersInfoManager.getClusterInfoManager().getIIOPClustersMapping();
        if(iiopMapping != null){
            for(Iterator it = iiopMapping.keySet().iterator(); it.hasNext();){
                String domainName = (String)it.next();
                List iCls = (List)iiopMapping.get(domainName);
                String domainXMLfile = this.commonInfo.getDestinationDomainPath(domainName)+File.separator+"config"+File.separator+"domain.xml";
                for(int i=0; i<iCls.size(); i++){
                    IIOPCluster iiopCluster = (IIOPCluster)iCls.get(i);
                    UpgradeUtils.getUpgradeUtils(this.commonInfo).updateDomainXMLWithIIOPCluster(this.commonInfo, domainXMLfile, iiopCluster);                    
                }
            }
        }
        // Get PersistenceStore info from cluster info manager and process them
        Hashtable persistenceStoreMapping = ClustersInfoManager.getClusterInfoManager().getPersistenceStorePropertiesMapping();
        if(persistenceStoreMapping != null){
            for(Iterator pit = persistenceStoreMapping.keySet().iterator(); pit.hasNext();){
                String domainName = (String)pit.next();
                java.util.Properties props = (java.util.Properties)persistenceStoreMapping.get(domainName);
                String domainXMLfile = this.commonInfo.getDestinationDomainPath(domainName)+File.separator+"domain.xml";
                UpgradeUtils.getUpgradeUtils(this.commonInfo).updateDomainXMLWithPersistenceStoreProps(props);
            }
        }
    }
    private List getProcessableInstanceList(DomainInfo dInfo){
        List instanceList = new ArrayList();
        List domainInstanceList = dInfo.getInstanceNames();
        if(commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS80_PE)||
           commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS81_PE) ||
           commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS80_SE) ||
           commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS81_EE) ||
           commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS90_PE)){
            instanceList.add(new String(""));
            return instanceList;
        }
        for(int i=0; i<domainInstanceList.size(); i++){
            String instanceName = (String)domainInstanceList.get(i);
            if(commonInfo.getSourceVersion().equals(UpgradeConstants.VERSION_7X)){
                // For 7x remove admin server from the list
                if(instanceName.equals("admin-server"))
                    continue;
                instanceList.add(instanceName);
                if(commonInfo.getSourceEdition().equals(UpgradeConstants.EDITION_PE)){
                    // IF PE only return the first element  fromt he list
                    if (domainInstanceList.size() > 2) {
                        logger.log(Level.INFO,stringManager.getString("enterprise.tools.upgrade.more_thanone_instance",instanceName));
                    }
                    break;
                }
            }
        }
        if(instanceList.isEmpty())
            logger.log(Level.WARNING, stringManager.getString("enterprise.tools.upgrade.no_server_instance", dInfo.getDomainName()));
        return instanceList;
    }
    private void loadModules(){
        List list = CommonProperties.getRegisteredModules(commonInfo);
        int size = list.size();
        for(int i=0; i<size; i++){
            String moduleClassName = (String)list.get(i);
            try{
                Class cls = Class.forName(moduleClassName);
                BaseModule baseModule = (BaseModule)cls.newInstance();
                moduleList.add(baseModule);
            }catch(ClassNotFoundException e){
                logger.log(Level.SEVERE,stringManager.getString("enterprise.tools.upgrade.load_instantiate_error", e.getMessage()),e);
            }catch(InstantiationException e){
                logger.log(Level.SEVERE,stringManager.getString("enterprise.tools.upgrade.load_instantiate_error", e.getMessage()),e);
            }catch(IllegalAccessException e){
                logger.log(Level.SEVERE,stringManager.getString("enterprise.tools.upgrade.load_instantiate_error", e.getMessage()),e);
            }
        }
    }
}
