/*
 * 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@ricochet.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2001-2004 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.venice.core.impl;

import java.io.*;
import java.sql.*;
import java.util.*;
import org.apache.log4j.*;
import com.silverwrist.util.StringUtil;
import com.silverwrist.venice.db.*;
import com.silverwrist.venice.core.*;
import com.silverwrist.venice.core.internals.EnvEngine;
import com.silverwrist.venice.except.*;

class PublishedMessageImpl implements TopicMessageContext
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

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

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

  private EnvEngine env;                   // the execution environment
  private int cid;
  private long postid;
  private long parent;
  private int topicid;
  private int num;
  private int linecount;
  private int creator_uid;
  private java.util.Date posted;
  private String pseud;
  private String creator_cache = null;
  private String text_cache = null;
  private PublishedMessageTopicImpl topic = null;

  /*--------------------------------------------------------------------------------
   * Constructors
   *--------------------------------------------------------------------------------
   */

  PublishedMessageImpl(EnvEngine env, int cid, long postid, long parent, int topicid, int num, int linecount,
		       int creator_uid, java.util.Date posted, String pseud, String creator_cache,
		       String text_cache)
  {
    this.env = env;
    this.cid = cid;
    this.postid = postid;
    this.parent = parent;
    this.topicid = topicid;
    this.num = num;
    this.linecount = linecount;
    this.creator_uid = creator_uid;
    this.posted = posted;
    this.pseud = pseud;
    this.creator_cache = creator_cache;
    this.text_cache = text_cache;

  } // end constructor

  protected PublishedMessageImpl(EnvEngine env, int cid, long postid, long parent, int topicid, int num,
				 int linecount, int creator_uid, java.util.Date posted, String pseud)
  {
    this.env = env;
    this.cid = cid;
    this.postid = postid;
    this.parent = parent;
    this.topicid = topicid;
    this.num = num;
    this.linecount = linecount;
    this.creator_uid = creator_uid;
    this.posted = posted;
    this.pseud = pseud;

  } // end constructor

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

  private static final String quickGetUserName(Connection conn, int uid) throws SQLException
  {
    Statement stmt = null;

    try
    { // run the quick select
      stmt = conn.createStatement();
      ResultSet rs = stmt.executeQuery("SELECT username FROM users WHERE uid = " + String.valueOf(uid) + ";");
      if (rs.next())
	return rs.getString(1);
      else
	return "(unknown)";

    } // end try
    finally
    { // shut down the statement to conserve resources
      SQLUtil.shutdown(stmt);

    } // end finally

  } // end quickGetUserName

  /*--------------------------------------------------------------------------------
   * Implementations from interface TopicMessageContext
   *--------------------------------------------------------------------------------
   */

  public long getPostID()
  {
    return postid;

  } // end getPostID

  public long getParentPostID()
  {
    return parent;

  } // end getParentPostID

  public int getPostNumber()
  {
    return num;

  } // end getPostNumber

  public int getNumLines()
  {
    return linecount;

  } // end getNumLines

  public int getCreatorUID()
  {
    return creator_uid;

  } // end getCreatorUID

  public String getCreatorName() throws DataException
  {
    if (creator_cache==null)
    { // we don't have the user name yet, get it out of the database
      Connection conn = null;

      try
      { // use a database connection to get the user name
	conn = env.getConnection();
	creator_cache = quickGetUserName(conn,creator_uid);

      } // end try
      catch (SQLException e)
      { // turn this into a DataException
	logger.error("DB error reading user name: " + e.getMessage(),e);
	throw new DataException("unable to retrieve user name: " + e.getMessage(),e);

      } // end catch
      finally
      { // make sure we release the connection before we go
	SQLUtil.shutdown(conn);

      } // end finally

    } // end if

    return creator_cache;

  } // end getCreatorName

  public java.util.Date getPostDate()
  {
    return posted;

  } // end getPostDate

  public boolean isHidden()
  {
    return false;

  } // end isHidden

  public boolean isScribbled()
  {
    return false;

  } // end isScribbled

  public boolean isNuked()
  {
    return false;

  } // end isNuked

  public java.util.Date getScribbleDate()
  {
    return null;

  } // end getScribbleDate

  public String getPseud()
  {
    return pseud;

  } // end getPseud

  public String getBodyText() throws DataException
  {
    if (text_cache==null)
    { // we don't have the body text yet, go get it
      Connection conn = null;
      Statement stmt = null;

      try
      { // use a database connection to get the body text
	conn = env.getConnection();
	stmt = conn.createStatement();
	ResultSet rs = stmt.executeQuery("SELECT data FROM postdata WHERE postid = "
					 + String.valueOf(postid) + ";");
	if (rs.next())
	  text_cache = rs.getString(1);
	else
	  return "<b>[[Venice Internal Error: Data Missing]]</b>";
	  // FUTURE: throw an exception?

      } // end try
      catch (SQLException e)
      { // turn this into a DataException
	logger.error("DB error reading post data: " + e.getMessage(),e);
	throw new DataException("unable to retrieve post data: " + e.getMessage(),e);

      } // end catch
      finally
      { // make sure we release the connection before we go
	SQLUtil.shutdown(stmt);
	SQLUtil.shutdown(conn);

      } // end finally

    } // end if

    return text_cache;

  } // end getBodyCache

  public boolean hasAttachment()
  {
    return false;  // FUTURE: allow publishing paperclips?

  } // end hasAttachment

  public String getAttachmentType()
  {
    return null;  // FUTURE: allow publishing paperclips?

  } // end getAttachmentType

  public String getAttachmentFilename()
  {
    return null;  // FUTURE: allow publishing paperclips?

  } // end getAttachmentFilename

  public int getAttachmentLength()
  {
    return 0;  // FUTURE: allow publishing paperclips?

  } // end getAttachmentLength

  public InputStream getAttachmentData() throws AccessError, DataException
  {
    // FUTURE: allow publishing paperclips?
    throw new AccessError("There is no attachment data for this message.");

  } // end getAttachmentData

  public int getAttachmentData(OutputStream here) throws AccessError, DataException, IOException
  {
    // FUTURE: allow publishing paperclips?
    throw new AccessError("There is no attachment data for this message.");

  } // end getAttachmentData

  public boolean canHide()
  {
    return false;

  } // end canHide

  public boolean canScribble()
  {
    return false;

  } // end canScribble

  public boolean canNuke()
  {
    return false;

  } // end canNuke

  public void setHidden(boolean flag) throws DataException, AccessError
  {
    throw new AccessError("You are not permitted to change the hidden status of this message.");

  } // end setHidden

  public void scribble() throws DataException, AccessError
  {
    throw new AccessError("You are not permitted to scribble this message.");

  } // end scribble

  public void nuke() throws DataException, AccessError
  {
    throw new AccessError("You are not permitted to nuke this message.");

  } // end nuke

  public void attachData(String m_type, String file, int length, InputStream data)
      throws AccessError, DataException
  {
    throw new AccessError("You are not permitted to add an attachment to this message.");

  } // end attachData

  public void attachData(String m_type, String file, byte[] data) throws AccessError, DataException
  {
    throw new AccessError("You are not permitted to add an attachment to this message.");

  } // end attachData

  public boolean canPublish()
  {
    return false;

  } // end canPublish

  public void publish() throws DataException, AccessError
  {
    throw new DataException("Cannot publish a message that has already been published.");

  } // end publish

  public TopicContext getEnclosingTopic() throws DataException
  {
    if (topic==null)
      topic = new PublishedMessageTopicImpl(env,cid,topicid);
    return topic;

  } // end getEnclosingTopic

  public String getPostLink() throws DataException
  {
    return getEnclosingTopic().getPostLink() + "." + num;

  } // end getPostLink

  public void moveToTopic(int newtopicid) throws DataException
  {
    throw new DataException("Cannot move a message that has already been published.");

  } // end moveToTopic

  public boolean canMove()
  {
    return false;

  } // end canMove

  /*--------------------------------------------------------------------------------
   * Static operations usable only within package
   *--------------------------------------------------------------------------------
   */

  static final void backfillCache(List cache_list, int desired_size, EnvEngine env) throws DataException
  {
    Connection conn = null;
    Statement stmt = null;

    try
    { // get a database connection
      conn = env.getConnection();
      stmt = conn.createStatement();

      // create the statement to retrieve the post information
      StringBuffer sql =
	  new StringBuffer("SELECT p.sigid, p.postid, q.parent, q.topicid, q.num, q.linecount, q.creator_uid, "
			   + "q.posted, q.pseud FROM postpublish p, posts q WHERE p.postid = q.postid "
			   + "ORDER BY p.on_date DESC, p.postid ASC LIMIT ");
      sql.append(cache_list.size()).append(", ").append(desired_size-cache_list.size()).append(';');

      // execute the statement!
      ResultSet rs = stmt.executeQuery(sql.toString());
      while (rs.next())
      { // create PublishedMessageImpl objects to backfill the cache
	TopicMessageContext tmc = new PublishedMessageImpl(env,rs.getInt(1),rs.getLong(2),rs.getLong(3),
							   rs.getInt(4),rs.getInt(5),rs.getInt(6),rs.getInt(7),
							   SQLUtil.getFullDateTime(rs,8),rs.getString(9));
	cache_list.add(tmc);

      } // end while

    } // end try
    catch (SQLException e)
    { // error retrieving post information
      logger.error("DB error reading post information: " + e.getMessage(),e);
      throw new DataException("unable to retrieve post information: " + e.getMessage(),e);

    } // end catch
    finally
    { // make sure we release the connection before we go
      SQLUtil.shutdown(stmt);
      SQLUtil.shutdown(conn);

    } // end finally

  } // end backfillCache

  static void backfillReturn(List return_list, EnvEngine env) throws DataException
  {
    Connection conn = null;
    Statement stmt = null;

    try
    { // get a database connection
      conn = env.getConnection();
      stmt = conn.createStatement();

      // How many posts have been published anyway?
      ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM postpublish;");
      if (!(rs.next()))
	throw new InternalStateError("Count query screwup in PublishedMessageImpl.backfillReturn");
      if (rs.getInt(1)<=return_list.size())
	return;  // nothing to do here!

      // execute the statement to retrieve the post information
      final String sql = "SELECT p.sigid, p.postid, q.parent, q.topicid, q.num, q.linecount, q.creator_uid, "
	               + "q.posted, q.pseud FROM postpublish p, posts q WHERE p.postid = q.postid "
	               + "ORDER BY p.on_date DESC, p.postid ASC;";
      rs = stmt.executeQuery(sql);

      // append to the output list
      int ctr = return_list.size();
      while (rs.next())
      { // we need to skip the first batch of output, because it's cached
	if ((ctr--)<=0)
	{ // append context, please
	  TopicMessageContext ctxt = 
	      new PublishedMessageImpl(env,rs.getInt(1),rs.getLong(2),rs.getLong(3),rs.getInt(4),rs.getInt(5),
				       rs.getInt(6),rs.getInt(7),SQLUtil.getFullDateTime(rs,8),
				       rs.getString(9));
	  return_list.add(ctxt);

	} // end if

      } // end while

    } // end try
    catch (SQLException e)
    { // error retrieving post information
      logger.error("DB error reading post information: " + e.getMessage(),e);
      throw new DataException("unable to retrieve post information: " + e.getMessage(),e);

    } // end catch
    finally
    { // make sure we release the connection before we go
      SQLUtil.shutdown(stmt);
      SQLUtil.shutdown(conn);

    } // end finally

  } // end backfillReturn

} // end class PublishedMessageImpl
