/*
** Luxor - XML User Interface Language (XUL) Toolkit
** Copyright (c) 2001, 2002 by Gerald Bauer
**
** This program is free software.
**
** You may redistribute it and/or modify it under the terms of the GNU
** General Public License as published by the Free Software Foundation.
** Version 2 of the license should be included with this distribution in
** the file LICENSE, as well as License.html. If the license is not
** included with this distribution, you may find a copy at the FSF web
** site at 'www.gnu.org' or 'www.fsf.org', or you may write to the
** Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139 USA.
**
** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
** REDISTRIBUTION OF THIS SOFTWARE.
**
*/

package luxor;

import java.awt.Color;
import java.awt.Component;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
import org.jdom.*;
import org.jdom.input.*;
import luxor.core.*;
import luxor.core.data.*;
import luxor.core.input.*;
import luxor.core.loader.*;
import luxor.core.menu.*;
import luxor.core.misc.*;
import luxor.core.portal.*;
import luxor.core.toolbar.*;
import luxor.spi.*;
import luxor.status.*;
import luxor.template.*;
import luxor.util.*;

public class XulManager
{

   public static NullJComponentResolver NULL_JCOMPONENT_RESOLVER = new NullJComponentResolver();
   static Logger T = Logger.getLogger( XulManager.class );

   XulResourceLoader _loader;

   private static XulManager _manager;

   /**
    *  holds luxor.XulAction
    */
   private HashMap _action = new HashMap( 81 );

   /**
    *  holds luxor.AnimDef
    */
   private HashMap _anim = new HashMap( 3 );

   private HashMap _box = new HashMap( 81 );

   /**
    *  holds luxor.spi.BrowserService
    */
   private HashMap _browser = new HashMap( 13 );

   /**
    *  holds luxor.CommandDef
    */
   private HashMap _command = new HashMap( 81 );

   private ArrayList _historyResolver = new ArrayList();

   /**
    *  holds luxor.IconDef
    */
   private HashMap _icon = new HashMap( 81 );

   /**
    *  holds luxor.KeyDef
    */
   private HashMap _key = new HashMap( 81 );
   private HashMap _list = new HashMap( 13 );

   private HashMap _map = new HashMap( 13 );

   /**
    *  holds javax.swing.JMenu
    */
   private HashMap _menu = new HashMap( 13 );

   /**
    *  holds luxor.MenuBarDef
    */
   private HashMap _menubar = new HashMap( 13 );
   private HashMap _popup = new HashMap( 13 );

   /**
    *  holds luxor.PortalDef
    */
   private HashMap _portal = new HashMap( 13 );

   /**
    *  holds luxor.XulPortlet
    */
   private HashMap _portlet = new HashMap( 81 );

   /**
    *  holds luxor.PreDef
    */
   private HashMap _pre = new HashMap( 13 );

   /**
    *  holds luxor.XulServlet
    */
   private HashMap _servlet = new HashMap( 81 );

   /**
    *  holds luxor.ToolBarDef
    */
   private HashMap _toolbar = new HashMap( 13 );

   private XulManager() { }

   public XulResourceLoader setResourceLoader( XulResourceLoader newLoader )
   {
      XulResourceLoader oldLoader = _loader;
      _loader = newLoader;
      return oldLoader;
   }

   public static XulManager getXulManager()
   {
      if( _manager == null )
         _manager = new XulManager();

      return _manager;
   }

   public Map getActions()
   {
      return _action;
   }

   public Map getAnims()
   {
      return _anim;
   }

   public Map getBoxes()
   {
      return _box;
   }

   public Map getCommands()
   {
      return _command;
   }

   /**
    *  convenience method; adds html directory to path
    */
   public String getHTML( Map data, String name )
   {
      // todo: rename to getHtml??
      return getTemplate( data, "html/" + name );
   }

   /**
    *  convenience method
    */
   public String getHTML( String name )
   {
      return getHTML( Collections.EMPTY_MAP, name );
   }

   public Map getIcons()
   {
      return _icon;
   }

   public int getInt( String key, int defaultValue )
   {
      Integer value = ( Integer ) _loader.getConfigData( key );
      if( value == null )
         return defaultValue;
      return value.intValue();
   }

   public Map getKeys()
   {
      return _key;
   }

   public Map getLists()
   {
      return _list;
   }

   public Map getMaps()
   {
      return _map;
   }

   public Map getMenuBars()
   {
      return _menubar;
   }

   public Map getMenus()
   {
      return _menu;
   }

   public Map getPopups()
   {
      return _popup;
   }

   /**
    *  convienience method; doesn't require a Map
    */
   public String getPortal( String id )
   {
      return getPortal( Collections.EMPTY_MAP, id );
   }

   public String getPortal( Map data, String id )
   {
      PortalDef portal = ( PortalDef ) _portal.get( id );
      if( portal == null )
      {
         Xul.error( "portal definition '" + id + "' not found" );
         return "<h1>portal definition " + id + " not found </h1>";
      }

      return portal.getContent( data );
   }

