/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.venice.servlets;

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.log4j.*;
import com.silverwrist.util.StringUtil;
import com.silverwrist.venice.core.*;
import com.silverwrist.venice.except.*;
import com.silverwrist.venice.servlets.format.*;

public class ConfDisplay extends VeniceServlet
{
  /*--------------------------------------------------------------------------------
   * Internal class used to get post number defaults
   *--------------------------------------------------------------------------------
   */

  static class PostInterval
  {
    private int first;
    private int last;

    public PostInterval(int f, int l)
    {
      if (f<=l)
      { // the sort is good
	first = f;
	last = l;

      } // end if
      else
      { // reverse the order
	first = l;
	last = f;

      } // end else

    } // end constructor

    public int getFirst()
    {
      return first;

    } // end getFirst

    public int getLast()
    {
      return last;

    } // end getLast

  } // end class PostInterval

  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static Category logger = Category.getInstance(ConfDisplay.class);

  /*--------------------------------------------------------------------------------
   * Internal functions
   *--------------------------------------------------------------------------------
   */

  private static void getViewSortDefaults(ServletRequest request, int confid, TopicSortHolder tsc,
					  String on_error) throws ErrorBox
  {
    String str = request.getParameter("view");
    if (!(StringUtil.isStringEmpty(str)))
    { // we need to change the view parameter
      try
      { // convert the parameter to an integer and then check it against defined values
	int p = Integer.parseInt(str);
	switch (p)
	{
	  case ConferenceContext.DISPLAY_NEW:
	  case ConferenceContext.DISPLAY_ACTIVE:
	  case ConferenceContext.DISPLAY_ALL:
	  case ConferenceContext.DISPLAY_HIDDEN:
	  case ConferenceContext.DISPLAY_ARCHIVED:
	    tsc.setViewOption(confid,p);
	    break;

	  default:
	    throw new ErrorBox(null,"Invalid view parameter.",on_error);

	} // end switch

      } // end try
      catch (NumberFormatException nfe)
      { // failure in parseInt
	logger.error("Cannot convert view parameter '" + str + "'!");
	throw new ErrorBox(null,"Invalid view parameter.",on_error);

      } // end catch

    } // end if

    str = request.getParameter("sort");
    if (!(StringUtil.isStringEmpty(str)))
    { // we need to change the sort parameter
      try
      { // convert the parameter to an integer and then check it against defined values
	int p = Integer.parseInt(str);
	int real_p = ((p<0) ? -p : p);
	switch (real_p)
	{
	  case ConferenceContext.SORT_NUMBER:
	  case ConferenceContext.SORT_NAME:
	  case ConferenceContext.SORT_UNREAD:
	  case ConferenceContext.SORT_TOTAL:
	  case ConferenceContext.SORT_DATE:
	    tsc.setSortOption(confid,p);
	    break;

	  default:
	    throw new ErrorBox(null,"Invalid sort parameter.",on_error);
	    
	} // end switch

      } // end try
      catch (NumberFormatException nfe)
      { // failure in parseInt
	logger.error("Cannot convert sort parameter '" + str + "'!");
	throw new ErrorBox(null,"Invalid sort parameter.",on_error);

      } // end catch

    } // end if

  } // end getViewSortDefaults

