// RealmsEditor.java
// $Id: RealmsEditor.java,v 1.12 1997/03/07 12:42:11 abaird Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.jigsaw.auth;

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

import w3c.tools.store.*;
import w3c.tools.sorter.*;
import w3c.jigsaw.http.* ;
import w3c.jigsaw.resources.* ;
import w3c.jigsaw.forms.* ;
import w3c.jigsaw.formedit.*;
import w3c.www.http.*;

class UserEditorHandler extends FormCardHandler {
    AuthUser target = null ;
    
    public void notifyChange(FormFieldInterface field, Object value) 
	throws FormFieldException
    {
	target.setValue(field.getName(), value) ;
    }

    public String notifyEndProcessing(FormCardResource card, Request request)
	throws FormProcessingException
    {
	return null;
    }

    UserEditorHandler(AuthUser target) {
	this.target   = target ;
    }

}

class UserEditor extends FormResource {

    UserEditor(ResourceContext context, String url, AuthUser target) {
	super(context, url) ;
	// Define the card for user's attribute edition:
	FormCardResource c = defineCard(new UserEditorHandler(target) 
					, "Attributes"
					, "Attributes of "+target.getName());
	Attribute attrs[] = target.getAttributes() ;
	for (int i = 0 ; i < attrs.length ; i++) {
	    Attribute a = attrs[i] ;
	    if ( ! a.checkFlag(Attribute.EDITABLE) )
		continue ;
	    FormFieldInterface field = FormFieldRegistry.getField(target, a) ;
	    if ( field != null )
		c.addField(field) ;
	}
    }
}

class RealmAddHandler extends FormCardHandler {
    String    url    = null ;
    AuthRealm target = null ;
    
    public void notifyButtonClick(String label) 
	throws FormProcessingException
    {
	target.save() ;
    }

    public String notifyEndProcessing(FormCardResource card, Request request)
	throws FormProcessingException
    {
	// Check input values:
	String name = (String) card.lookupField("name").getValue() ;
	if ( name == null )
	    throw new FormProcessingException("Fill in the user's name.");
	// Create the user:
	AuthUser user = AuthUser.makeUser(name) ;
	target.registerUser(user) ;
	// Relocate to its editor:
	return url+"/"+URLEncoder.encode(name);
    }

    RealmAddHandler (String url, AuthRealm target) {
	this.url    = url ;
	this.target = target ;
    }

}

class RealmListHandler extends FormCardHandler {
    String    url    = null ;
    AuthRealm target = null ;
    Vector    marked = null ;

    /**
     * Update the form card.
     */

    public FormCardResource updateFormCard(FormCardResource card) {
	// Reset the whole card (What a waste of time :-(
	card.reset() ;
	// Rebuild the fields that are now available
	Enumeration enum  = target.enumerateUserNames() ;
	Vector      users = Sorter.sortStringEnumeration(enum) ;
	for (int i = 0 ; i < users.size() ; i++) {
	    String name = (String) users.elementAt(i) ;
	    card.addField(new CheckBoxField(name
					    , name
					    , url+"/"+URLEncoder.encode(name)
					    , false));
	}
	return card ;
    }
    
    /**
     * On notify change, mark the user for deletion.
     */

    public void notifyChange(FormFieldInterface field, Object value) 
	throws FormFieldException
    {
	if ( marked == null )
	    marked = new Vector() ;
	marked.addElement(field.getName()) ;
    }

    /**
     * On notifyEndProcessing, delete all marked users
     */

    public String notifyEndProcessing(FormCardResource card, Request request)
	throws FormProcessingException
    {
	if ( marked == null )
	    return null ;
	// Remove marked users
	int vsize = marked.size() ;
	for (int i = 0 ; i < vsize ; i++) {
	    String   id   = (String) marked.elementAt(i) ;
	    AuthUser user = (AuthUser) target.loadUser(id);
	    if ( user != null )
		user.delete();
	    card.removeField(id) ;
	}
	marked.setSize(0) ;
	return null ;
    }
    
    RealmListHandler(String url, AuthRealm target) {
	this.url    = url ;
	this.target = target ;
    }

}

class RealmEditor extends FormResource implements ResourceLocker {
    AuthRealm target = null ;
    Hashtable loaded = null ;

