// PutListResource.java
// $Id: PutListResource.java,v 1.16 1998/03/04 16:33:25 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.jigsaw.filters;

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

import org.w3c.util.*;
import org.w3c.tools.resources.*;
import org.w3c.tools.resources.ProtocolException;
import org.w3c.cvs.*;
import org.w3c.jigsaw.auth.AuthFilter;
import org.w3c.jigsaw.http.*;
import org.w3c.jigsaw.frames.*;
import org.w3c.www.http.*;
import org.w3c.jigsaw.forms.*;
import org.w3c.jigsaw.resources.*;
import org.w3c.jigsaw.html.*;

public class PutListResource extends FramedResource {

    protected static final boolean debug = true;

    /**
     * Attribute index - The file used to store the modification list.
     */
    protected static int ATTR_FILE = -1;
    /**
     * Attribute index - The user's local space.
     */
    protected static int ATTR_SPACE = -1;
    /**
     * Attribute index - The web server public space.
     */
    protected static int ATTR_ROOT = -1;
    

    static {
	Class     c = null;
	Attribute a = null;

	try {
	    c = Class.forName("org.w3c.jigsaw.filters.PutListResource");
	} catch (Exception ex) {
	    ex.printStackTrace();
	    System.exit(1);
	}
	// Register the file attribute:
	a = new FileAttribute("file"
			      , null
			      , Attribute.EDITABLE|Attribute.MANDATORY);
	ATTR_FILE = AttributeRegistry.registerAttribute(c, a);
	// Register the space attribute:
	a = new FileAttribute("space"
			      , null
			      , Attribute.EDITABLE|Attribute.MANDATORY);
	ATTR_SPACE = AttributeRegistry.registerAttribute(c, a);
	// Register the server root:
	a = new FileAttribute("root"
			      , null
			      , Attribute.EDITABLE| Attribute.MANDATORY);
	ATTR_ROOT = AttributeRegistry.registerAttribute(c, a);
    }

    /**
     * Known entries.
     */
    Hashtable entries = null;

    /**
     * Published
     */
    Hashtable published = null;

    /**
     * Our server context properties.
     */
    ObservableProperties props = null;

    /**
     * Compute the path of the public file for the given local file.
     * This method uses the <em>space</em> and <em>root</em> attributes
     * to translate the path of the given file from the user's local space
     * to the public (server) space.
     * @return A File instance, or <strong>null</strong>.
     */

    protected File getServerFile(File file) {
	String fpath  = file.getAbsolutePath();
	String fspace = getCvsSpace().getAbsolutePath();
	if ( ! fpath.startsWith(fspace) )
	    return null;
	return new File(getRoot(), fpath.substring(fspace.length()));
    }

    /**
     * Get the file to use to store the edited list of files.
     * @return The file.
     */

    public File getFile() {
	return (File) getValue(ATTR_FILE, null);
    }

    public File getPubFile() {
        File file = getFile();
	if (file != null)
  	    return new File(file+".pub");
	else
	  return null;
    }

    /**
     * Get the root directory of the public server to update.
     * @return The root directory of the public server space, supposed to
     * be controled by CVS.
     */

    public File getRoot() {
	return (File) getValue(ATTR_ROOT, null);
    }

    /**
     * Get this user's local CVS space root directory.
     * @return The usre's root of the CVS local space, assumed to be 
     * under CVS control.
     */

    public File getCvsSpace() {
	return (File) getValue(ATTR_SPACE, null);
    }

    /**
     * Dump the current list of edited files back to disk.
     */

    public synchronized void writeList() {
	File file   = getFile();
	File backup = null;
	// Save old version if available:
	if ( file.exists() ) {
	    backup = new File(getFile()+".bak");
	    if ( backup.exists() )
		backup.delete();
	    file.renameTo(backup);
	}
	try {
	    // This also resets the file:
	    DataOutputStream out = (new DataOutputStream
				    (new FileOutputStream(file)));
	    Enumeration enum = entries.elements();
	    // Dump all entries:
	    out.writeInt(entries.size());
	    while (enum.hasMoreElements()) {
		PutedEntry e = (PutedEntry) enum.nextElement();
		e.pickle(out);
	    }
	    out.close();
	} catch (IOException ex) {
	    // FIXME
	    ex.printStackTrace();
	    if ( backup != null )
		backup.renameTo(file);
	}
    }

