/*
** 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.core;

import java.util.*;
import javax.swing.*;
import org.jaxen.JaxenException;
import org.jaxen.XPathSyntaxException;
import org.jaxen.jdom.XPath;
import org.jdom.*;
import luxor.css.*;
import luxor.css.xul.*;
import luxor.css.xul.data.*;
import luxor.spi.*;
import luxor.status.*;
import luxor.util.*;
import luxor.*;

public class XulNode
{
   static Logger T = Logger.getLogger( XulNode.class );

   private Element _element;

   private String _id;

   /**
    *  cached style properties todo: use Style instead of StyleSheet?
    */
   private StyleSheet _style;
   private String _styleClass;

   public XulNode( Element element )
   {
      _element = element;

      // make sure required attribute id is always present
      _id = getAttribute( Xul.Attribute.ID, "anonymous" );

      _styleClass = getAttribute( Xul.Attribute.CLASS, "anonymous" );

      T.debug( "created " + element.getName() + ": id=" + _id );
   }

   public KeyStroke getAccelKey()
   {
      String accelKey = getAttribute( Xul.Attribute.KEY );
      if( accelKey == null )
         return null;

      String accel = XulManager.getXulManager().lookupKey( accelKey );
      if( accel == null )
         return null;
      else
         return KeyStroke.getKeyStroke( accel );
   }

   public char getAccessKey()
   {
      String accessKey = getAttribute( Xul.Attribute.ACCESSKEY );
      if( accessKey == null || accessKey.length() == 0 )
         return 0;

      return accessKey.charAt( 0 );
   }

   public Action getAction()
   {
      String actionKey = getAttribute( Xul.Attribute.COMMAND );
      if( actionKey == null )
         return null;

      Action action = XulManager.getXulManager().lookupAction( actionKey );
      if( action == null )
         Xul.warning( "action " + actionKey + " not found" );

      return action;
   }

   public int getAlignAttribute( String key, int defaultValue )
   {
      String value = getAttribute( key );

      if( value == null )
         return defaultValue;
      else
      {
         value = value.toLowerCase().trim();

         if( value.equals( "top" ) )
            return Xul.Align.TOP;
         else if( value.equals( "center" ) )
            return Xul.Align.CENTER;
         else if( value.equals( "bottom" ) )
            return Xul.Align.BOTTOM;
         else if( value.equals( "left" ) )
            return Xul.Align.LEFT;
         else if( value.equals( "right" ) )
            return Xul.Align.RIGHT;
         else if( value.equals( "max" ) )
            return Xul.Align.MAX;
         else if( value.equals( "middle" ) )
            return Xul.Align.MIDDLE;
         else
            return defaultValue;
      }
   }

   public String getAttribute( String key, String defaultValue )
   {
      String value = _element.getAttributeValue( key );
      if( value == null )
         return defaultValue;
      else
         return value;
   }

   public String getAttribute( String key )
   {
      return _element.getAttributeValue( key );
   }

   public boolean getBooleanAttribute( String key, boolean defaultValue )
   {
      String value = getAttribute( key );
      if( value == null )
         return defaultValue;
      else
      {
         value = value.toLowerCase().trim();
         if( value.equals( "true" ) || value.equals( "yes" ) || value.equals( "on" ) )
            return true;
         else
            return false;
      }
   }

   public boolean getChecked()
   {
      return getBooleanAttribute( Xul.Attribute.CHECKED, false );
   }

   public int getColSpan()
   {
      return getIntAttribute( Xul.Attribute.COLSPAN, 1 );
   }

   public boolean getCollapsible()
   {
      return getBooleanAttribute( Xul.Attribute.COLLAPSIBLE, false );
   }

   public int getCols( int defaultValue )
   {
      return getIntAttribute( Xul.Attribute.COLS, defaultValue );
   }

   public Element getElement()
   {
      return _element;
   }

   public String getElementName()
   {
      return _element.getName();
   }

   public int getFlex()
   {
      return getIntAttribute( Xul.Attribute.FLEX, 0 );
   }

   public String getHeading( String defaultValue )
   {
      return getAttribute( Xul.Attribute.HEADING, defaultValue );
   }

   public int getHeight()
   {
      StyleSheet style = getStyle();
      CssValue value = style.lookup( Css.Property.HEIGHT );
      if( value == null )
         return -1;

      if( value.getType() == CssValue.INTEGER )
         return ( ( CssInteger ) value ).getIntegerValue();
      else if( value.getType() == CssValue.PIXEL )
         return ( int ) ( ( CssFloat ) value ).getFloatValue();
      else
      {
         Xul.syntax( "unsupported value type for css property height" );
         return -1;
      }
   }

   public HistoryModel getHistory()
   {
      String historyKey = getAttribute( Xul.Attribute.HISTORY );
      if( historyKey == null )
         return null;

      HistoryModel history = XulManager.getXulManager().lookupHistory( historyKey );
      if( history == null )
         Xul.warning( "history " + historyKey + " not found" );

      return history;
   }

   public ImageIcon getIcon()
   {
      String iconKey = getAttribute( Xul.Attribute.ICON );
      if( iconKey == null )
         return null;

      ImageIcon icon = XulManager.getXulManager().lookupIcon( iconKey );
      if( icon == null )
         Xul.warning( "icon " + iconKey + " not found" );

      return icon;
   }

   public String getId()
   {
      return _id;
   }

   public int getIntAttribute( String key, int defaultValue )
   {
      String value = getAttribute( key );
      if( value == null )
         return defaultValue;
      else
      {
         try
         {
            return Integer.parseInt( value );
         }
         catch( NumberFormatException nex )
         {
            Xul.warning( "invalid number format: " + nex.toString() );
            return defaultValue;
         }
      }
   }

   public String getKey()
   {
      return getAttribute( Xul.Attribute.KEY );
   }

   public String getLabel()
   {
      return getAttribute( Xul.Attribute.LABEL );
   }

   public String[] getList()
   {
      String listKey = getAttribute( Xul.Attribute.LIST );
      if( listKey == null )
         return null;

      String list[] = XulManager.getXulManager().createList( listKey );
      if( list == null )
         Xul.warning( "list " + listKey + " not found" );

      return list;
   }

   public XulMap getMap()
   {
      String mapKey = getAttribute( Xul.Attribute.MAP );
      if( mapKey == null )
         return null;

      XulMap map = XulManager.getXulManager().createMap( mapKey );
      if( map == null )
         Xul.warning( "map " + mapKey + " not found" );

      return map;
   }

   public int getMaxHeight()
   {
      StyleSheet style = getStyle();
      CssValue value = style.lookup( Css.Property.MAX_HEIGHT );
      if( value == null )
         return -1;

      if( value.getType() == CssValue.INTEGER )
         return ( ( CssInteger ) value ).getIntegerValue();
      else if( value.getType() == CssValue.PIXEL )
         return ( int ) ( ( CssFloat ) value ).getFloatValue();
      else
      {
         Xul.syntax( "unsupported value type for css property max-height" );
         return -1;
      }
   }

   public int getMaxWidth()
   {
      StyleSheet style = getStyle();
      CssValue value = style.lookup( Css.Property.MAX_WIDTH );
      if( value == null )
         return -1;

      if( value.getType() == CssValue.INTEGER )
         return ( ( CssInteger ) value ).getIntegerValue();
      else if( value.getType() == CssValue.PIXEL )
         return ( int ) ( ( CssFloat ) value ).getFloatValue();
      else
      {
         Xul.syntax( "unsupported value type for css property max-width" );
         return -1;
      }
   }

   public int getMinHeight()
   {
      StyleSheet style = getStyle();
      CssValue value = style.lookup( Css.Property.MIN_HEIGHT );
      if( value == null )
         return -1;

      if( value.getType() == CssValue.INTEGER )
         return ( ( CssInteger ) value ).getIntegerValue();
      else if( value.getType() == CssValue.PIXEL )
         return ( int ) ( ( CssFloat ) value ).getFloatValue();
      else
      {
         Xul.syntax( "unsupported value type for css property min-height" );
         return -1;
      }
   }

   public int getMinWidth()
   {
      StyleSheet style = getStyle();
      CssValue value = style.lookup( Css.Property.MIN_WIDTH );
      if( value == null )
         return -1;

      if( value.getType() == CssValue.INTEGER )
         return ( ( CssInteger ) value ).getIntegerValue();
      else if( value.getType() == CssValue.PIXEL )
         return ( int ) ( ( CssFloat ) value ).getFloatValue();
      else
      {
         Xul.syntax( "unsupported value type for css property min-width" );
         return -1;
      }
   }

   public int getOrient( int defaultValue )
   {
      String orient = getAttribute( Xul.Attribute.ORIENT );
      if( orient == null )
         return defaultValue;

      orient = orient.toLowerCase().trim();
      if( orient.equals( "vertical" )
             || orient.equals( "vert" ) )
         return SwingConstants.VERTICAL;
      else
         return SwingConstants.HORIZONTAL;
   }

   public int getOrient()
   {
      return getOrient( SwingConstants.HORIZONTAL );
   }

   public int getRowSpan()
   {
      return getIntAttribute( Xul.Attribute.ROWSPAN, 1 );
   }

   public int getRows( int defaultValue )
   {
      return getIntAttribute( Xul.Attribute.ROWS, defaultValue );
   }

   public XPath getSelect( String defaultValue )
   {
      // fix: either throw exception or
      //  return prebuild empty xpath

      String expr = getAttribute( Xul.Attribute.SELECT, defaultValue );

      XPath xpath = createXPath( expr );
      if( xpath != null )
         return xpath;

      // use default value as fallback
      if( !expr.equalsIgnoreCase( defaultValue ) )
      {
         Xul.warning( "using default XPath expr '" + defaultValue + "' as a fallback" );
         return createXPath( defaultValue );
      }
      else
         return null;
   }

   public StyleSheet getStyle()
   {
      if( _style == null )
      {
         String id = getId();
         String clazz = getStyleClass();
         String element = getElementName();
         boolean hasId = id.equals( "anonymous" );
         boolean hasClass = id.equals( "anonymous" );

         StyleRegister styleRegister = StyleRegister.getStyleRegister();
         if( hasId && hasClass )
            _style = styleRegister.getStyleSheet( id, clazz, element );
         else if( hasId )
            _style = styleRegister.getStyleSheet( id, element );
         else
            _style = styleRegister.getStyleSheet( element );

         // check for inline style declaration
         String styleDeclaration = _element.getAttributeValue( Xul.Attribute.STYLE );
         if( styleDeclaration != null )
         {
            Map inlineProps = XulCssStyleDeclarationParser.parse( styleDeclaration );
            _style = new StyleSheet( inlineProps, _style );
         }
      }
      return _style;
   }

   /**
    *  note, that I can't use simple getClass as this method is already used in
    *  java.lang.Object
    */
   public String getStyleClass()
   {
      return _styleClass;
   }

   public String getTooltip()
   {
      return getAttribute( Xul.Attribute.TOOLTIP );
   }

   public String getValue()
   {
      return getAttribute( Xul.Attribute.VALUE );
   }

   public int getWidth()
   {
      StyleSheet style = getStyle();
      CssValue value = style.lookup( Css.Property.WIDTH );
      if( value == null )
         return -1;

      if( value.getType() == CssValue.INTEGER )
         return ( ( CssInteger ) value ).getIntegerValue();
      else if( value.getType() == CssValue.PIXEL )
         return ( int ) ( ( CssFloat ) value ).getFloatValue();
      else
      {
         Xul.syntax( "unsupported value type for css property width" );
         return -1;
      }
   }

   private XPath createXPath( String expr )
   {
      try
      {
         return new XPath( expr );
      }
      catch( XPathSyntaxException sex )
      {
         Xul.error( "syntax error in XPath expr '" + expr + "': " + sex.getMultilineMessage() );
      }
      catch( JaxenException jex )
      {
         Xul.error( "failed to create XPath expr '" + expr + "': " + jex.toString() );
      }
      return null;
   }
}