    /**
     * Resource locker implementation - Some user is going down.
     * @param resource The user being shutdown.
     */

    public synchronized void notifyResourceLockBreak(Resource r) {
	loaded.remove(r.getIdentifier());
    }
    
    /**
     * Define the form cards for the editor.
     */

    protected void defineFormCards() {
	FormFieldInterface field = null ;
	String             url   = getURLPath() ;

	// The card to remove users:
	FormCardHandler list_handler = new RealmListHandler(url, target) ;
	FormCardResource c = defineCard(list_handler, "Users", "Edit users");
	c.setOkLabel("Delete");
	list_handler.updateFormCard(c) ;
	// The card to add user (is the same as the card 
	FormCardHandler add_handler = new RealmAddHandler(url, target);
	c = defineCard(add_handler, "Add user", "Add user");
	c.setOkLabel("Create");
	// The name of the user to create:
	field = new TextField("name", "name", null, null) ;
	c.addField(field) ;
	// The button to save the realm:
	c.addButton("Save") ;
    }

    /**
     * Lookup in the realm for an editor targeted to some given user.
     * @param state The current lookup state.
     * @exception HTTPException If the user wasn't found.
     */

    public boolean lookup(LookupState ls, LookupResult lr) 
	throws HTTPException
    {
	if ( ! ls.hasMoreComponents() )
	    return super.lookup(ls, lr);
	// Look for an already existing editor
	String     name   = ls.peekNextComponent() ;
	UserEditor editor = (UserEditor) loaded.get(name) ;
	if ( editor != null ) {
	    ls.getNextComponent() ;
	    lr.setTarget(editor);
	    return editor.lookup(ls, lr);
	}
	// Install a new one (if user's exist)
	AuthUser user = target.loadUser(name);
	if ( user == null )
	    return super.lookup(ls, lr) ;
	ls.getNextComponent() ;
	editor = new UserEditor(getContext()
				, getURLPath()+"/"+URLEncoder.encode(name)
				, user) ;
	user.lock(this);
	loaded.put(name, editor) ;
	lr.setTarget(editor);
	return editor.lookup(ls, lr);
    }

    RealmEditor(ResourceContext context, String url, AuthRealm target) {
	super(context, url) ;
	this.target = target ;
	this.loaded = new Hashtable() ;
	defineFormCards() ;
    }
}

class RealmsAddHandler extends FormCardHandler {
    httpd         server  = null;
    RealmsCatalog catalog = null ;
    String        url     = null ;

    /**
     * The only button is the save button.
     */

    public void notifyButtonClick(String label) 
	throws FormProcessingException
    {
	catalog.save() ;
    }

    /**
     * Given a realm name and a realm repository, create a new realm.
     */

    public String notifyEndProcessing(FormCardResource card, Request request)
	throws FormProcessingException
    {
	// Get and check input values:
	String name    = (String) card.lookupField("name").getValue() ;
	String repname = (String) card.lookupField("repository").getValue();
	if (name == null) {
	    String msg = "Fill in both the realm name (mandatory), and the "
		+ " repository (were the realm's database should be saved).";
	    throw new FormProcessingException(msg) ;
	}
	// Provide a default rep name:
	if ( repname == null )
	    repname = name + ".db";
	File file = new File(server.getAuthDirectory(), repname);
	if ( file.exists() ) {
	    String msg = "The file \"" + file + "\" already exists. You "
		+ " should either choose an other name for the repository "
		+ " or delete this file.";
	    throw new FormProcessingException(msg);
	}
	// Create the realm :
	AuthRealm realm = AuthRealm.makeRealm(server, name, file) ;
	catalog.registerRealm(realm) ;
	// Relocate to the realm editor.
	return url+"/"+URLEncoder.encode(name);
    }

    RealmsAddHandler (httpd server, String url, RealmsCatalog catalog) {
	this.server  = server;
	this.url     = url ;
	this.catalog = catalog ;
    }

}

class RealmsListHandler extends FormCardHandler {
    RealmsCatalog catalog = null ;
    String        url     = null ;
    Vector        marked  = null ;

    /**
     * Update the catalog list of realms.
     */

