// Segment.java
// $Id: Segment.java,v 1.16 1998/10/15 08:16:11 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.jigsaw.ssi ;

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

import org.w3c.util.* ;
import org.w3c.tools.resources.FileResource ;
import org.w3c.jigsaw.http.* ;
import org.w3c.jigsaw.ssi.commands.*;

/**
 * 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 SSIFrame ssiframe = 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(SSIFrame ssiframe,
		   String commandName,
		   ArrayDictionary parameters,
		   int start,
		   int end)
    {
	this.ssiframe    = ssiframe;
	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(ssiframe,
			 request,
			 registry,
			 parameters,
			 ssiframe.vars);

	return jump;
    }

    public final Reply get() {
	if (command == null)
	    return null;
	return command.execute(ssiframe,
			       request,
			       parameters,
			       ssiframe.vars);
    }
    
    public boolean needsRevalidate() {
	if (command == null)
	    return false;
	else
	    return (! command.acceptCaching());
    }

    public final Reply init(SSIFrame ssiframe,
			    Request request,
			    Dictionary variables,
			    CommandRegistry registry,
			    int position)
    {
	// If we're an unparsed segment, return null;
	if(commandName == null)
	    return null ;
	
	this.ssiframe = ssiframe;
	this.request = request;

	// Cache the command for efficiency
	// (Cache the registry just in case the SSIFrame 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(ssiframe, 
			   request,
			   registry,
			   parameters,
			   variables,
			   position);
	    ssiframe.doNotCacheReply();
	    return null;
	}
	if ((ssiframe.cacheReplies()) && command.acceptCaching())
	    return get();
	else
	    return null;
    }
}
