// Segment.java
// $Id: Segment.java,v 1.13 1998/01/14 10:47:25 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.jigsaw.ssi ;

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

import w3c.util.* ;
import w3c.jigsaw.resources.FileResource ;
import w3c.jigsaw.http.* ;

/**
 * Represents a segment within the parsed content
 * @author Antonio Ramirez <anto@mit.edu>
 * @author Benoit Mahe <bmahe@sophia.inria.fr>
 */

// FIXME: it's getting complex enough to need encapsulation.

class Segment {
  // Unpickling/pickling constants
  protected static final int UNPARSED = 0 ;
  protected static final int COMMAND = 1 ;

  protected boolean control = false;

  protected SSIResource resource = null;

  protected Request request = null;

  public boolean isControl() {
    return control;
  }

  /**
   * Where this segment starts in the input file (inclusive)
   */
  int start = 0 ;

  /**
   * Where it ends (exclusive)
   */
  int end = 0 ;
    
  /** The name of the command that this segment runs, if any  */
  String commandName = null ;

  /** The command that it's linked to, if any. */
  private Command command = null ;

  /** The last registry that this command was executed from, if any */
  private CommandRegistry registry = null ;
      
  /** Its list of parameters */
  ArrayDictionary parameters = null ;


  public Segment(int start, int end)
  {
    this.start = start ;
    this.end = end ;
  }

  public Segment(SSIResource resource,
		 String commandName,
		 ArrayDictionary parameters,
		 int start,
		 int end)
  {
    this.resource    = resource;
    this.commandName = commandName ;
    this.parameters  = parameters ;
    this.start       = start ;
    this.end         = end ;
    
  }

  protected Segment()
  {
    start = end = -1 ;
  }

  /**
   * Get the number of bytes required to save that attribute value.
   * @param The value about to be pickled.
   * @return The number of bytes needed to pickle that value.
   */

  public final int getPickleLength(Object value) {
    CountOutputStream out  = new CountOutputStream();
    DataOutputStream  dout = new DataOutputStream(out);
    try {
      pickle(dout);
      dout.close();
      return out.getCount();
    } catch (IOException ex) {
      throw new RuntimeException("IO erred in CountOutputStream.");
    }
  }


  public void pickle(DataOutputStream out)
    throws IOException
  {
    if(commandName != null) {
      out.writeInt(COMMAND) ;
      out.writeInt(start) ;
      out.writeInt(end) ;
      out.writeUTF(commandName) ;
      out.writeInt(parameters.size()) ;
      for(int i=0;
	  i < parameters.capacity() && parameters.keyAt(i)!=null;
	  i++) {
	out.writeUTF((String)parameters.keyAt(i)) ;
	out.writeUTF((String)parameters.elementAt(i)) ;
      }
    } else {
      out.writeInt(UNPARSED) ;
      out.writeInt(start) ;
      out.writeInt(end) ;
    }
  }

  public static Segment unpickle(DataInputStream in)
    throws IOException
  {
    Segment seg =  new Segment() ;
	
    int type = in.readInt() ;
    seg.start = in.readInt() ;
    seg.end = in.readInt() ;
    if(type == COMMAND) {
      seg.commandName = in.readUTF() ;
      int n = in.readInt() ;
      String parNames[] = new String[n] ;
      String parValues[] = new String[n] ;
      for(int i=0;i<n;i++) {
	parNames[i] = in.readUTF() ;
	parValues[i] = in.readUTF() ;
      }
      seg.parameters = new ArrayDictionary(parNames,parValues) ;
    } else {
      seg.commandName = null ;
      seg.parameters = null ;
    }
    return seg ;
  }

  public final boolean isUnparsed()
  {
    return commandName == null ;
  }

  public final String toString()
  {
    return commandName != null 
      ? "<"+start+','+end+": "+commandName+" "+parameters+'>'
      : "<"+start+','+end+'>' ;
  }

  public int jumpTo() 
    throws ControlCommandException
  {
    if ((! control) || (command == null))
      throw new ControlCommandException("SEGMENT","Internal Error");
    ControlCommand cc = (ControlCommand)command;
    int jump = 0;
    jump = cc.jumpTo(resource,
		     registry,
		     parameters,
		     resource.vars);

    return jump;
  }

  public final Reply get() {
    if (command == null)
      return null;
    return command.execute(resource,
			   request,
			   parameters,
			   resource.vars);
  }
    
  public final Reply init(SSIResource resource,
			  Request request,
			  Dictionary variables,
			  CommandRegistry registry,
			  int position)
  {
    // If we're an unparsed segment, return null;
    if(commandName == null)
      return null ;
	
    this.resource = resource;
    this.request = request;

    // Cache the command for efficiency
    // (Cache the registry just in case the SSIResource registry
    // changes without reparsing, even though maybe it shouldn't happen)
    if(command == null || registry != this.registry) {
      this.registry = registry ;
      this.command  = registry.lookupCommand(commandName) ;
      this.control  = (command instanceof ControlCommand);
    }
    if (control) {
      ControlCommand cc = (ControlCommand)command;
      cc.setPosition(resource, 
		     registry,
		     parameters,
		     variables,
		     position);
    }
    return resource.createDefaultReply(request, w3c.www.http.HTTP.OK);
  }
}


