/*
 * ApplicationManager.java  $Revision: 1.8 $  $Date: 2007/10/03 04:39:39 $
 *
 * Copyright (C) 2007 Meldcraft Corporation
 *
 * This file is part of Scaffold.
 * 
 * Scaffold 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 3 of the License, or
 * (at your option) any later version.
 * 
 * Scaffold 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 Scaffold.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.meldcraft.application.container;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;

import com.meldcraft.application.Application;
import com.meldcraft.application.ApplicationClosingNotAllowedException;
import com.meldcraft.application.ApplicationConstants;
import com.meldcraft.application.ApplicationContextEvent;
import com.meldcraft.application.ApplicationContextListener;
import com.meldcraft.util.ConfigurationManager;
import com.meldcraft.util.PropertiesHelper;
import com.meldcraft.util.ReflectUtil;
import com.meldcraft.util.logging.LogUtil;

/**
 * @version $Revision: 1.8 $ $Date: 2007/10/03 04:39:39 $
 * @author Rob Richards
 */
public class ApplicationManager
{
// Static variables
    public static final String  LOOK_N_FEEL_KEY = "Application.Container.LNF";

    private static final HashMap                        sAppManagers = new HashMap();
    private static final ApplicationContextListener     sExitListener = new ExitListener();
    private static ApplicationVMManager                 sVMManager;

// Member variables
// Constructors and initialization
// Interface implemenations
// Public methods
    public static void openApp(String appName, String[] args)
        throws Exception
    {
        ApplicationInstanceManager mgr = null;
        synchronized(sAppManagers)
        {
            mgr = (ApplicationInstanceManager)sAppManagers.get(appName);
            if (mgr == null)
            {
                mgr = createApplicationInstanceManager(appName);
                sAppManagers.put(appName, mgr);
            }
        }

        if (sVMManager.openRemoteApplicationInstance(appName, args))
        {
            // exit if this was the only app
            sExitListener.applicationContextChanged(new ApplicationContextEvent(null, Application.APPLICATION_CLOSED_KEY, null));
        }
        else
        {
            Application app = mgr.openApplicationInstance(appName, args);
            app.getApplicationContext().addApplicationContextListener(sExitListener);
        }
    }

    public static void closeAll() throws ApplicationClosingNotAllowedException
    {
        ApplicationInstanceManager[] mgrs = null;
        synchronized(sAppManagers)
        {
            mgrs = (ApplicationInstanceManager[])sAppManagers.values().
                                    toArray(new ApplicationInstanceManager[0]);
        }

        ApplicationClosingNotAllowedException excep = null;
        for (int i=0 ; i < mgrs.length ; i++)
        {
            Application[] apps = mgrs[i].getAllApplicationInstances();
            if (apps != null)
            {
                for (int j=0 ; j < apps.length ; j++)
                {
                    try
                    {
                        apps[j].applicationClose();
                    }
                    catch (ApplicationClosingNotAllowedException e)
                    {
                        if (excep != null)
                        {
                            excep = e;
                        }
                    }
                }
            }
        }

        if (excep != null)
        {
            throw excep;
        }
    }