  private static PostInterval getInterval(VeniceEngine engine, ServletRequest request, TopicContext topic,
					  String on_error) throws ErrorBox
  {
    int first, last;

    String foo = request.getParameter("pxg");
    if (!(StringUtil.isStringEmpty(foo)))
    { // we have a Go box parameter - try and decode it
      try
      { // look for a range specifier
	int p = foo.indexOf('-');
	if (p<0)
	{ // single post number - try and use it
	  first = Integer.parseInt(foo);
	  last = first;

	} // end if
	else if (p==0)
	{ // "-number" - works like "0-number"
	  last = Integer.parseInt(foo.substring(1));
	  first = 0;

	} // end if
	else if (p==(foo.length()-1))
	{ // "number-" - works like "number-end"
	  first = Integer.parseInt(foo.substring(0,p));
	  last = topic.getTotalMessages() - 1;

	} // end else if
	else
	{ // two numbers to decode
	  first = Integer.parseInt(foo.substring(0,p));
	  last = Integer.parseInt(foo.substring(p+1));

	} // end else

	return new PostInterval(first,last);

      } // end try
      catch (NumberFormatException nfe)
      { // if numeric conversion fails, just fall out and try to redisplay the other way
      } // end catch

    } // end if

    foo = request.getParameter("p1");
    if (StringUtil.isStringEmpty(foo))
    { // no range specified - cook up a default one
      last = topic.getTotalMessages();
      int ur = topic.getUnreadMessages();
      if ((ur==0) || (ur>=engine.getNumPostsPerPage()))
	first = last - engine.getNumPostsPerPage();
      else
	first = last - (ur + engine.getNumOldPostsBeforeNew());
      last--;

    } // end if
    else
    { // we have at least one parameter...
      try
      { // convert it to an integer and range-limit it
	first = Integer.parseInt(foo);
	if (first<0)
	  first = 0;
	else if (first>=topic.getTotalMessages())
	  first = topic.getTotalMessages() - 1;

      } // end try
      catch (NumberFormatException nfe)
      { // we could not translate the parameter to a number
	throw new ErrorBox(null,"Message parameter is invalid.",on_error);

      } // end catch

      foo = request.getParameter("p2");
      if (StringUtil.isStringEmpty(foo))
        last = first;  // just specify ONE post...
      else
      { // OK, we have an actual "last message" parameter...
	try
	{ // convert it to an integer and range-limit it
	  last = Integer.parseInt(foo);
	  if ((last<0) || (last>=topic.getTotalMessages()))
	    last = topic.getTotalMessages() - 1;

	} // end try
	catch (NumberFormatException nfe)
	{ // we could not translate the parameter to a number
	  throw new ErrorBox(null,"Message parameter is invalid.",on_error);

	} // end catch

      } // end else

    } // end else

    return new PostInterval(first,last);

  } // end getInterval

  private static boolean restorePosts(ServletRequest request, ConferenceContext conf, TopicContext curr_topic)
  {
    String xtopic = request.getParameter("rtop");
    if (StringUtil.isStringEmpty(xtopic))
      return true;
    String xcount = request.getParameter("rct");
    if (StringUtil.isStringEmpty(xcount))
      return true;
    
    TopicContext topic;
    try
    { // get the topic corresponding to the first parameter
      topic = conf.getTopic(Short.parseShort(xtopic));

    } // end try
    catch (NumberFormatException nfe)
    { // the topic number was invalid - forget it
      logger.warn("restorePosts: error translating topic number");
      return true;

    } // end catch
    catch (DataException de)
    { // could not get the topic...
      logger.warn("restorePosts: DataException getting topic - " + de.getMessage(),de);
      return true;

    } // end catch
    catch (AccessError ae)
    { // no access to the topic
      logger.warn("restorePosts: AccessError getting topic - " + ae.getMessage(),ae);
      return true;

    } // end catch

    int nunread;
    try
    { // translate the number of unread posts to set
      nunread = Integer.parseInt(xcount);
      if ((nunread<=0) || (nunread>topic.getTotalMessages()))
      { // must be in the range [1, #messages]...
	logger.warn("restorePosts: unread post count out of range");
	return true;

      } // end if

    } // end try
    catch (NumberFormatException nfe)
    { // the number of unread posts was invalid - forget it
      logger.warn("restorePosts: error translating unread post count");
      return true;

    } // end catch

    try
    { // now try to set the unread messages
      topic.setUnreadMessages(nunread);

    } // end try
    catch (DataException de)
    { // could not get the topic...
      logger.warn("restorePosts: DataException setting unread messages - " + de.getMessage(),de);

    } // end catch

    return (topic.getTopicID()!=curr_topic.getTopicID());

  } // end restorePosts

  /*--------------------------------------------------------------------------------
   * Overrides from class HttpServlet
   *--------------------------------------------------------------------------------
   */

  public String getServletInfo()
  {
    String rc = "ConfDisplay servlet - Display of conference topic and message lists\n"
              + "Part of the Venice Web Communities System\n";
    return rc;

  } // end getServletInfo

  /*--------------------------------------------------------------------------------
   * Overrides from class VeniceServlet
   *--------------------------------------------------------------------------------
   */

