// ResourceFrame.java
// $Id: ResourceFrame.java,v 1.7 1998/03/09 15:44:28 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.tools.resources ;

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

import org.w3c.tools.resources.event.*;

/**
 * The resource frame class. A ResourceFrame can be attached to a
 * resource.
 */
public class ResourceFrame extends FramedResource 
                           implements AttributeChangedListener
{

  /**
   * Our FrameEventListener.
   */
  protected transient FrameEventListener frameListener = null;

  /**
   * Our target resource.
   */
  protected FramedResource resource = null;

  static {
    Attribute a   = null ;
    Class     cls = null ;
    // Get a pointer to our own class:
    try {
      cls  = Class.forName("org.w3c.tools.resources.ResourceFrame") ;
    } catch (Exception ex) {
      ex.printStackTrace() ;
      System.exit(1) ;
    }
  }

  /**
   * Get the file part of the URL this resource is attached to.
   * @return An URL object specifying the location in the information 
   *    space of this resource.
   */
  public String getURLPath() {
    return getString(ATTR_URL, getResource().getURLPath()) ;
  }

  /**
   * Get the space entry for that resource. This Object is use to
   * retrieve the resource in the resource space.
   * A ResourceFrame has no SpaceEntry.
   * @return always null.
   */
  protected SpaceEntry getSpaceEntry() {
    return null;
  }

  private ResourceReference self = null;
  /**
   * Get The FrameReference of this frame, or <strong>null</strong>
   * if this frame is not registered.
   * @return A ResourceReference instance.
   */
  public ResourceReference getFrameReference() {
    if ((self == null) &&  (resource != null)) {
      self = resource.getFrameReference(this);
    }
    return self;
  }
  

  /**
   * If our target resource has some children, we could have
   * some attribute to give to them.
   * @param attrs A Hashtable.
   */
  protected void updateDefaultChildAttributes(Hashtable attrs) {
    //nothing here
  }

  /**
   * Check if this kind of request can be perform by this resource.
   * @param request A RequestInterface instance
   * @return a boolean.
   */
  public boolean checkRequest(RequestInterface request) {
    return true;
  }

  /**
   * FIXME doc
   */ 
  public ReplyInterface perform(RequestInterface request) 
    throws ProtocolException, NotAProtocolException
  {
    return super.perform(request);
  }

  /**
   * FIXME doc
   */ 
  public boolean lookup(LookupState ls, LookupResult lr) 
    throws ProtocolException
  {
    //FIXME does a frame can have frames other than filters?
    //exclude filters?
    ResourceFrame frames[] = getFrames();
    if (frames != null) {
      for (int i = 0 ; i < frames.length ; i++) {
	if (frames[i] == null)
	  continue;
	if (frames[i].lookup(ls,lr))
	  return true;
      }
    }
    //
    // FIXME unuseful.
    //
    if ( ls.hasMoreComponents() ) {
      // We are not a container resource, and we don't have children:
      lr.setTarget(null);
      return false;
    } else {
      // We are done !
      //    org.w3c.util.Trace.showTrace("lookup done : "+
      //		       this+", "+resource.getResourceReference());
      lr.setTarget(resource.getResourceReference());
      return true;
    }
  }

  public void processEvent(ResourceEvent evt) {
    if (evt instanceof FrameEvent) {
      fireFrameEvent((FrameEvent)evt);
    }
  }

  /**
   * Add a frame event listener.
   * @param l The new frame event listener.
   */

  public void addFrameEventListener(FrameEventListener l) {
    frameListener = ResourceEventMulticaster.add(frameListener, l);
  }

  /**
   * Remove a frame event listener.
   * @param l The listener to remove.
   */
    
  public void removeFrameEventListener (FrameEventListener l) {
    frameListener = ResourceEventMulticaster.remove(frameListener, l);
  }

  /**
   * Post a frameEvent.
   * @param the frame event type.
   */
  protected void postFrameEvent(int type) {
    if (frameListener != null) {
      FrameEvent evt = new FrameEvent(this, type);
      postEvent(evt);
    }
  }

  /**
   * Fire a frameEvent.
   * @param the frame event type.
   */
  protected void fireFrameEvent(FrameEvent evt) {
    if (frameListener != null) {
      int type = evt.getID();
      switch (type) {
      case Events.FRAME_ADDED :
	frameListener.frameAdded(evt);
	break;
      case Events.FRAME_MODIFIED :
	frameListener.frameModified(evt);
	break;
      case Events.FRAME_REMOVED :
	frameListener.frameRemoved(evt);
	break;
      }
    }
  }

  /**
   * Listen its resource.
   */
  public void attributeChanged(AttributeChangedEvent evt) {
    displayEvent( this, evt );
    setValue(ATTR_LAST_MODIFIED, new Long(System.currentTimeMillis()));
    
  }

  /**
   * This handles the <code>FRAME_MODIFIED</code> kind of events.
   * @param evt The event describing the change.
   */
  
  public void frameModified(FrameEvent evt) {
    displayEvent( this, evt );
    markModified();
    postFrameEvent(evt.getID());
  }

  /**
   * We overide setValue, to fire event.
   * @param idx The index of the attribute to modify.
   * @param value The new attribute value.
   */
  public synchronized void setValue(int idx, Object value) {
    super.setValue(idx,value);
    if (idx != ATTR_LAST_MODIFIED)
      postFrameEvent(Events.FRAME_MODIFIED); 
  }

  /**
   * Get the target resource.
   * @return a resource instance.
   */
  public FramedResource getResource() {
    return resource;
  }

  /**
   * Register a target resource. Called after initialize,
   * set the context. getServer() can be call only after
   * this method call.
   * @parame resource The resource to register.
   */ 
  public void registerResource(FramedResource resource) {
    this.resource = resource;
    postFrameEvent(Events.FRAME_ADDED);
    setValue(ATTR_CONTEXT, resource.getContext());
  }

  /**
   * Register a target resource.
   * @parame resource The resource to register.
   */ 
  public void unregisterResource(Resource resource) {
    //FIXME (can we have more than one resource? )
    this.resource = null;
    postFrameEvent(Events.FRAME_REMOVED);
  }

}
