/* $Id: Glance.java,v 1.24 2003/09/10 18:05:23 em Exp $
 */

import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

import com.hp.hpl.mesa.rdf.jena.common.*;
import com.hp.hpl.mesa.rdf.jena.mem.ModelMem;
import com.hp.hpl.mesa.rdf.jena.model.*;
import com.hp.hpl.mesa.rdf.jena.vocabulary.RDF;
import com.hp.hpl.mesa.rdf.jena.vocabulary.RSS;

import vocabulary.DC;
import vocabulary.GLANCE;

import util.Base64Decoder;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import java.util.Date;
import java.text.SimpleDateFormat;
import java.text.ParseException;

public class Glance extends HttpServlet {
    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");
    PrintWriter out = null;

    public void init(ServletConfig config)
	throws ServletException {
	super.init(config);
    }

    public void doGet(HttpServletRequest request,
                      HttpServletResponse response) 
	throws IOException, ServletException  {

	// get the users session
	HttpSession session = request.getSession();

	// get the model stored in the session
	Model glance = (Model) session.getAttribute("basemodel");

	// get output
        out = response.getWriter();

	// if no model exisits, create one
	if (glance == null) {
	    glance = new ModelMem();
	    
	    // configure base model
	    String configuri = rb.getString("glance.configuri");

	    try {
		readFeed(glance, configuri, null);
	    } catch (Exception e) {
		System.out.println(e);
	    }

	    // save model
	    session.setAttribute("basemodel", glance);
	}

	try {
	    response.setContentType("text/html");

	    String viewuri = rb.getString("glance.viewuri");
	    String abouturi = rb.getString("glance.abouturi");
	    String loginuri = rb.getString("glance.loginuri");

	    String navigation = "<a href=\"" + viewuri + "\">Home</a> | ";
	    navigation += "<a href=\"" + abouturi + "\">About</a> | ";
	    navigation += "<a href=\"" + loginuri + "\">Login</a> ";
	    
	    printHeader(out, navigation, 0);

	    // check for feed in question (could be via http param else
	    // init param)	
	    String feed = request.getParameter("feed");
	    String top = rb.getString("glance.feed");
	    String since = request.getParameter("since");

	    if (feed == null) {
		// no parameter input, perhaps init... load inital value
		// from config file
		feed = top;
	    }
	    
	    readFeed(glance, top, null);
	    out.println("<div id=\"content\">");

	    // print outline
	    out.println("<div id=\"toc\">");

	    // print outline based on base feed
	    Resource base = glance.getResource(top);
	    printDateSearch(since, out);
	    printOutline(glance, base, feed, "feed", null, out);
	    out.println("</div>");
	
	    // print feed(s) in question
	    Resource item = glance.getResource(feed);
	
	    out.println("<div id=\"channel\">");
	    printChannel(glance, item, null, since, out);
	    out.println("</div>");

	    out.println("</div>");

	    // print footer
	    printFooter(out);
	} catch (Exception e) {
	    System.out.println(e);
	}
	out.close();
    }

    public void doPost(HttpServletRequest request,
		       HttpServletResponse response)
	throws IOException, ServletException {
        doGet(request, response);
    }

    public static void readFeed(Model model,
				String uri,
				String auth) throws RDFException {
	if (model == null) return;
	if (uri == null) return;

	// check to see if uri is already loaded for this session
	Resource item = model.getResource(uri);

	if (!(item.hasProperty(GLANCE.connected, GLANCE.True))) {
	    if (auth != null) {
		// authenticate access
		try {
		    URL url = new URL (uri);
		     
		    // Get encoded user and password, comes after "BASIC "
		    String userpassEncoded = auth.substring(6);
		    
		    URLConnection connection = url.openConnection();
		    connection.setRequestProperty("Authorization", "Basic " + userpassEncoded);
		    connection.setRequestProperty("Proxy-Authorization", userpassEncoded);
		     
		    InputStream content = (InputStream) connection.getInputStream();
		     
		    BufferedReader in = new BufferedReader (new InputStreamReader (content));
		     
		    // get a Reader from a BufferedReader...
		    Reader reader = (Reader) in;

		    model.read(reader, uri, null);
		} catch (Exception e) {
		    System.out.println(e);
		}
	    } else {
		// read uri (w/o authentication)
		model.read(uri);
	    }
	    // mark read
	    item.addProperty(GLANCE.connected, GLANCE.True);
	}
    }

