/*
 *  PHEX - The pure-java Gnutella-servent.
 *  Copyright (C) 2001 - 2004 Gregor Koukkoullis ( phex <at> kouk <dot> de )
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 *  --- CVS Information ---
 *  $Id: GUIRegistry.java,v 1.32 2004/09/26 01:27:10 nxf Exp $
 */
package phex.gui.common;

import java.io.File;
import java.util.HashMap;

import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.xml.bind.JAXBException;

import org.apache.commons.lang.SystemUtils;

import phex.common.Environment;
import phex.common.EnvironmentConstants;
import phex.common.ServiceManager;
import phex.gui.actions.ConnectNetworkAction;
import phex.gui.actions.DisconnectNetworkAction;
import phex.gui.actions.ExitPhexAction;
import phex.gui.actions.FWAction;
import phex.gui.actions.NewDownloadAction;
import phex.gui.actions.SwitchNetworkAction;
import phex.gui.chat.ChatFrameManager;
import phex.update.UpdateManager;
import phex.utils.Logger;
import phex.utils.VersionUtils;
import phex.xml.ObjectFactory;
import phex.xml.XJBGUISettings;
import phex.xml.XJBGUITableList;
import phex.xml.XJBPhex;
import phex.xml.XMLBuilder;

import com.jgoodies.plaf.Options;

public class GUIRegistry implements GUIRegistryConstants
{
    // singleton
    private static GUIRegistry instance = new GUIRegistry();

    /**
     * Contains the global actions of this app together with a retrieval key.
     */
    private HashMap globalActionMap;

    private LazyEventQueue lazyEventQueue;
    private TableUpdateService tableUpdateService;
    private DesktopIndicator desktopIndicator;
    private IconFactory iconFactory;
    private ChatFrameManager chatFrameManager;
    private MainFrame mainFrame;


    private boolean showTableHorizontalLines;
    private boolean showTableVerticalLines;

    private GUIRegistry()
    {
    }

    public static GUIRegistry getInstance()
    {
        return instance;
    }
    
    public void initialize()
    {
        // make sure you never need to keep a reference of XJBGUISettings
        // by a class attributes...
        XJBGUISettings guiSettings = loadGUISettings();
        initializeGUISettings( guiSettings );

        iconFactory = new IconFactory( ICON_RESOURCE_NAME );
        // only systray support on windows...
        if ( Environment.getInstance().isWindowsOS() )
        {
            try
            {
                desktopIndicator = new DesktopIndicator();
            }
            catch(UnsupportedOperationException x)
            {
                desktopIndicator = null;
            }
        }
        
        if ( SystemUtils.IS_OS_MAC_OSX )
        {
            new MacOsxHandler();
        }
        initializeGlobalActions();
        chatFrameManager = new ChatFrameManager();
        try {
            mainFrame = new MainFrame( null, guiSettings );
            Logger.logMessage( Logger.FINE, Logger.GUI, "GUIRegistry initialized." );
        } catch ( java.awt.HeadlessException ex ) 
        {
        }

        GUIUpdateNotificationListener listener = new GUIUpdateNotificationListener();
        UpdateManager.getInstance().triggerAutoBackgroundCheck( listener );
    }

    /**
     * Returns the DesktopIndicator responsible for systray support.
     * Method might return null if no systray is supported.
     */
    public DesktopIndicator getDesktopIndicator()
    {
        return desktopIndicator;
    }

    public MainFrame getMainFrame()
    {
        return mainFrame;
    }

    public IconFactory getIconFactory()
    {
        return iconFactory;
    }

    public LazyEventQueue getLazyEventQueue()
    {
        if ( lazyEventQueue == null )
        {
            lazyEventQueue = new LazyEventQueue();
        }
        return lazyEventQueue;
    }

    public TableUpdateService getTableUpdateService()
    {
        if ( tableUpdateService == null )
        {
            tableUpdateService = new TableUpdateService();
        }
        return tableUpdateService;
    }

    public FWAction getGlobalAction( String actionKey )
    {
        return (FWAction) globalActionMap.get( actionKey );
    }
    
    public String getUsedLAFClass()
    {
        return UIManager.getLookAndFeel().getClass().getName();
    }

    /**
     * Returns true if the tables draw horizontal lines between cells, false if
     * they don't. The default is false for MacOSX and Windows, true for others.
     * @return true if the tables draw horizontal lines between cells, false
     * if they don't.
     */
    public boolean getShowTableHorizontalLines()
    {
        return showTableHorizontalLines;
    }

    /**
     * Sets whether the tables draw horizontal lines between cells. If
     * showHorizontalLines is true it does; if it is false it doesn't.
     * @param showHorizontalLines
     */
    public void setShowTableHorizontalLines( boolean showHorizontalLines )
    {
        showTableHorizontalLines = showHorizontalLines;
    }

    /**
     * Returns true if the tables draw vertical lines between cells, false if
     * they don't. The default is false for MacOSX and Windows, true for others.
     * @return true if the tables draw vertical lines between cells, false
     * if they don't.
     */
    public boolean getShowTableVerticalLines()
    {
        return showTableVerticalLines;
    }

    /**
     * Sets whether the tables draw vertical lines between cells. If
     * showVerticalLines is true it does; if it is false it doesn't.
     * @param showVerticalLines
     */
    public void setShowTableVerticalLines( boolean showVerticalLines )
    {
        showTableVerticalLines = showVerticalLines;
    }