  protected VeniceContent doVeniceGet(HttpServletRequest request, VeniceEngine engine,
				      UserContext user, RenderData rdat)
    throws ServletException, IOException, VeniceServletResult
  {
    // get the community
    CommunityContext comm = getCommunityParameter(request,user,true,"top");
    changeMenuCommunity(request,comm);

    // get the conference
    ConferenceContext conf = getConferenceParameter(request,comm,true,"top");

    // get the topic, if we have it
    TopicContext topic = getTopicParameter(request,conf,false,"top");

    if (topic!=null)
    { // we're handling messages within a single topic
      if (logger.isDebugEnabled())
	logger.debug("MODE: display messages in topic");
      String on_error = "confdisp?sig=" + comm.getCommunityID() + "&conf=" + conf.getConfID();
      setMyLocation(request,on_error + "&topic=" + topic.getTopicNumber());

      // if this request is restoring the number of unread posts in another topic, try to do so
      boolean do_readnew = restorePosts(request,conf,topic);

      // determine what the post interval is we want to display
      PostInterval piv = getInterval(engine,request,topic,on_error);
      boolean read_new = do_readnew && !(StringUtil.isStringEmpty(request.getParameter("rnm")));
      boolean show_adv = !(StringUtil.isStringEmpty(request.getParameter("shac")));
      boolean no_bozos = !(StringUtil.isStringEmpty(request.getParameter("nbz")));

      // Create the post display.
      try
      { // create the display
	return new TopicPosts(request,engine,user,comm,conf,topic,piv.getFirst(),piv.getLast(),read_new,
			      show_adv,no_bozos);

      } // end try
      catch (DataException de)
      { // there was a database error retrieving messages
	return new ErrorBox("Database Error","Database error listing messages: " + de.getMessage(),on_error);

      } // end catch
      catch (AccessError ae)
      { // we were unable to retrieve the message list
	return new ErrorBox("Access Error",ae.getMessage(),on_error);

      } // end catch

    } // end if (messages in a topic)
    else
    { // we're displaying the conference's topic list
      if (logger.isDebugEnabled())
	logger.debug("MODE: display topics in conference");
      String on_error = "confops?sig=" + comm.getCommunityID();
      String my_location = "confdisp?sig=" + comm.getCommunityID() + "&conf=" + conf.getConfID();
      boolean read_new = !(StringUtil.isStringEmpty(request.getParameter("rnm")));
      if (read_new)
	my_location += "&rn=1";
      setMyLocation(request,my_location);

      // get any changes to view or sort options
      TopicSortHolder opts = TopicSortHolder.retrieve(request.getSession(true));
      getViewSortDefaults(request,conf.getConfID(),opts,on_error);

      if (read_new)
      { // we need to generate a TopicPosts view
	try
	{ // generate a topic list first
	  List topic_list = conf.getTopicList(opts.getViewOption(conf.getConfID()),
					      opts.getSortOption(conf.getConfID()));

	  // now generate the topic visit order
	  TopicVisitOrder ord = TopicVisitOrder.initialize(request.getSession(true),conf.getConfID(),
							   topic_list);

	  // use the new visit order to get the topic we need to visit
	  short topic_nbr = ord.getNext();
	  Iterator it = topic_list.iterator();
	  while (it.hasNext())
	  { // locate the first topic to be read
	    topic = (TopicContext)(it.next());
	    if (topic.getTopicNumber()==topic_nbr)
	      break;

	  } // end while

	  if (topic==null) // no suitable topic found - just create the topic listing
	    return new TopicListing(request,comm,conf,opts.getViewOption(conf.getConfID()),
				    opts.getSortOption(conf.getConfID()));

	  // determine what the post interval is we want to display
	  PostInterval piv = getInterval(engine,request,topic,on_error);

	  // create the topic posts view
	  return new TopicPosts(request,engine,user,comm,conf,topic,piv.getFirst(),piv.getLast(),true,
				false,false);

	} // end try
	catch (DataException de)
	{ // there was a database error retrieving messages
	  return new ErrorBox("Database Error","Database error listing messages: " + de.getMessage(),on_error);

	} // end catch
	catch (AccessError ae)
	{ // we were unable to retrieve the message list
	  return new ErrorBox("Access Error",ae.getMessage(),on_error);

	} // end catch

      } // end if (creating a "read new" topic list view)
      else
      { // topic listing only...
	TopicListing tl = null;
	try
	{ // create the topic list
	  tl = new TopicListing(request,comm,conf,opts.getViewOption(conf.getConfID()),
				opts.getSortOption(conf.getConfID()));

	} // end try
	catch (DataException de)
	{ // there was a database error retrieving topics
	  return new ErrorBox("Database Error","Database error listing topics: " + de.getMessage(),on_error);

	} // end catch
	catch (AccessError ae)
	{ // we were unable to retrieve the topic list
	  return new ErrorBox("Access Error",ae.getMessage(),on_error);

	} // end catch

	return tl;

      } // end else (not reading new messages, but just displaying topics)

    } // end else (topics in a conference)

  } // end doVeniceGet

} // end class ConfDisplay