    public static void printOutline(Model glance,
				    Resource item,
				    String feed,
				    String qs,
				    String auth,
				    PrintWriter out) throws RDFException {
	ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");
	
	String efeeduri = null;
	String selectedTitle = null;
	String currentTitle = null;
	String docuri = rb.getString("glance.docuri");

       	if (item == null) return;

	// get display title of current selected feed
	if (feed != null) {
	    Resource selected = glance.getResource(feed);
	    if (selected.hasProperty(GLANCE.displayTitle)) {
		selectedTitle = selected.getProperty(GLANCE.displayTitle).getString();
	    }
	}

	// display nav map title
	if (item.hasProperty(GLANCE.displayTitle)) {
	    out.println("\n<ul class=\"grouping\">");
	    if(item.hasProperty(GLANCE.access, GLANCE.TeamAccess)) {
		out.println("<li class=\"team\"><img src=\"" + docuri + "team\" alt=\"Team Feed\" />");
	    } else if (item.hasProperty(GLANCE.access, GLANCE.MemberAccess)) {
		out.println("<li class=\"member\"><img src=\"" + docuri + "group\" alt=\"Member Feed\" />");
	    } else {
		out.println("<li class=\"public\"><img src=\"" + docuri + "world\" alt=\"Public Feed\" />");
	    }

	    efeeduri = URLEncoder.encode(item.toString());

	    if (auth != null) {
		String userpassEncoded = auth.substring(6);
		String userpassDecoded = Base64Decoder.decode(userpassEncoded);
		String user = GlanceAuth.getUsername(userpassDecoded);
		out.print("<a href=\"" + makeUserURI("", user, qs, efeeduri) + "\"");
	    } else {
		out.print("<a href=\"?" + qs + "=" + efeeduri + "\"");
	    }

	    currentTitle = item.getProperty(GLANCE.displayTitle).getString();

	    if (selectedTitle == currentTitle) {
		out.print(" class=\"currentFeed\">"); 
	    } else {
		out.print(">"); 
	    }

	    out.print(currentTitle);
	    out.print("</a>");
	}
	
	// iterator over the items in the Outline
        if (item.hasProperty(RSS.items)) {
	    Seq items = item.getProperty(RSS.items).getSeq();
	    int itemmax = Integer.parseInt(rb.getString("glance.itemmax"));
	    for (int i = 1; ((i <= items.size()) && (i <= itemmax)); i++) {
		String resourceUri = items.getResource(i).toString();
		printOutline(glance, glance.getResource(resourceUri), feed, qs, auth, out);
	    }
        }

	// get member items
        if (item.hasProperty(GLANCE.mitems)) {
	    Seq items = item.getProperty(GLANCE.mitems).getSeq();
	    int itemmax = Integer.parseInt(rb.getString("glance.itemmax"));
	    for (int i = 1; ((i <= items.size()) && (i <= itemmax)); i++) {
		printOutline(glance, items.getResource(i), feed, qs, auth, out);
	    }
        }

	// get team items
        if (item.hasProperty(GLANCE.titems)) {
	    Seq items = item.getProperty(GLANCE.titems).getSeq();
	    int itemmax = Integer.parseInt(rb.getString("glance.itemmax"));
	    for (int i = 1; ((i <= items.size()) && (i <= itemmax)); i++) {
		printOutline(glance, items.getResource(i), feed, qs, auth, out);
	    }
        }

        if (item.hasProperty(GLANCE.displayTitle)) {
            out.println("</li>");
            out.println("</ul>");
	}
    }

    public static void printDateSearch(String since, PrintWriter out) {
	String val = null;
	if(since == null || since.equals("")) {
	    val = "YYYY-MM-DD";
	} else {
	    val = since;
	}

	out.println("<form action=\"\" method=\"get\">");
	out.println("News Since <input name=\"since\" type=\"text\" value=\"" +
		    val + "\" size=\"12\" /><br /><br />");
	out.println("<input type=\"submit\" value=\"Submit\" />");
	out.println("</form>");
	out.println("<form action=\"\" method=\"get\">");
	out.println("<input type=\"hidden\" name=\"since\" value=\"\" />");
	out.println("<input type=\"submit\" value=\"Forget\" />");
	out.println("</form>");
    }