    public static void main(String[] args)
    {
        if (args.length < 1)
        {
            String usage =
                "Usage: java " + ApplicationManager.class.getName() + "{<appClassname>|<appResource>} [options]";
            System.err.println(usage);
            System.exit(1);
        }

        String className = args[0];
        String vmName = args[0];
        try
        {
            Map config = ConfigurationManager.getConfiguration(args[0]);
            configureVM(config);
            vmName = PropertiesHelper.getString(config, ApplicationConstants.APP_VM_NAME, vmName);
        }
        catch (Exception e)
        {
            // ignored
        }

        sVMManager = new ApplicationVMManager(vmName);

        String[] appArgs = null;
        if (args.length > 1)
        {
            appArgs = new String[args.length-1];
            for (int i=1 ; i < args.length ; i++)
            {
                appArgs[i-1] = args[i];
            }
        }

        try
        {
            openApp(className, appArgs);
        }
        catch (Exception e)
        {
            System.err.println("Error starting application " + className + ":");
            e.printStackTrace(System.err);
            System.exit(2);
        }

        //System.exit(0);
        while(true)
        {
            try{Thread.sleep(10000);} catch(Exception e) {}
        }
    }

// Protected methods
// Private methods
    private static void configureVM(Map config)
    {
        String lnf = PropertiesHelper.getString(config, LOOK_N_FEEL_KEY, null);
        boolean list = false;
        if ((lnf == null) || lnf.equals("list"))
        {
            list = (lnf != null) && lnf.equals("list");
            Map cfg = ConfigurationManager.getConfiguration(ApplicationManager.class);
            lnf = PropertiesHelper.getString(cfg, LOOK_N_FEEL_KEY,
                                             UIManager.getSystemLookAndFeelClassName());
        }

        if (list || (lnf.indexOf('.') == -1))
        {
            LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();
            if (infos != null)
            {
                for (int i=0 ; i < infos.length ; i++)
                {
                    if (list)
                    {
                        String msg = infos[i].getName() + ": " + infos[i].getClassName();
                        if (infos[i].getClassName().equals(UIManager.getSystemLookAndFeelClassName()))
                        {
                            msg = "* "  + msg;
                        }
                        System.out.println(msg);
                    }
                    else if (lnf.toLowerCase().equals(infos[i].getName().toLowerCase()))
                    {
                        lnf = infos[i].getClassName();
                        break;
                    }
                }
            }
        }
        
        try
        {
            UIManager.setLookAndFeel(lnf);
        }
        catch (Exception e)
        {
            Logger logger = LogUtil.getLogger(ApplicationManager.class);
            logger.log(Level.WARNING, "Could not install requested LNF: "+lnf, e);
            try
            {
                lnf = UIManager.getSystemLookAndFeelClassName();
                UIManager.setLookAndFeel(lnf);
            }
            catch (Exception e2)
            {
                logger.log(Level.WARNING, "Could not install requested LNF: "+lnf, e2);
            }
        }
    }

    private static ApplicationInstanceManager createApplicationInstanceManager(String appName)
    {
        ApplicationInstanceManager mgr = null;
        try
        {
            Map config = ConfigurationManager.getConfiguration(appName);
            String cname = PropertiesHelper.getString(config, ApplicationConstants.APP_INSTANCE_MANAGER, null);
            if (cname != null)
            {
                mgr = (ApplicationInstanceManager)ReflectUtil.createInstance(cname);
            }
        }
        catch (Throwable e)
        {
            LogUtil.getLogger(ApplicationManager.class).log(Level.WARNING, "Could not instantiate specified application manager.  Using default: " + DefaultApplicationInstanceManager.class.getName(), e);
        }

        if (mgr == null)
        {
            mgr = new DefaultApplicationInstanceManager();
        }
        return mgr;
    }

// Inner classes
    public static class ExitListener implements ApplicationContextListener
    {
        public void applicationContextChanged(ApplicationContextEvent e)
        {
            if (e.getKey().equals(Application.APPLICATION_CLOSED_KEY))
            {
                ApplicationInstanceManager[] mgrs = null;
                synchronized(sAppManagers)
                {
                    mgrs = (ApplicationInstanceManager[])sAppManagers.values().
                                    toArray(new ApplicationInstanceManager[0]);
                }

                boolean haveApps = false;
                for (int i=0 ; i < mgrs.length ; i++)
                {
                    Application[] apps = mgrs[i].getAllApplicationInstances();
                    if ((apps != null) && (apps.length > 0))
                    {
                        haveApps = true;
                        break;
                    }
                }
                if (!haveApps)
                {
                    LogUtil.getLogger(ApplicationManager.class).info("Exiting - no more applications.");
                    System.exit(0);
                }
            }
        }
    };
}
