// MapResource.java
// $Id: MapResource.java,v 1.4 1997/01/24 12:01:04 abaird Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.jigsaw.map ;

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

import w3c.www.http.HTTP ;
import w3c.tools.store.*;
import w3c.jigsaw.resources.* ;
import w3c.jigsaw.http.*;
import w3c.jigsaw.html.*;


/**
 * Implements handling of image map requests.  Understands both the
 * NCSA and the W3C (CERN) format for map description files, with
 * the "nosearch" extension for both.
 *
 * @author <a href="mailto:anto@w3.org">Antonio Ram&iacute;rez</a>
 */
 
public class MapResource extends FileResource {

    // Attribute for internal representation of map data:
    protected static int ATTR_MAP = -1;

    // NCSA/W3C map file format flag
    protected static int ATTR_NCSA = -1;
    
    static {
	Attribute a = null;
	Class cls = null;
	try {
	    cls = Class.forName("w3c.jigsaw.map.MapResource") ;
	} catch (Exception ex) {
	    ex.printStackTrace();
	    System.exit(0);
	}

	a = new MapAttribute("map"
			     , null
			     , Attribute.COMPUTED) ;
	ATTR_MAP = AttributeRegistry.registerAttribute(cls,a);

	a = new BooleanAttribute("useNCSA"
				 , Boolean.FALSE
				 , Attribute.EDITABLE);
	ATTR_NCSA = AttributeRegistry.registerAttribute(cls,a);
    }

    // Hopefully this will make the MapAttribute persistent

    /**
     * This method parses the map file whenever it changes.
     */
    public void updateFileAttributes()
    {

	super.updateFileAttributes();

	Map map = null;

	try {
	    map = parseMap();
	} catch(IOException ex) {
	    map = null;
	} catch(MapException ex) {
	    map = null;
	} finally {
	    setValue(ATTR_MAP, map);
	}
    }

    private boolean getUseNCSA()
    {
	return ((Boolean)
		getValue(ATTR_NCSA,Boolean.FALSE)).booleanValue();
    }

    // Parse the map freshly from file 
    private final Map parseMap()
	throws IOException, MapException
    {
	return new Map(new
		       DataInputStream(new
				       FileInputStream(getFile())),
		       getUseNCSA());
    }
    

    /** 
     * This method will try to get the two coordinates of the imagemap
     * from the request query string, and will the appropriate
     * redirection reply.
     *
     * @param request the HTTP request
     * @return the HTTP reply
     */
    public Reply get(Request request)
	throws HTTPException
    {
	try {
	    checkContent();
	    String url = null ;

	    Map map = (Map) getValue(ATTR_MAP,null) ;
	    if(map == null) {
		map = parseMap() ;
		setValue(ATTR_MAP,map) ;
	    }
	    
	    url = map.getMatchingURL(parseSelection(request.getQueryString()));
	    if(url == null) {
		// FIXME: Status code ?
		Reply error = createDefaultReply(request,HTTP.OK) ;
		error.setStream
		    (generateError
		     ("Malformed query: " +
		      "two numerical coordinates are needed.")) ;
		throw new HTTPException(error) ;
	    } else {
		Reply reply =
		    createDefaultReply(request,HTTP.MOVED_TEMPORARILY) ;
		
		reply.setLocation(new URL(getURL(request), url));
		
		HtmlGenerator g = new HtmlGenerator("Document moved") ;
		g.append("<h1>Document Moved</h1>"+
			 "<p>This document has moved "+
			 "<a href=\"" + url +"\">here</a>.<p>") ;
		reply.setStream(g) ;
		return reply ;
	    }

	} catch(MapException ex) {
	    Reply error =
		createDefaultReply(request,HTTP.INTERNAL_SERVER_ERROR) ;
	    error.setStream(generateError
			    ("Bad map specification: "+ex.getMessage())) ;
	    throw new HTTPException(error) ;
	} catch(Exception ex) {
	    Reply error =
		createDefaultReply(request,HTTP.INTERNAL_SERVER_ERROR) ;
	    error.setStream(generateError
			    ("Problems reading map definition file")) ;
	    throw new HTTPException(error) ;
	}
    }
    
    private HtmlGenerator generateError(String msg)
    {
	HtmlGenerator g = new HtmlGenerator("Image Map Error") ;
	g.append("<h1>Image Map Error</h1>") ;
	if(msg != null)
	    g.append("<p>"+msg+"</p>") ;
	return g ;
    }



    private Point parseSelection(String line)
    {
	if(line == null) return null;

	StringTokenizer strtok = new StringTokenizer(line,",");
	Point selection = null;

	try {
	    selection = new Point();
	    selection.x = Integer.parseInt(strtok.nextToken().trim());
	    selection.y = Integer.parseInt(strtok.nextToken().trim());
	    if(strtok.hasMoreTokens()) selection = null;
	} catch(NumberFormatException ex) {
	    selection = null;
	} catch(NoSuchElementException ex) {
	    selection = null;
	} finally {
	    return selection;
	}
    }
}
