/*
 * 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.format;

import java.util.*;
import java.io.*;
import java.text.DateFormat;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.log4j.*;
import com.silverwrist.util.IOUtil;
import com.silverwrist.util.StringUtil;
import com.silverwrist.venice.core.IDUtils;
import com.silverwrist.venice.core.UserContext;
import com.silverwrist.venice.core.VeniceEngine;
import com.silverwrist.venice.db.PostLinkRewriter;
import com.silverwrist.venice.db.UserNameRewriter;
import com.silverwrist.venice.except.DataException;
import com.silverwrist.venice.servlets.format.menus.LeftMenu;

public class RenderData implements ColorSelectors
{
  /*--------------------------------------------------------------------------------
   * Static data values
   *--------------------------------------------------------------------------------
   */

  private static final String ATTR_NAME = "com.silverwrist.venice.RenderData";

  private static Category logger = Category.getInstance(RenderData.class.getName());

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private RenderConfig rconf;
  private ServletContext ctxt;
  private HttpServletRequest request;
  private HttpServletResponse response;
  private boolean can_gzip = false;
  private Locale my_locale;
  private TimeZone my_timezone;
  private DateFormat activity_time = null;
  private DateFormat display_date = null;

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  RenderData(RenderConfig rconf, UserContext uc, ServletContext ctxt, HttpServletRequest request,
	     HttpServletResponse response)
  {
    this.rconf = rconf;
    this.ctxt = ctxt;
    this.request = request;
    this.response = response;

    // determine whether this browser can accept GZIP-encoded content
    String encodings = request.getHeader("Accept-Encoding");
    if ((encodings!=null) && (encodings.indexOf("gzip")>=0))
      can_gzip = true;

    // read the user's preferred locale
    try
    { // get the user default locale
      my_locale = uc.getLocale();

    } // end try
    catch (DataException de)
    { // locale problems...
      my_locale = Locale.getDefault();

    } // end catch

    // read the user's preferred time zone
    try
    { // get the user default timezone
      my_timezone = uc.getTimeZone();

    } // end try
    catch (DataException de)
    { // time zone problems...
      my_timezone = TimeZone.getDefault();

    } // end catch

  } // end constructor

  /*--------------------------------------------------------------------------------
   * External static operations
   *--------------------------------------------------------------------------------
   */

  public static RenderData retrieve(ServletRequest request)
  {
    return (RenderData)(request.getAttribute(ATTR_NAME));

  } // end retrieve

  /*--------------------------------------------------------------------------------
   * External operations
   *--------------------------------------------------------------------------------
   */

  public void store(ServletRequest request)
  {
    request.setAttribute(ATTR_NAME,this);

  } // end store

  public boolean useHTMLComments()
  {
    return rconf.useHTMLComments();

  } // end useHTMLComments

  public boolean canGZIPEncode()
  {
    return rconf.isGZIPAllowed() && can_gzip;

  } // end canGZIPEncode

  public boolean noSmartTags()
  {
    return rconf.noSmartTags();

  } // end noSmartTags

  public String getSiteImageTag(int hspace, int vspace)
  {
    return rconf.getSiteImageTag(hspace,vspace);

  } // end getSiteImageTag

  public String getFullServletPath(String name)
  {
    StringBuffer buf = new StringBuffer(request.getContextPath());
    buf.append('/').append(name);
    return buf.toString();

  } // end getFullServletPath

  public String getCompleteServletPath(String name)
  {
    StringBuffer buf = new StringBuffer("http://");
    buf.append(request.getServerName());
    if (request.getServerPort()!=80)
      buf.append(':').append(request.getServerPort());
    buf.append(request.getContextPath()).append('/').append(name);
    return buf.toString();

  } // end getCompleteServletPath

  public String getEncodedServletPath(String name)
  {
    return response.encodeURL(this.getFullServletPath(name));

  } // end getEncodedServletPath

  public String getFullImagePath(String name)
  {
    return rconf.getFullImagePath(name);

  } // end getFullImagePath

  public String getStaticFilePath(String name)
  {
    return rconf.getStaticFilePath(name);

  } // end getStaticFilePath

  public String getFormatJSPPath(String name)
  {
    return "/format/" + name;

  } // end getFormatJSPPath

  public String getStaticIncludePath(String name)
  {
    return "/static/" + name;

  } // end getStaticIncludePath

  public String getContextRelativePath(String name)
  {
    StringBuffer buf = new StringBuffer(request.getContextPath());
    if (name.charAt(0)!='/')
      buf.append('/');
    return buf.append(name).toString();

  } // end getContextRelativePath

  public String getStdFontTag(String color, int size)
  {
    return rconf.getStdFontTag(color,size);

  } // end getStdFontTag

  public String getStdFontTag(int selector, int size)
  {
    return rconf.getStdFontTag(selector,size);

  } // end getStdFontTag

  public String getStdBaseFontTag(int size)
  {
    return rconf.getStdBaseFontTag(size);

  } // end getStdBaseFontTag

  public String getTitleTag(String specific)
  {
    return rconf.getTitleTag(specific);

  } // end getTitleTag

  public String getRequiredBullet()
  {
    return rconf.getRequiredBullet();

  } // end getRequiredBullet

  public void writeContentHeader(Writer out, String primary, String secondary) throws IOException
  {
    rconf.writeContentHeader(out,primary,secondary);

  } // end writeContentHeader

  public void writeContentSelectorHeader(Writer out, String caption, List choices,
					 List urls, int selected) throws IOException
  {
    int nchoice = choices.size();
    if (urls.size()<nchoice)
      nchoice = urls.size();

    out.write("<SPAN CLASS=\"chead1\">" + rconf.getStdFontTag(CONTENT_HEADER,5) + "<B>"
              + StringUtil.encodeHTML(caption) + "</B></FONT></SPAN>&nbsp;&nbsp;<SPAN CLASS=\"chead2\">"
	      + rconf.getStdFontTag(CONTENT_HEADER,3));
    for (int i=0; i<nchoice; i++)
    { // loop through all the choices
      if (i==0)
	out.write("[");
      else
	out.write("|");
      out.write("&nbsp;");
      if (i==selected)
	out.write("<B>");
      else
	out.write("<A HREF=\"" + getEncodedServletPath((String)(urls.get(i))) + "\">");
      out.write(StringUtil.encodeHTML((String)(choices.get(i))));
      if (i==selected)
	out.write("</B>");
      else
	out.write("</A>");
      out.write("&nbsp;");

    } // end for

    out.write("]</FONT></SPAN><HR ALIGN=LEFT SIZE=2 WIDTH=\"90%\" NOSHADE>\n");

  } // end writeContentSelectorHeader

  public String getStockMessage(String identifier)
  {
    return rconf.getStockMessage(identifier);

  } // end getStockMessage

  public void writeStockMessage(Writer out, String identifier) throws IOException
  {
    rconf.writeStockMessage(out,identifier);

  } // end writeStockMessage

  public String getStdColor(int selector)
  {
    return rconf.getStdColor(selector);

  } // end getStdColor

  public int scaleFooterLogo(int param)
  {
    return rconf.scaleFooterLogo(param);

  } // end scaleFooterLogo

  public LeftMenu getLeftMenu(String identifier)
  {
    return rconf.getLeftMenu(identifier);

  } // end getLeftMenu

  public String loadStyleSheetData() throws IOException
  {
    return rconf.loadStyleSheetData();

  } // end loadStyleSheetData

  public boolean hasStyleSheetChanged()
  {
    return rconf.hasStyleSheetChanged();

  } // end hasStyleSheetChanged

  public boolean useStyleSheet()
  {
    return rconf.useStyleSheet();

  } // end useStyleSheet

  public String getPhotoNotAvailURL()
  {
    if (rconf.getPhotoNotAvailFixup())
      return rconf.getFullImagePath(rconf.getPhotoNotAvail());
    else
      return rconf.getPhotoNotAvail();

  } // end getPhotoNotAvailURL

  public String formatDateForDisplay(Date date)
  {
    if (display_date==null)
    { // create the display date formatter
      display_date = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.MEDIUM,my_locale);
      display_date.setTimeZone(my_timezone);

    } // end if

    return display_date.format(date);

  } // end formatDateForDisplay

  public Calendar createCalendar()
  {
    return new GregorianCalendar(my_timezone,my_locale);

  } // end createCalendar

  public Calendar createCalendar(Date date)
  {
    Calendar rc = new GregorianCalendar(my_timezone,my_locale);
    rc.setTime(date);
    return rc;

  } // end createCalendar

  public void flushOutput() throws IOException
  {
    response.flushBuffer();

  } // end flushOutput

  public String encodeURL(String url)
  {
    return response.encodeURL(url);

  } // end encodeURL

  public void storeBaseJSPData(BaseJSPData data)
  {
    data.store(request);

  } // end storeBaseJSPData

  public void storeJSPRender(JSPRender jr)
  {
    jr.store(request);

  } // end storeJSPRender

  public void setRequestAttribute(String name, Object value)
  {
    request.setAttribute(name,value);

  } // end setRequestAttribute

  public void forwardDispatch(RequestDispatcher dispatcher) throws IOException, ServletException
  {
    dispatcher.forward(request,response);

  } // end forwardDispatch

  public void includeDispatch(RequestDispatcher dispatcher) throws IOException, ServletException
  {
    dispatcher.include(request,response);

  } // end forwardDispatch

  public void redirectTo(String servlet) throws IOException
  {
    String url = response.encodeRedirectURL(this.getFullServletPath(servlet));
    response.sendRedirect(url);

  } // end redirectTo

  public void redirectAbsolute(String url) throws IOException
  {
    response.sendRedirect(url);

  } // end redirectAbsolute

  public String getActivityString(Date date)
  {
    if (date==null)
      return "Never";  // safeguard

    Calendar c_last = createCalendar(date);
    Calendar c_now = createCalendar(new Date());
    int delta_days = 0;
    while (   (c_last.get(Calendar.YEAR)!=c_now.get(Calendar.YEAR))
	   || (c_last.get(Calendar.DAY_OF_YEAR)!=c_now.get(Calendar.DAY_OF_YEAR)))
    { // advance until we're pointing at the same year and the same day of the year
      delta_days++;
      c_last.add(Calendar.DAY_OF_YEAR,1);

    } // end while

    switch (delta_days)
    { // now return a string based on the difference in days
      case 0:
	if (activity_time==null)
	{ // get the "activity" time formatter
	  activity_time = DateFormat.getTimeInstance(DateFormat.MEDIUM,my_locale);
	  activity_time.setTimeZone(my_timezone);

	} // end if
	return "Today, " + activity_time.format(date);

      case 1:
	if (activity_time==null)
	{ // get the "activity" time formatter
	  activity_time = DateFormat.getTimeInstance(DateFormat.MEDIUM,my_locale);
	  activity_time.setTimeZone(my_timezone);

	} // end if
	return "Yesterday, " + activity_time.format(date);

      default:
	return String.valueOf(delta_days) + " days ago";

    } // end switch

  } // end getActivityString

  public void nullResponse()
  {
    response.setStatus(response.SC_NO_CONTENT);

  } // end nullResponse

  public void errorResponse(int code) throws IOException
  {
    response.sendError(code);

  } // end errorResponse

  public void errorResponse(int code, String msg) throws IOException
  {
    response.sendError(code,msg);

  } // end errorResponse

  public void sendBinaryData(String type, String filename, int length, InputStream data) throws IOException
  {
    response.setContentType(type);
    response.setContentLength(length);
    if (filename!=null)  // make sure we pass the filename along, too
      response.setHeader("Content-Disposition","attachment; filename=\"" + filename + "\";");

    // Copy the contents of the "data" stream to the output.
    IOUtil.copy(data,response.getOutputStream());

  } // end sendBinaryData

  public String rewritePostData(String data)
  {
    if ((data.indexOf(PostLinkRewriter.URI_PREFIX)<0) && (data.indexOf(UserNameRewriter.URI_PREFIX)<0))
      return data;

    StringBuffer buf = new StringBuffer();
    String interm;

    if (data.indexOf(PostLinkRewriter.URI_PREFIX)>=0)
    { // begin replacing everything with post links
      String t = data;
      int p = t.indexOf(PostLinkRewriter.URI_PREFIX);
      while (p>=0)
      { // break off the start of the string
	if (p>0)
	  buf.append(t.substring(0,p));
	t = t.substring(p + PostLinkRewriter.URI_PREFIX.length());

	// find the end of the post link...
	p = 0;
	while (IDUtils.isValidPostLinkChar(t.charAt(p)))
	  p++;

	if (p>0)
	{ // append the post link to the "go" servlet path, and encode the lot
	  buf.append(getEncodedServletPath("go/" + t.substring(0,p)));
	  t = t.substring(p);

	} // end if
	else  // false alarm
	  buf.append(PostLinkRewriter.URI_PREFIX);

	// and now look again...
	p = t.indexOf(PostLinkRewriter.URI_PREFIX);

      } // end while

      buf.append(t);
      interm = buf.toString();
      buf.setLength(0);

    } // end if
    else  // no post link strings, this is the intermediate form
      interm = data;

    if (interm.indexOf(UserNameRewriter.URI_PREFIX)>=0)
    { // begin replacing everything with user links
      String t = interm;
      int p = t.indexOf(UserNameRewriter.URI_PREFIX);
      while (p>=0)
      { // break off the start of the string
	if (p>0)
	  buf.append(t.substring(0,p));
	t = t.substring(p + UserNameRewriter.URI_PREFIX.length());

	// find the end of the user link...
	p = 0;
	while (IDUtils.isValidVeniceIDChar(t.charAt(p)))
	  p++;

	if (p>0)
	{ // append the post link to the "user" servlet path, and encode the lot
	  buf.append(getEncodedServletPath("user/" + t.substring(0,p)));
	  t = t.substring(p);

	} // end if
	else  // false alarm
	  buf.append(UserNameRewriter.URI_PREFIX);

	// and now look again...
	p = t.indexOf(UserNameRewriter.URI_PREFIX);

      } // end while

      buf.append(t);
      return buf.toString();

    } // end if
    else  // no more to find - just return this
      return interm;

  } // end rewritePostData

  public String mapToPath(String path)
  {
    return ctxt.getRealPath(path);

  } // end mapToPath

  public Cookie createCookie(String name, String value, int age)
  {
    Cookie rc = new Cookie(name,value);
    rc.setMaxAge(age);
    rc.setPath(request.getContextPath());
    return rc;

  } // end createCookie

} // end class RenderData