   public Map getPortals()
   {
      return _portal;
   }

   public Map getPortlets()
   {
      return _portlet;
   }

   public String getPre( String key )
   {
      PreDef def = ( PreDef ) _pre.get( key );
      if( def == null )
      {
         Xul.error( "pre definition '" + key + "' not found" );
         return "";
      }
      return def.getPreFormattedText();
   }

   public Map getPres()
   {
      return _pre;
   }

   public ImageIcon getResourceAsImageIcon( String name )
   {
      URL url = _loader.getResourceAsUrl( name );
      if( url == null )
      {
         Xul.error( "icon resource '" + name + "' not found" );
         return null;
      }

      return new ImageIcon( url );
   }

   public Properties getResourceAsProperties( String name ) throws IOException
   {
      InputStream in = _loader.getResourceAsStream( name );
      if( in == null )
      {
         String msg = "resource '" + name + "' not found";
         Xul.error( msg );
         throw new XulResourceNotFoundException( msg );
         // todo: should i return null instead of throwing an exception?
      }

      Properties props = new Properties();
      props.load( in );

      return props;
   }

   public InputStream getResourceAsStream( String name )
   {
      InputStream in = _loader.getResourceAsStream( name );
      if( in == null )
         Xul.error( "resource '" + name + "' not found" );

      return in;
   }

   public String getResourceAsString( String name ) throws IOException
   {
      InputStream in = _loader.getResourceAsStream( name );
      if( in == null )
      {
         String msg = "resource '" + name + "' not found";
         Xul.error( msg );
         throw new XulResourceNotFoundException( msg );
         // todo: should i return null instead of throwing an exception?
      }

      String text = FileUtils.getInputStreamAsString( in );
      return text;
   }

   public URL getResourceAsUrl( String name )
   {
      URL url = _loader.getResourceAsUrl( name );
      if( url == null )
      {
         Xul.error( "resource '" + name + "' not found" );
         return null;
      }
      return url;
   }

   public Document getResourceAsXmlDocument( String name ) throws JDOMException
   {
      InputStream in = _loader.getResourceAsStream( name );
      if( in == null )
      {
         // todo: should i throw a XulResourceNotFound Exception?

         Xul.error( "resource '" + name + "' not found" );
         return null;
      }

      SAXBuilder builder = new SAXBuilder();
      Document doc = builder.build( in );
      return doc;
   }

   public Map getServlets()
   {
      return _servlet;
   }

   public String getString( String key )
   {
      // todo: should i issue a warning if key doesn't exist?
      return ( String ) _loader.getConfigData( key );
   }

   public String getString( String key, String defaultValue )
   {
      String value = getString( key );
      if( value == null )
         value = defaultValue;
      return value;
   }

   public String[] getStringArray( String key )
   {
      // todo: should i rename getStringTable? getStrings?
      // todo: should i issue a waring if key doesn't exist?
      return ( String[] ) _loader.getConfigData( key );
   }

   public String getTemplate( Map data, String name )
   {
      return TemplateManager.getTemplateManager().getTemplate( data, name );
   }

   /**
    *  convenience method; doesn't require a map
    */
   public String getTemplate( String name )
   {
      return getTemplate( Collections.EMPTY_MAP, name );
   }

   public Map getToolBars()
   {
      return _toolbar;
   }


   public void addAction( String key, XulAction value )
   {
      _action.put( key, value );
   }

   public void addAnimDef( AnimDef def )
   {
      _anim.put( def.getId(), def );
   }

   public void addBoxDef( XulBox def )
   {
      _box.put( def.getId(), def );
   }

   public void addBrowser( String key, BrowserService browser )
   {
      _browser.put( key, browser );
   }

   public void addCommandDef( CommandDef def )
   {
      _command.put( def.getId(), def );
   }


   public void addHistoryResolver( HistoryResolver resolver )
   {
      _historyResolver.add( resolver );
   }

   public void addIconDef( IconDef idef )
   {
      _icon.put( idef.getId(), idef );
   }

   public void addKey( KeyDef def )
   {
      _key.put( def.getId(), def );
   }

   public void addListDef( ListDef def )
   {
      _list.put( def.getId(), def );
   }

   public void addMapDef( MapDef def )
   {
      _map.put( def.getId(), def );
   }

   public void addMenu( String key, JMenu menu )
   {
      _menu.put( key, menu );
   }

   public void addMenuBarDef( MenuBarDef mdef )
   {
      _menubar.put( mdef.getId(), mdef );
   }

   public void addPopupDef( PopupDef def )
   {
      _popup.put( def.getId(), def );
   }

   public void addPortalDef( PortalDef def )
   {
      _portal.put( def.getId(), def );
   }


   public void addPortlet( String id, XulPortlet portlet )
   {
      _portlet.put( id, portlet );
   }