    public synchronized void writePubList() {
	File file   = getPubFile();
	File backup = null;
	// Save old version if available:
	if ( file.exists() ) {
	    backup = new File(getPubFile()+".bak");
	    if ( backup.exists() )
		backup.delete();
	    file.renameTo(backup);
	}
	try {
	    // This also resets the file:
	    DataOutputStream out = (new DataOutputStream
				    (new FileOutputStream(file)));
	    Enumeration enum = published.elements();
	    // Dump all published:
	    out.writeInt(published.size());
	    while (enum.hasMoreElements()) {
		PutedEntry e = (PutedEntry) enum.nextElement();
		e.pickle(out);
	    }
	    out.close();
	} catch (IOException ex) {
	    // FIXME
	    ex.printStackTrace();
	    if ( backup != null )
		backup.renameTo(file);
	}
    }

    /**
     * Restore the list from the file.
     */

    public synchronized void readList() {
	File file = getFile();
	try {
	    DataInputStream in = (new DataInputStream
				  (new FileInputStream(file)));
	    int size = in.readInt();
	    while (--size >= 0) {
		PutedEntry e = (PutedEntry) AttributeHolder.unpickle(in, null);
		addEntry(e);
	    }
	} catch (IOException ex) {
	    // FIXME
	    ex.printStackTrace();
	}
    }

    /**
     * Restore the published list from the file.
     */

    public synchronized void readPubList() {
	File file = getPubFile();
	try {
	    DataInputStream in = (new DataInputStream
				  (new FileInputStream(file)));
	    int size = in.readInt();
	    while (--size >= 0) {
		PutedEntry e = (PutedEntry) AttributeHolder.unpickle(in, null);
		addPubEntry(e);
	    }
	} catch (IOException ex) {
	    // FIXME
	    ex.printStackTrace();
	}
    }

    public void addEntry(PutedEntry e) {
	entries.put(e.getKey(), e);
    }

    public void addPubEntry(PutedEntry e) {
	published.put(e.getKey(), e);
    }

    public PutedEntry lookupEntry(Request request) {
	ResourceReference rr = request.getTargetResource();
	String            k  = request.getURL().toExternalForm();
	Resource          r  = null;
	if (rr != null) {
	  try {
	    r = rr.lock();
	    if ( r instanceof FileResource )
	      k = ((FileResource) r).getFile().getAbsolutePath().toString();
	  } catch (InvalidResourceException ex) {
	    // continue
	  } finally {
	    rr.unlock();
	  }
	}
	return (PutedEntry) entries.get(k);
    }

    /**
     * Register the given request, which must has a PUT method.
     * @param file The modified file.
     */

    public synchronized void registerRequest(Request request) {
	PutedEntry e = lookupEntry(request);
	if ( e == null ) {
	    e = PutedEntry.makeEntry(request);
	    addEntry(e);
	} else {
	    e.update(request);
	}
    }

    public synchronized void notifyUnload() {
	writeList();
	writePubList();
	super.notifyUnload();
    }

    public void initialize(Object values[]) {
	super.initialize(values);
	// Prepare empty entry list:
	File file = getFile();
	if ((file != null) && file.exists())
	    readList();
	File pub = getPubFile();
	if ((pub != null) && pub.exists())
	    readPubList();
	// Get the server properties:
	this.props = getServer().getProperties();
	try {
	  registerFrameIfNone("org.w3c.jigsaw.filters.PutListFrame",
			      "putlist-frame");
	} catch (Exception ex) {
	  ex.printStackTrace();
	}
    }

    public PutListResource() {
	super();
	this.entries = new Hashtable(11);
	this.published = new Hashtable(11);
    }

}
