// ServletCommand.java
// $Id: ServletCommand.java,v 1.12 2000/08/16 21:37:48 ylafon Exp $  
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.jigsaw.ssi.servlets;

import java.util.Dictionary;
import java.util.Hashtable;

import java.net.MalformedURLException;
import java.net.URL;

import org.w3c.www.http.HTTP;
import org.w3c.www.http.HttpMessage;

import org.w3c.jigsaw.http.Reply;
import org.w3c.jigsaw.http.Request;
import org.w3c.jigsaw.http.httpd;

import org.w3c.tools.resources.FramedResource;
import org.w3c.tools.resources.InvalidResourceException;
import org.w3c.tools.resources.LookupResult;
import org.w3c.tools.resources.LookupState;
import org.w3c.tools.resources.ProtocolException;
import org.w3c.tools.resources.Resource;
import org.w3c.tools.resources.ResourceException;
import org.w3c.tools.resources.ResourceReference;

import org.w3c.jigsaw.servlet.JigsawHttpServletRequest;
import org.w3c.jigsaw.servlet.JigsawHttpServletResponse;
import org.w3c.jigsaw.servlet.ServletWrapper;

import org.w3c.util.ArrayDictionary;

import org.w3c.jigsaw.ssi.commands.Command;

import org.w3c.jigsaw.ssi.SSIFrame;

import org.w3c.tools.resources.ProtocolException;
import org.w3c.tools.resources.ResourceException;

/**
 * Implementation of the SSI <code>servlet</code> command.
 * Servlet can be executed 
 * simply by providing a url path to a servlet class.
 * @author Benoit Mahe <bmahe@sophia.inria.fr> 
 */
public class ServletCommand implements Command {
    private final static String  NAME  = "servlet";
    private final static boolean debug = true;

    private static final String keys[] = {
	"code",
	"param",
	"value",
	"name"
    };

    protected static Hashtable wrappers = null; // <classname , wrapper>

    static {
	wrappers = new Hashtable(10);
    }

    protected void addParam(Dictionary d, String name, 
			    String param, String value) 
    {
	Hashtable params = (Hashtable)d.get(getClass().getName()+"."+name);
	if (params == null) {
	    params = new Hashtable(5);
	    params.put(param,value);
	} else {
	    Object ovalue = params.get(param);
	    if (ovalue == null) {
		params.put(param,value);
	    } else  if (ovalue instanceof String[]) {
		String oldValues [] = (String[])ovalue;
		String newValues [] = new String[oldValues.length+1];
		System.arraycopy(oldValues,0,newValues,0,oldValues.length);
		newValues[oldValues.length] = value;
		params.put(param,newValues); 
	    } else {
		String newValues [] = new String[2];
		newValues[0] = (String)ovalue;
		newValues[1] = value;
		params.put(param,newValues);
	    }
	}
	d.put(getClass().getName()+"."+name, params);
    }

    protected Hashtable getParams(Dictionary d, String name) {
	return (Hashtable) d.get(getClass().getName()+"."+name);
    }

    public String getName() {
	return NAME;
    }

    public String getValue(Dictionary variables, String var, Request request) {
	return null;
    }

    protected boolean isRemote(String code) {
	try {
	    URL url = new URL(code);
	} catch (MalformedURLException ex) {
	    return false;
	}
	return true;
    }

    /**
     * return true if reply can be cached.
     * @return a boolean.
     */
    public boolean acceptCaching() {
	return false;
    }

    public Reply execute(SSIFrame ssiframe
			 , Request request
			 , ArrayDictionary parameters
			 , Dictionary variables) {
	Object values[] = parameters.getMany(keys);
	String code     = (String) values[0];
	String param    = (String) values[1];
	String value    = (String) values[2];
	String name     = (String) values[3];

	if (name != null) {
	    if ((param != null) && (value != null)) {
		//store a new param for servlet "name"
		addParam(variables,name,param,value);
	    }
	    if (code != null) { // remote or not ??
		ResourceReference r_wrapper = 
		    (ResourceReference)wrappers.get(code);
		if (r_wrapper == null) { //lookup for wrapper
		    httpd server = 
			(httpd) ssiframe.getFileResource().getServer();
		    ResourceReference rr_root = server.getRootReference();
		    try {
			FramedResource root = (FramedResource) rr_root.lock();
			LookupState ls = new LookupState(code);
			LookupResult lr = new LookupResult(rr_root);
			ResourceReference wrap = null;
			if (root.lookup(ls,lr))
			    wrap = lr.getTarget();
			if (wrap != null) {
			    try {
				if (wrap.lock() instanceof ServletWrapper) {
				    wrappers.put(code,wrap);
				    r_wrapper = wrap;
				}
			    } catch (InvalidResourceException ex) { 
				ex.printStackTrace();
				r_wrapper = null;
			    } finally {
				wrap.unlock();
			    }
			}
		    } catch (ProtocolException ex) {
			ex.printStackTrace();
			r_wrapper = null;
		    } catch (InvalidResourceException ex) {
			ex.printStackTrace();
			r_wrapper = null;
		    } finally {
			rr_root.unlock();
		    }
		}
		if (r_wrapper != null) {
		    //initialize the wrapper (params)
		    Hashtable params = getParams(variables, name);
		    request.setState(JigsawHttpServletRequest.STATE_PARAMETERS,
				     params);
		    //perform the request
		    try {
			FramedResource wrapper = 
			    (FramedResource)r_wrapper.lock();
			Request req = (Request)request.getClone();
			req.setState(JigsawHttpServletResponse.INCLUDED, 
				     Boolean.TRUE);
			return (Reply) wrapper.perform(req);
		    } catch (ProtocolException ex) {
			ex.printStackTrace();
			// return default reply
		    } catch (ResourceException ex2) {
			ex2.printStackTrace();
			// return default reply
		    } catch (InvalidResourceException ex3) {
			ex3.printStackTrace();
			// return default reply
		    } finally {
			r_wrapper.unlock();
		    }
		}
	    }
	}
	// We are NOT doing notMod hack here (tricky and useless ?)
	//Reply reply = ssiframe.createCommandReply(request, HTTP.OK);
	Reply reply = request.makeReply(HTTP.OK);
	reply.setContent("");
	return reply;
    }
}