    public static void printChannel(Model glance, 
				    Resource channel,
				    String auth,
				    String since,
				    PrintWriter out) throws RDFException {
	ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");
	String docuri = rb.getString("glance.docuri");
        String uri = null;
        String title = null;
        
        // get the channel URL if it has one
        if (channel.hasProperty(RSS.link)) {
            uri = channel.getProperty(RSS.link).getString();
        }

	out.print("<div class=\"feed\">");

        // open the link tag if there is a uri
	out.print("<h3>");            

        if (uri != null && uri != "") {
            out.print("<a href=\"" + uri + "\">");
        }
            
        // output the title (or display title)
        if (channel.hasProperty(RSS.title)) {
	    title = channel.getProperty(RSS.title).getString();
	}

	if ((title != null) & (title != "")) {
	    out.print(title);
	} else if (channel.hasProperty(GLANCE.displayTitle)) {
            out.print(channel.getProperty(GLANCE.displayTitle).getString());
	}

        // close the link tag if there is a link
        if (uri != null && uri != "") {
            out.print("</a>");
        }

	// provide link to raw news feed
	out.println("&nbsp;&nbsp;<a href=\"");
        if (channel.hasProperty(GLANCE.feed)) {
	    out.print(channel.getProperty(GLANCE.feed).getResource().getURI());
	} else {
	    out.println(channel.getURI());
	}
	out.println("\"><img src=\"http://www.w3.org/RDF/icons/rdf_flyer.24\" alt=\"news feed\" border=\"0\" /></a>");

	// print access level if more restrictive than world
	if (channel.hasProperty(GLANCE.access, GLANCE.TeamAccess)) {
	    out.println("<img src=\"" + docuri + "team\" alt=\"Team Feed\" width=\"20\" height=\"14\" />");
	} else if (channel.hasProperty(GLANCE.access, GLANCE.MemberAccess)) {
	    out.println("<img src=\"" + docuri + "group\" alt=\"Member Feed\" width=\"20\" height=\"14\" />");
	}

	out.print("</h3>");

        // output the description
        if (channel.hasProperty(RSS.description)) {
            out.println("<b><span class=\"description\">");
            out.println(channel.getProperty(RSS.description).getString());
            out.println("</span></b><br />");
        }
                        
        // output the image
        if (channel.hasProperty(RSS.image)) {
            printImage(glance, channel.getProperty(RSS.image).getResource(), out);
        }
                
	// print text input
        if (channel.hasProperty(RSS.textinput)) {
            printTextInput(glance, channel.getProperty(RSS.textinput).getResource(), out);
        }

        // now print the items
        if (channel.hasProperty(RSS.items)) {
	    Seq items = channel.getProperty(RSS.items).getSeq();
	    int itemmax = Integer.parseInt(rb.getString("glance.itemmax"));
	    for (int i = 1; ((i <= items.size()) && (i <= itemmax)); i++) {
		String resourceUri = items.getResource(i).toString();
		printItem(glance, glance.getResource(resourceUri), auth, since, out);
	    }
        }

	// member items
        if (channel.hasProperty(GLANCE.mitems)) {
	    Seq items = channel.getProperty(GLANCE.mitems).getSeq();
	    int itemmax = Integer.parseInt(rb.getString("glance.itemmax"));
	    for (int i = 1; ((i <= items.size()) && (i <= itemmax)); i++) {
		printItem(glance, items.getResource(i), auth, since, out);
	    }
        }

	// team items
        if (channel.hasProperty(GLANCE.titems)) {
	    Seq items = channel.getProperty(GLANCE.titems).getSeq();
	    int itemmax = Integer.parseInt(rb.getString("glance.itemmax"));
	    for (int i = 1; ((i <= items.size()) && (i <= itemmax)); i++) {
		printItem(glance, items.getResource(i), auth, since, out);
	    }
        }

	out.print("<p><a href=\"#toc\" class=\"contents\">[feed list]</a></p>");
	out.print("</div>");
    }
    
    
    public static void printItem(Model glance, 
				 Resource item,
				 String auth,
				 PrintWriter out) throws RDFException {
        if (item == null) return;

	String uri = null;

	if (item.hasProperty(RDF.type, RSS.channel)) {
	    if (item.hasProperty(GLANCE.feed)) {
		// add channel to rss model (if not already in memory)
		readFeed(glance, item.getProperty(GLANCE.feed).getResource().toString(), auth);
		printChannel(glance, item, auth, null, out);
	    } else {
		readFeed(glance, item.toString(), auth);
		printChannel(glance, item, auth, null, out);
	    }
	} 

	if (item.hasProperty(RDF.type, RSS.item)
	    && !item.hasProperty(RDF.type, RSS.channel)) {
	    if (item.hasProperty(RSS.link)) {
		uri = item.getProperty(RSS.link).getString();
	    }
	    
	    out.print("<p class=\"itemtitle\">");
	    
	    if (uri != null && uri != "") {
		out.print("<a href=\"" + uri + "\">");
	    }
	    
	    // output the item title
	    if (item.hasProperty(RSS.title)) {
		out.print(item.getProperty(RSS.title).getString());
	    }
	    
	    // close the link tag if there is a link
	    if (uri != null && uri != "") {
		out.print("</a>");
	    }
	    
	    out.print("</p><p>");
	    
	    if (item.hasProperty(DC.date)) {
		out.print("<span class=\"itemdate\"><b>" + 
			  item.getProperty(DC.date).getString() + 
			  " </b></span>\r\n");
	    }
	    
	    if (item.hasProperty(RSS.description)) {
		out.print("<span class=\"itemdescription\">" + 
			  item.getProperty(RSS.description).getString() + 
			  " </span><br />\r\n");
	    }

	    out.print("</p>");
	}
    }