    /**
     * Loads the XJBGUISettings object or null if its not existing or a parsing
     * error occures.
     * @return the XJBGUISettings object.
     */
    private XJBGUISettings loadGUISettings()
    {
        Logger.logMessage( Logger.FINE, Logger.GLOBAL, "Load gui settings file." );

        File file = Environment.getInstance().getPhexConfigFile(
            EnvironmentConstants.XML_GUI_SETTINGS_FILE_NAME );
        XJBPhex phex;
        try
        {
            phex = XMLBuilder.loadXJBPhexFromFile( file );
            if ( phex == null )
            {
                Logger.logMessage( Logger.FINE, Logger.GLOBAL,
                    "No gui settings configuration file found." );
                return null;
            }
            return phex.getGuiSettings();
        }
        catch ( JAXBException exp )
        {
            // TODO bring a GUI message that file cant be created
            Logger.logError( exp );
            return null;
        }
    }

    /**
     * Saves the XJBGUISettings object or null if its not existing or a parsing
     * error occures.
     * @return the XJBGUISettings object.
     */
    public void saveGUISettings()
    {
        Logger.logMessage( Logger.CONFIG, Logger.SEARCH,
            "Saving gui settings..." );

        // JAXB-beta way
        try
        {
            XJBPhex phex = ObjectFactory.createPhexElement();
            phex.setPhexVersion( VersionUtils.getProgramVersion() );

            XJBGUISettings xjbSettings = ObjectFactory.createXJBGUISettings();
            phex.setGuiSettings( xjbSettings );

            XJBGUITableList xjbTableList = ObjectFactory.createXJBGUITableList();
            xjbTableList.setShowHorizontalLines( showTableHorizontalLines );
            xjbTableList.setShowVerticalLines( showTableVerticalLines );
            xjbSettings.setTableList( xjbTableList );
            
            xjbSettings.setLookAndFeelClass( getUsedLAFClass() );

            mainFrame.saveGUISettings( xjbSettings );

            File file = Environment.getInstance().getPhexConfigFile(
                EnvironmentConstants.XML_GUI_SETTINGS_FILE_NAME );
            XMLBuilder.saveToFile( file, phex );
        }
        catch ( JAXBException exp )
        {
            // TODO bring a GUI message that the file cant be created
            Logger.logError( exp );
        }
    }

    /**
     * Initializes global actions that need or can be available before the main
     * frame is initialized.
     */
    private void initializeGlobalActions()
    {
        // required capacity is calculated by
        // Math.ceil( actionCount * 1 / 0.75 )
        // -> actionCount = 10 -> capacity =  14
        globalActionMap = new HashMap( 2 );

        FWAction action = new ExitPhexAction();
        globalActionMap.put( EXIT_PHEX_ACTION, action );
        
        action = new NewDownloadAction();
        globalActionMap.put( NEW_DOWNLOAD_ACTION, action );

        action = new ConnectNetworkAction();
        globalActionMap.put( CONNECT_NETWORK_ACTION, action );

        action = new DisconnectNetworkAction();
        globalActionMap.put( DISCONNECT_NETWORK_ACTION, action );

        action = new SwitchNetworkAction();
        globalActionMap.put( SWITCH_NETWORK_ACTION, action );
    }

    private void initializeGUISettings( XJBGUISettings guiSettings )
    {
        // set default values...
        Environment env = Environment.getInstance();
        if ( env.isMacOSX() || env.isWindowsOS() )
        {
            showTableHorizontalLines = false;
            showTableVerticalLines = false;
        }
        else
        {
            showTableHorizontalLines = true;
            showTableVerticalLines = true;
        }

        // sets old values from old cfg...
        ToolTipManager.sharedInstance().setEnabled(
            ServiceManager.sCfg.mUIDisplayTooltip );
            
        String lafClass;
        // load values from gui new settings if available.
        if ( guiSettings != null )
        {
            XJBGUITableList tableList = guiSettings.getTableList();
            if ( guiSettings.getTableList().isSetShowHorizontalLines() )
            {
                showTableHorizontalLines = tableList.isShowHorizontalLines();
            }
            if ( guiSettings.getTableList().isSetShowVerticalLines() )
            {
                showTableVerticalLines = tableList.isShowVerticalLines();
            }
            lafClass = guiSettings.getLookAndFeelClass();
        }
        else
        {
            lafClass = null;
        }
        
        // if no LAF is set define one...
        if ( lafClass == null )
        {
            if( Environment.getInstance().isMacOSX())
            {
                // set the look and feel to System
                lafClass = UIManager.getSystemLookAndFeelClassName();
            }
            else
            {
                // set the look and feel to Metal
                //lafClass = UIManager.getCrossPlatformLookAndFeelClassName();
                lafClass = Options.PLASTICXP_NAME;
            }
        }
        if ( lafClass.equals( UIManager.getLookAndFeel().getClass().getName()))
        {
            // in case correct laf is already set just update UI!
            // this must be done to get colors correctly initialized!
            GUIUtils.updateComponentsUI();
        }
        else
        {
            try {
                LookAndFeelUtils.setLookAndFeel( lafClass );
                // check if laf switch failed out of any reason..
                if ( !lafClass.equals( UIManager.getLookAndFeel().getClass().getName()) )
                {// update components ui to work at least right with other laf
                    GUIUtils.updateComponentsUI();
                }
            }
            catch ( ExceptionInInitializerError ex ) 
            {
                // headless mode
            }
        }
    }
}