   public void addPreDef( PreDef def )
   {
      _pre.put( def.getId(), def );
   }

   public void addServlet( String key, XulServlet value )
   {
      _servlet.put( key, value );
   }

   public void addToolBarDef( ToolBarDef tdef )
   {
      _toolbar.put( tdef.getId(), tdef );
   }

   public JComponent createJComponentFromBox( String key )
   {
      // fix: use XulManager as JComponentResolver

      // todo: should createBox return a JPanel?

      XulBox def = ( XulBox ) _box.get( key );
      if( def == null )
      {
         Xul.error( "box definition '" + key + "' not found" );

         // fix: return special error panel
         // (e.g background is red, etc. to make it stand out)
         JPanel panel = new JPanel();
         panel.setBackground( Color.red );
         return panel;
      }

      return def.createNComponent( NULL_JCOMPONENT_RESOLVER ).getJComponent();
   }


   public String[] createList( String key )
   {
      ListDef def = ( ListDef ) _list.get( key );
      if( def == null )
      {
         Xul.error( "list definition '" + key + "' not found" );
         return null;
      }
      return def.createStringArray();
   }


   public XulMap createMap( String key )
   {
      MapDef def = ( MapDef ) _map.get( key );
      if( def == null )
      {
         Xul.error( "map definition '" + key + "' not found" );
         return null;
      }
      return def.createMap();
   }


   public JMenuBar createMenuBar( String key )
   {
      MenuBarDef def = ( MenuBarDef ) _menubar.get( key );

      if( def == null )
      {
         Xul.error( "menu bar definition '" + key + "' not found" );
         return null;
      }

      return def.createMenuBar();
   }

   public NContainer createNContainerFromBox( String key, JComponentResolver resolver )
   {
      XulBox def = ( XulBox ) _box.get( key );
      if( def == null )
      {
         Xul.error( "box definition '" + key + "' not found" );

         // fix: return special error panel
         // (e.g background is red, etc. to make it stand out)
         return null;
      }

      return def.createNContainer( resolver );
   }


   public JToolBar createToolBar( String key )
   {
      ToolBarDef def = ( ToolBarDef ) _toolbar.get( key );

      if( def == null )
      {
         Xul.error( "toolbar definition '" + key + "' not found" );
         return null;
      }

      return def.createToolBar();
   }

   public void load()
   {
      XulLoader xulLoader = new XulLoader( this );

      Iterator it = _loader.getBootstrapEntries().iterator();
      while( it.hasNext() )
      {
         String name = ( String ) it.next();
         xulLoader.load( name );
      }
   }


   public XulAction lookupAction( String key )
   {
      return ( XulAction ) _action.get( key );
   }


   public ImageIcon[] lookupAnim( String key )
   {
      AnimDef def = ( AnimDef ) _anim.get( key );
      if( def == null )
      {
         Xul.error( "anim definition '" + key + "' not found" );
         return null;
      }
      return def.create();
   }

   public BrowserService lookupBrowser( String key )
   {
      return ( BrowserService ) _browser.get( key );
   }

   public HistoryModel lookupHistory( String key )
   {
      Iterator it = _historyResolver.iterator();

      while( it.hasNext() )
      {
         HistoryResolver resolver = ( HistoryResolver ) it.next();
         HistoryModel model = resolver.lookupHistory( key );
         if( model != null )
            return model;
      }

      return null;
   }

   public ImageIcon lookupIcon( String key )
   {
      IconDef def = ( IconDef ) _icon.get( key );
      if( def == null )
      {
         Xul.error( "icon definition '" + key + "' not found" );
         return null;
      }

      return def.createIcon();
   }

   public String lookupKey( String id )
   {
      KeyDef def = ( KeyDef ) _key.get( id );
      if( def == null )
      {
         Xul.error( "accelerator key definition '" + id + "' not found" );
         return null;
      }
      return def.getJavaString();
   }

   public JMenu lookupMenu( String key )
   {
      return ( JMenu ) _menu.get( key );
   }

   public JPopupMenu lookupPopup( String key )
   {
      PopupDef def = ( PopupDef ) _popup.get( key );
      if( def == null )
      {
         Xul.error( "popup definition '" + key + "' not found" );
         return null;
      }
      return def.createPopupMenu();
   }

   public XulPortlet lookupPortlet( String id )
   {
      XulPortlet portlet = ( XulPortlet ) _portlet.get( id );
      if( portlet == null )
      {
         portlet = new PortletNotFoundPortlet();
         Xul.error( "portlet '" + id + "' not found" );
      }
      return portlet;
   }

   public XulServlet lookupServlet( String key )
   {
      return ( XulServlet ) _servlet.get( key );
   }

   public static class NullJComponentResolver implements JComponentResolver
   {
      public JComponent lookupJComponent( String key )
      {
         return null;
      }
   }
}