    public static void printItem(Model glance, 
				 Resource item,
				 String auth,
				 String since,
				 PrintWriter out) throws RDFException {
        if (item == null) return;

	if (since == null) {
	    printItem(glance, item, auth, out);
	    return;
	}

	String uri = null;

	if (item.hasProperty(RDF.type, RSS.channel)) {
	    if (item.hasProperty(GLANCE.feed)) {
		// add channel to rss model (if not already in memory)
		readFeed(glance, item.getProperty(GLANCE.feed).getResource().toString(), auth);
		printChannel(glance, item, auth, since, out);
	    } else {
		readFeed(glance, item.toString(), auth);
		printChannel(glance, item, auth, since, out);
	    }
	} 

	if (item.hasProperty(RDF.type, RSS.item) && item.hasProperty(DC.date)) {
	    if (isMoreNew(since, item.getProperty(DC.date).getString())) {
		if (item.hasProperty(RSS.link)) {
		    uri = item.getProperty(RSS.link).getString();
		}
	    
		out.print("<p class=\"itemtitle\">");
	    
		if (uri != null) {
		    out.print("<a href=\"" + uri + "\">");
		}
	    
		// output the item title
		if (item.hasProperty(RSS.title)) {
		    out.print(item.getProperty(RSS.title).getString());
		}
	    
		// close the link tag if there is a link
		if (uri != null) {
		    out.print("</a>");
		}
	    
		out.print("</p><p>");
	    
		if (item.hasProperty(DC.date)) {
		    out.print("<span class=\"itemdate\"><b>" + 
			      item.getProperty(DC.date).getString() + 
			      " </b></span>\r\n");
		}
	    
		if (item.hasProperty(RSS.description)) {
		    out.print("<span class=\"itemdescription\">" + 
			      item.getProperty(RSS.description).getString() + 
			      " </span><br />\r\n");
		}

		out.print("</p>");
	    }
	}
    }
    
    public static void printImage(Model glance, 
				  Resource image,
				  PrintWriter out) throws RDFException {
        String uri   = null;
        String title = null;
        
        if (image.hasProperty(RSS.link)) {
            uri = image.getProperty(RSS.link).getString();
        }
        
        if (image.hasProperty(RSS.title)) {
            title = image.getProperty(RSS.title).getString();
        }
        
        if (uri != null) {
            out.print("<span class=\"itemlink\"><a href=\"" + uri + "\">");
        }
        
        // write the image tag        
	out.print("<img src=\"" + image.getURI() + "\"");

	if (title != null) {
	    out.print(" alt=\"" + title + "\"");
	}

	out.print(" />");

        if (uri != null) {
            out.print("</a></span>");
        }
    }
    
    public static void printTextInput(Model glance, 
				      Resource textInput,
				      PrintWriter out) throws RDFException {
        String uri = null;
        String title = null;
        String name = null;
        String description = null;
        
        if (textInput.hasProperty(RSS.link)) {
            uri = textInput.getProperty(RSS.link).getString();
        } else {
	    // didn't have enough information, return	    
	    return;
	}

        if (textInput.hasProperty(RSS.title)) {
            title = textInput.getProperty(RSS.title).getString();
        } else {
	    // didn't have enough information, return	    
	    return;
	}

        if (textInput.hasProperty(RSS.name)) {
            name = textInput.getProperty(RSS.name).getString();
        } else {
	    // didn't have enough information, return	    
	    return;
	}

        if (textInput.hasProperty(RSS.description)) {
            description = textInput.getProperty(RSS.description).getString();
        } else {
	    // didn't have enough information, return	    
	    return;
	}
        
        out.println("<span class=\"itemform\"><form method=\"get\" action=\"" + uri + "\">");
        out.println("<p>" + title + " <input name=\"" + name + "\" size=\"20\"></p>");
        out.println("<p>" + description + "</p>");
        out.println("</form></span>");
    }