    public FormCardResource updateFormCard(FormCardResource card) {
	// FIXME (check for last-modified)
	card.reset() ;
	// Enumerate the available realms:
	Enumeration enum   = catalog.enumerateRealmNames() ;
	Vector      realms = Sorter.sortStringEnumeration(enum) ;
	for (int i = 0 ; i < realms.size() ; i++) {
	    String name = (String) realms.elementAt(i) ;
	    card.addField(new CheckBoxField(name
					    , name
					    , url+"/"+URLEncoder.encode(name)
					    , false)) ;
	}
	return card ;
    }

    /**
     * On notifyChange, mark the appropriate realm.
     */

    public void notifyChange(FormFieldInterface field, Object value)
	throws FormFieldException
    {
	if ( marked == null ) 
	    marked = new Vector() ;
	marked.addElement(field.getName()) ;
    }

    /**
     * On notifyEndProcessing, delete all marked realms.
     * @param card The card wose processing is to be finished.
     * @param request The request that trigered the processing.
     * @exception FormProcessingException If the processing failed.
     */

    public String notifyEndProcessing(FormCardResource card, Request request) 
	throws FormProcessingException
    {
	// No realms marked ?
	if ( marked == null )
	    return null ;
	// Remove all marked realms.
	int vsize = marked.size() ;
	for (int i = 0 ; i < vsize ; i++) {
	    String    id    = (String) marked.elementAt(i);
	    AuthRealm realm = (AuthRealm) catalog.loadRealm(id);
	    if ( realm != null )
		realm.delete();
	    card.removeField(id) ;
	}
	marked.setSize(0) ;
	return null ;
    }

    RealmsListHandler (String url, RealmsCatalog catalog) {
	this.url     = url ;
	this.catalog = catalog ;
    }
}

public class RealmsEditor extends FormResource implements ResourceLocker {
    Hashtable     loaded  = null ;
    RealmsCatalog catalog = null ;

    /**
     * Resource locker implementation - Some realm is going down.
     * @param resource The realm being shutdown.
     */

    public synchronized void notifyResourceLockBreak(Resource r) {
	loaded.remove(r.getIdentifier());
    }

    /**
     * Define the forms card.
     */

    protected void defineFormCards() {
	FormFieldInterface field = null ;
	String             url   = getURLPath() ;
	
	// The card to delete realms:
	FormCardHandler list_handler = new RealmsListHandler(url, catalog);
	FormCardResource c = defineCard(list_handler
					, "Realms"
					, "Edit authentication realms");
	c.setOkLabel("Delete");
	list_handler.updateFormCard(c) ;
	// The card to define new realms:
	FormCardHandler add_handler = new RealmsAddHandler(getServer()
							   , url
							   , catalog);
	c = defineCard(add_handler, "Add realm", "Define a new realm.");
	c.setOkLabel("Create");
	// The name of the realm.
	field = new TextField("name", "name", null, null) ;
	c.addField(field) ;
	// The repository for the realm
	field = new FilenameField("repository"
				  , "repository (not mandatory)"
				  , null, null);
	c.addField(field);
    }

    /**
     * Lookup in the realms editor for a defined realm.
     * @param state The current lookup state.
     * @return A Resource to edit the realm.
     */

    public boolean lookup(LookupState ls, LookupResult lr) 
	throws HTTPException
    {
	if ( ! ls.hasMoreComponents() ) 
	    return super.lookup(ls, lr);
	String      name   = ls.peekNextComponent() ;
	RealmEditor editor = (RealmEditor) loaded.get(name) ;
	// Is an editor available for the realm ?
	if ( editor != null ) {
	    ls.getNextComponent() ;
	    lr.setTarget(editor);
	    return editor.lookup(ls, lr);
	}
	// Install a new editor (if the realm exists) 
	AuthRealm realm = catalog.loadRealm(name) ;
	if (realm == null) 
	    return super.lookup(ls, lr);
	ls.getNextComponent() ;
	editor = new RealmEditor(getContext()
				 , getURLPath()+"/"+URLEncoder.encode(name)
				 , realm);
	realm.lock(this);
	loaded.put(name, editor) ;
	lr.setTarget(editor);
	return editor.lookup(ls, lr);
    }

    /**
     * Initialize the realm editor
     */

    public void initialize(Object values[]) {
	super.initialize(values) ;
	this.catalog = getServer().getRealmsCatalog() ;
	this.loaded  = new Hashtable(11) ;
	defineFormCards() ;
    }

}