    public static void printHeader(PrintWriter out,
				   String nav,
				   int custom) {
	ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");

	// get init parameters
        String title = rb.getString("glance.title");
	String viewuri = rb.getString("glance.viewuri");
	String styleuri = rb.getString("glance.styleuri");
	String abouturi = rb.getString("glance.abouturi");
	String docuri = rb.getString("glance.docuri");
	String loginuri = rb.getString("glance.loginuri");
	String reseturi = rb.getString("glance.reseturi");

	out.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
	out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"");
	out.println("\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
        
        out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
        out.println("<head>");
	
        out.println("  <title>" + title + "</title>");
	
	if (styleuri != null) {
	    out.println("  <link href=\"" + styleuri + "\" rel=\"stylesheet\" type=\"text/css\" />");
	}
	
        out.println("</head>");
	
        out.println("<body bgcolor=\"#FFFFFF\">");

	out.println("<div class=\"header\">");
	out.println("<a href=\"http://www.w3.org/\"><img src=\"http://www.w3.org/Icons/WWW/w3c_home\" alt=\"W3C\" border=\"0\" /></a>");

	out.println("<span class=\"headertitle\"> At A Glance</span>");

	out.println("</div>");

	out.println("<div class=\"bar\">");
	
	out.println("<table alt=\"tbar\" width=\"100%\">");
	out.println("<tr><td align=\"left\">");
	out.println("<span class=\"key\">Access Level: <img src=\"" + docuri + "world\" alt=\"Public Feed\" /> Public,  <img src=\"" + docuri + "group\" alt=\"Public Feed\" /> Member, <img src=\"" + docuri + "team\" alt=\"Team Feed\" /> Team</span>");

	out.println("</td><td align=\"right\">");

	out.println("<span class=\"nav\">");
	out.println(nav);
	if(custom == 1) {
	    out.println("| <a href=\"" + reseturi + "?redirect=glance\">Clear Configuration</a>");
	}
	out.println("</span>");

	out.println("</td></tr></table></div>");
    }

    public static void printFooter(PrintWriter out) {
	out.println("<span class=\"footer\">");
	out.println("<address>");

	out.println("<a href=\"http://www.w3.org/RDF/\"><img src=\"http://www.w3.org/rdf/icons/rdf_powered_button.40\" alt=\"rdf\" border=\"0\" /></a>");

	out.println(" Service built using <a href=\"http://www.hpl.hp.com/semweb/\">HP's Jena Toolkit</a><br /> Comments to <a href=\"http://www.w3.org/people/em\">Eric Miller</a>, em@w3.org, <a href=\"http://www.w3.org/People/ryanlee/\">Ryan Lee</a>, ryanlee@w3.org, and the <a href=\"http://lists.w3.org/Archives/Public/www-archive\">www-archive</a> mailing list; ");
	out.println("<a href=\"mailto:ryanlee@w3.org?cc=em@w3.org&cc=www-archive@w3.org&subject=At+A+Glance+Report\">send mail to all three</a>");

	out.println("</address>");

	out.println("</span>");

	out.println("</body></html>");
    }	

    public static String makeUserURI(String uri,
				     String user,
				     String param,
				     String value) {
	String result = uri + "?user=" + user + "&" + param + "=" + value;
	return result;
    }

    public static String makeUserURI(String uri,
				     String user) {
	String result = uri + "?user=" + user;
	return result;
    }

    public static boolean isMoreNew(String since,
				    String then) {
	String datePattern = "yyyy-MM-dd";
	SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern);
	Date sinceDate = null;
	Date thenDate  = null;
	int result;
	try {
	    sinceDate = dateFormat.parse(since);
	} catch(ParseException e) {
	    return true;
	}
        try {
	    thenDate  = dateFormat.parse(then);
	} catch(ParseException e) {
	    return false;
	}
	result = thenDate.compareTo(sinceDate);
	if(result >= 0) {
	    return true;
	} else {
	    return false;
	}
    }
}
