/*
 * 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) 2003 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.venice.sidebox;

import java.sql.*;
import java.util.*;
import org.apache.log4j.Logger;
import com.silverwrist.util.*;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.util.*;

public class SideboxOps_mysql extends SideboxOps
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static final Logger logger = Logger.getLogger(SideboxOps_mysql.class);

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

  private PropertySerializer m_psz;        // reference to property serializer

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

  public SideboxOps_mysql(DBConnectionPool pool)
  {
    super(pool);
    m_psz = (PropertySerializer)(pool.queryService(PropertySerializer.class));

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Internal operations
   *--------------------------------------------------------------------------------
   */

  private final String serializeContextParam(Object param) throws DatabaseException
  {
    if (param==null)
      return null;
    String s = m_psz.serializeProperty(param);
    if (s==null)
    { // serialization exception - throw it
      DatabaseException de = new DatabaseException(SideboxOps_mysql.class,"SideboxMessages","context.param.ser");
      de.setParameter(0,param.getClass().getName());
      throw de;

    } // end if

    return s;

  } // end serializeContextParam

  /*--------------------------------------------------------------------------------
   * Overrides from class OpsBase
   *--------------------------------------------------------------------------------
   */

  public void dispose()
  {
    m_psz = null;
    super.dispose();

  } // end dispose

  /*--------------------------------------------------------------------------------
   * Abstract implementations from class SideboxOps
   *--------------------------------------------------------------------------------
   */

  int[] getSideboxIDList(int uid, int context_nsid, String context_name, Object context_param) throws DatabaseException
  {
    String context_param_ser = serializeContextParam(context_param);
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    LinkedList tmp_list = new LinkedList();
    int total = 0;

    if (logger.isDebugEnabled())
      logger.debug("Getting sidebox list for UID #" + uid);

    try
    { // get a connection
      conn = getConnection();

      // create and execute the statement
      if (context_param_ser==null)
	stmt = conn.prepareStatement("SELECT sbid FROM sbox_deploy WHERE uid = ? AND ctx_nsid = ? AND ctx_name = ? "
				     + "AND param IS NULL ORDER BY seq;");
      else
      { // use the version with the parameter
	stmt = conn.prepareStatement("SELECT sbid FROM sbox_deploy WHERE uid = ? AND ctx_nsid = ? AND ctx_name = ? "
				     + "AND param = ? ORDER BY seq;");
	stmt.setString(4,context_param_ser);

      } // end else

      stmt.setInt(1,uid);
      stmt.setInt(2,context_nsid);
      stmt.setString(3,context_name);
      rs = stmt.executeQuery();

      // build the temporary return arrays
      int curptr = 0;
      int[] cur = new int[16];
      while (rs.next())
      { // add elements to the array
	if (curptr==16)
	{ // shift array to list
	  tmp_list.addLast(cur);
	  cur = new int[16];
	  curptr = 0;

	} // end if

	cur[curptr++] = rs.getInt(1);
	total++;

      } // end while

      tmp_list.addLast(cur);  // add the "tail" to the array

      if (logger.isDebugEnabled())
	logger.debug("found: " + total + " sidebox IDs");

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(conn);

    } // end finally

    // Build the return array.
    int[] rc = new int[total];
    int outptr = 0;
    while (total>0)
    { // copy array segments into the full return array
      int[] piece = (int[])(tmp_list.removeFirst());
      int ncopy = Math.min(total,16);
      System.arraycopy(piece,0,rc,outptr,ncopy);
      outptr += ncopy;
      total -= ncopy;

    } // end while

    tmp_list.clear();
    return rc;

  } // end getSideboxIDList

  PropertyKey getSideboxType(int sbid) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try
    { // get a connection
      conn = getConnection();

      // create and execute the statement
      stmt = conn.prepareStatement("SELECT type_nsid, type_name FROM sbox_master WHERE sbid = ?;");
      stmt.setInt(1,sbid);
      rs = stmt.executeQuery();
      if (rs.next())
	return new PropertyKey(rs.getInt(1),rs.getString(2));

      // throw an exception if we didn't find it
      DatabaseException de = new DatabaseException(SideboxOps_mysql.class,"SideboxMessages","no.sidebox");
      de.setParameter(0,String.valueOf(sbid));
      throw de;

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end getSideboxType

  PropertyKey getSideboxIdentity(int sbid) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try
    { // get a connection
      conn = getConnection();

      // create and execute the statement
      stmt = conn.prepareStatement("SELECT sb_nsid, sb_name FROM sbox_master WHERE sbid = ?;");
      stmt.setInt(1,sbid);
      rs = stmt.executeQuery();
      if (rs.next())
	return new PropertyKey(rs.getInt(1),rs.getString(2));

      // throw an exception if we didn't find it
      DatabaseException de = new DatabaseException(SideboxOps_mysql.class,"SideboxMessages","no.sidebox");
      de.setParameter(0,String.valueOf(sbid));
      throw de;

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end getSideboxIdentity

  Object getSideboxProperty(int sbid, PropertyKey key) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    String rc_str = null;
    try
    { // get a connection
      conn = getConnection();

      // look up the property
      stmt = conn.prepareStatement("SELECT prop_value FROM sbox_props WHERE sbid = ? AND nsid = ? AND prop_name = ?;");
      stmt.setInt(1,sbid);
      stmt.setInt(2,key.getNamespaceID());
      stmt.setString(3,key.getName());
      rs = stmt.executeQuery();
      if (!(rs.next()))
	return null;  // property not found

      rc_str = rs.getString(1);

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(conn);

    } // end finally

    // Deserialize the property value.
    Object rc = m_psz.deserializeProperty(rc_str);
    if (rc!=null)
      return rc;

    // deserialization exception - throw it
    DatabaseException de = new DatabaseException(SideboxOps_mysql.class,"SideboxMessages","property.deserialize");
    de.setParameter(0,key.getName());
    throw de;

  } // end getSideboxProperty

  List getSideboxDescriptors(int uid, int context_nsid, String context_name, Object context_param)
      throws DatabaseException
  {
    String context_param_ser = serializeContextParam(context_param);
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;

    try
    { // get a connection
      conn = getConnection();

      // create and execute the statement
      if (context_param_ser==null)
	stmt = conn.prepareStatement("SELECT m.sbid, m.descr FROM sbox_deploy d, sbox_master m WHERE m.sbid = d.sbid "
				     + "AND d.uid = ? AND d.ctx_nsid = ? AND d.ctx_name = ? AND d.param IS NULL "
				     + "ORDER BY d.seq;");
      else
      { // use the version with the parameter
	stmt = conn.prepareStatement("SELECT m.sbid, m.descr FROM sbox_deploy d, sbox_master m WHERE m.sbid = d.sbid "
				     + "AND d.uid = ? AND d.ctx_nsid = ? AND d.ctx_name = ? AND d.param = ? "
				     + "ORDER BY d.seq;");
	stmt.setString(4,context_param_ser);

      } // end else

      stmt.setInt(1,uid);
      stmt.setInt(2,context_nsid);
      stmt.setString(3,context_name);
      rs = stmt.executeQuery();

      // Build the return list.
      ArrayList rc = new ArrayList();
      while (rs.next())
	rc.add(new SideboxDescriptor(rs.getInt(1),rs.getString(2)));
      rc.trimToSize();
      return rc;

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end getSideboxDescriptors

  List getAllSideboxDescriptors(int context_nsid, String context_name) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;

    try
    { // get a connection
      conn = getConnection();

      // create and execute the statement
      stmt = conn.prepareStatement("SELECT m.sbid, m.descr FROM sbox_master m, sbox_context c WHERE m.sbid = c.sbid "
				   + "AND c.ctx_nsid = ? AND c.ctx_name = ? ORDER BY m.descr");
      stmt.setInt(1,context_nsid);
      stmt.setString(2,context_name);
      rs = stmt.executeQuery();

      // Build the return list.
      ArrayList rc = new ArrayList();
      while (rs.next())
	rc.add(new SideboxDescriptor(rs.getInt(1),rs.getString(2)));
      rc.trimToSize();
      return rc;

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end getAllSideboxDescriptors

  void moveSideboxItemToPosition(int uid, int context_nsid, String context_name, Object context_param, int sbid,
				 int new_pos) throws DatabaseException
  {
    String context_param_ser = serializeContextParam(context_param);
    Connection conn = null;
    PreparedStatement stmt = null;
    Statement stmt2 = null;
    PreparedStatement stmt_ren = null;
    ResultSet rs = null;

    try
    { // get a connection
      conn = getConnection();

      // lock the deploy table
      stmt2 = conn.createStatement();
      stmt2.executeUpdate("LOCK TABLES sbox_deploy WRITE;");

      // locate the current position of the sidebox in the list
      if (context_param_ser==null)
	stmt = conn.prepareStatement("SELECT seq FROM sbox_deploy WHERE sbid = ? AND uid = ? AND ctx_nsid = ? "
				     + "AND ctx_name = ? AND param IS NULL;");
      else
      { // add context parameter to statement
	stmt = conn.prepareStatement("SELECT seq FROM sbox_deploy WHERE sbid = ? AND uid = ? AND ctx_nsid = ? "
				     + "AND ctx_name = ? AND param = ?;");
	stmt.setString(5,context_param_ser);

      } // end else

      stmt.setInt(1,sbid);
      stmt.setInt(2,uid);
      stmt.setInt(3,context_nsid);
      stmt.setString(4,context_name);
      rs = stmt.executeQuery();
      if (!(rs.next()))
      { // sidebox does not exist here - throw exception
	DatabaseException de = new DatabaseException(SideboxOps_mysql.class,"SideboxMessages","sbox.not.in.list");
	de.setParameter(0,String.valueOf(sbid));
	throw de;

      } // end if

      int old_pos = rs.getInt(1);
      if (old_pos==new_pos)
	return;  // this is a no-op

      SQLUtils.shutdown(rs);   // prep for next statement
      rs = null;
      SQLUtils.shutdown(stmt);

      if (new_pos<0)
	new_pos = 0;  // range-check on low end
      else
      { // need to range-check on high end - create statement to get max index
	if (context_param_ser==null)
	  stmt = conn.prepareStatement("SELECT MAX(seq) FROM sbox_deploy WHERE uid = ? AND ctx_nsid = ? "
				       + "AND ctx_name = ? AND param IS NULL;");
	else
	{ // add context parameter to statement
	  stmt = conn.prepareStatement("SELECT MAX(seq) FROM sbox_deploy WHERE uid = ? AND ctx_nsid = ? "
				       + "AND ctx_name = ? AND param = ?;");
	  stmt.setString(4,context_param_ser);

	} // end else

	stmt.setInt(1,uid);
	stmt.setInt(2,context_nsid);
	stmt.setString(3,context_name);
	rs = stmt.executeQuery();
	int max_pos = SQLUtils.getReturnCountInt(rs,1);
	SQLUtils.shutdown(rs);   // prep for next statement
	rs = null;
	SQLUtils.shutdown(stmt);
	if (new_pos>max_pos)
	  new_pos = max_pos;

      } // end else

      // Start by renumbering the original item to index -1, normally unused.
      if (context_param_ser==null)
	stmt_ren = conn.prepareStatement("UPDATE sbox_deploy SET seq = ? WHERE sbid = ? AND uid = ? AND ctx_nsid = ? "
					 + "AND ctx_name = ? AND param IS NULL;");
      else
      { // include the context parameter
	stmt_ren = conn.prepareStatement("UPDATE sbox_deploy SET seq = ? WHERE sbid = ? AND uid = ? AND ctx_nsid = ? "
					 + "AND ctx_name = ? AND param = ?;");
	stmt_ren.setString(6,context_param_ser);

      } // end else

      stmt_ren.setInt(1,-1);
      stmt_ren.setInt(2,sbid);
      stmt_ren.setInt(3,uid);
      stmt_ren.setInt(4,context_nsid);
      stmt_ren.setString(5,context_name);
      stmt_ren.executeUpdate();

      // Now renumber the items in between the new position and the old position.
      if (new_pos<old_pos)
      { // prepare statement to shift some items down
	if (context_param_ser==null)
	  stmt = conn.prepareStatement("UPDATE sbox_deploy SET seq = seq + 1 WHERE seq >= ? AND seq <= ? AND uid = ? "
				       + "AND ctx_nsid = ? AND ctx_name = ? AND param IS NULL;");
	else
	{ // include the context parameter
	  stmt = conn.prepareStatement("UPDATE sbox_deploy SET seq = seq + 1 WHERE seq >= ? AND seq <= ? AND uid = ? "
				       + "AND ctx_nsid = ? AND ctx_name = ? AND param = ?;");
	  stmt.setString(6,context_param_ser);

	} // end else

	stmt.setInt(1,new_pos);
	stmt.setInt(2,old_pos);
	stmt.setInt(3,uid);
	stmt.setInt(4,context_nsid);
	stmt.setString(5,context_name);

      } // end if
      else
      { // prepare statement to shift some items up
	if (context_param_ser==null)
	  stmt = conn.prepareStatement("UPDATE sbox_deploy SET seq = seq - 1 WHERE seq >= ? AND seq <= ? AND uid = ? "
				       + "AND ctx_nsid = ? AND ctx_name = ? AND param IS NULL;");
	else
	{ // include the context parameter
	  stmt = conn.prepareStatement("UPDATE sbox_deploy SET seq = seq - 1 WHERE seq >= ? AND seq <= ? AND uid = ? "
				       + "AND ctx_nsid = ? AND ctx_name = ? AND param = ?;");
	  stmt.setString(6,context_param_ser);

	} // end else

	stmt.setInt(1,old_pos);
	stmt.setInt(2,new_pos);
	stmt.setInt(3,uid);
	stmt.setInt(4,context_nsid);
	stmt.setString(5,context_name);

      } // end else

      stmt.executeUpdate();

      // Now move the old element back into place in its new position, using the statement we've already prepared
      // and just changing its parameters.
      stmt_ren.setInt(1,new_pos);
      stmt_ren.executeUpdate();

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      MySQLUtils.unlockTables(conn);
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt_ren);
      SQLUtils.shutdown(stmt2);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end moveSideboxItemToPosition

  void removeSidebox(int uid, int context_nsid, String context_name, Object context_param, int sbid)
      throws DatabaseException
  {
    String context_param_ser = serializeContextParam(context_param);
    Connection conn = null;
    PreparedStatement stmt = null;
    Statement stmt2 = null;
    ResultSet rs = null;

    try
    { // get a connection
      conn = getConnection();

      // lock the deploy table
      stmt2 = conn.createStatement();
      stmt2.executeUpdate("LOCK TABLES sbox_deploy WRITE;");

      // look for the item in the list, and get its old position
      // locate the current position of the sidebox in the list
      if (context_param_ser==null)
	stmt = conn.prepareStatement("SELECT seq FROM sbox_deploy WHERE sbid = ? AND uid = ? AND ctx_nsid = ? "
				     + "AND ctx_name = ? AND param IS NULL;");
      else
      { // add context parameter to statement
	stmt = conn.prepareStatement("SELECT seq FROM sbox_deploy WHERE sbid = ? AND uid = ? AND ctx_nsid = ? "
				     + "AND ctx_name = ? AND param = ?;");
	stmt.setString(5,context_param_ser);

      } // end else

      stmt.setInt(1,sbid);
      stmt.setInt(2,uid);
      stmt.setInt(3,context_nsid);
      stmt.setString(4,context_name);
      rs = stmt.executeQuery();
      if (!(rs.next()))
      { // sidebox does not exist here - throw exception
	DatabaseException de = new DatabaseException(SideboxOps_mysql.class,"SideboxMessages","sbox.not.in.list");
	de.setParameter(0,String.valueOf(sbid));
	throw de;

      } // end if

      int old_pos = rs.getInt(1);

      SQLUtils.shutdown(rs);   // prep for next statement
      rs = null;
      SQLUtils.shutdown(stmt);

      // Erase this row from the table.
      if (context_param_ser==null)
	stmt = conn.prepareStatement("DELETE FROM sbox_deploy WHERE sbid = ? AND uid = ? AND ctx_nsid = ? "
				     + "AND ctx_name = ? AND param IS NULL;");
      else
      { // include the context parameter
	stmt = conn.prepareStatement("DELETE FROM sbox_deploy WHERE sbid = ? AND uid = ? AND ctx_nsid = ? "
				     + "AND ctx_name = ? AND param = ?;");
	stmt.setString(5,context_param_ser);

      } // end else

      stmt.setInt(1,sbid);
      stmt.setInt(2,uid);
      stmt.setInt(3,context_nsid);
      stmt.setString(4,context_name);
      stmt.executeUpdate();

      // Now renumber all the following rows to close the gap.
      SQLUtils.shutdown(stmt);
      if (context_param_ser==null)
	stmt = conn.prepareStatement("UPDATE sbox_deploy SET seq = seq - 1 WHERE seq >= ? AND uid = ? "
				     + "AND ctx_nsid = ? AND ctx_name = ? AND param IS NULL;");
      else
      { // include the context parameter
	stmt = conn.prepareStatement("UPDATE sbox_deploy SET seq = seq - 1 WHERE seq >= ? AND uid = ? "
				     + "AND ctx_nsid = ? AND ctx_name = ? AND param = ?;");
	stmt.setString(5,context_param_ser);

      } // end else

      stmt.setInt(1,old_pos);
      stmt.setInt(2,uid);
      stmt.setInt(3,context_nsid);
      stmt.setString(4,context_name);
      stmt.executeUpdate();

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      MySQLUtils.unlockTables(conn);
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt2);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end removeSidebox

  void addSidebox(int uid, int context_nsid, String context_name, Object context_param, int sbid)
      throws DatabaseException
  {
    String context_param_ser = serializeContextParam(context_param);
    Connection conn = null;
    PreparedStatement stmt = null;
    Statement stmt2 = null;
    ResultSet rs = null;

    try
    { // get a connection
      conn = getConnection();

      // lock the deploy table
      stmt2 = conn.createStatement();
      stmt2.executeUpdate("LOCK TABLES sbox_deploy WRITE, sbox_context READ;");

      // see if the given sidebox is already in the list
      if (context_param_ser==null)
	stmt = conn.prepareStatement("SELECT seq FROM sbox_deploy WHERE sbid = ? AND uid = ? AND ctx_nsid = ? "
				     + "AND ctx_name = ? AND param IS NULL;");
      else
      { // add context parameter to statement
	stmt = conn.prepareStatement("SELECT seq FROM sbox_deploy WHERE sbid = ? AND uid = ? AND ctx_nsid = ? "
				     + "AND ctx_name = ? AND param = ?;");
	stmt.setString(5,context_param_ser);

      } // end else

      stmt.setInt(1,sbid);
      stmt.setInt(2,uid);
      stmt.setInt(3,context_nsid);
      stmt.setString(4,context_name);
      rs = stmt.executeQuery();
      if (rs.next())
      { // already in list - throw exception
	DatabaseException de = new DatabaseException(SideboxOps_mysql.class,"SideboxMessages","sbox.already.in.list");
	de.setParameter(0,String.valueOf(sbid));
	throw de;

      } // end if

      SQLUtils.shutdown(rs);   // prep for next statement
      rs = null;
      SQLUtils.shutdown(stmt);

      // Make sure this sidebox is permissible in this context.
      stmt = conn.prepareStatement("SELECT sbid FROM sbox_context WHERE sbid = ? AND ctx_nsid = ? AND ctx_name = ?;");
      stmt.setInt(1,sbid);
      stmt.setInt(2,context_nsid);
      stmt.setString(3,context_name);
      rs = stmt.executeQuery();
      if (!(rs.next()))
      { // impermissible in this list - throw an exception
	DatabaseException de = new DatabaseException(SideboxOps_mysql.class,"SideboxMessages","sbox.impermissible");
	de.setParameter(0,String.valueOf(sbid));
	throw de;

      } // end if

      SQLUtils.shutdown(rs);   // prep for next statement
      rs = null;
      SQLUtils.shutdown(stmt);

      // Get the number of items we already have in the list, which is also the index of the new item (as
      // elements start numbering with 0).
      if (context_param_ser==null)
	stmt = conn.prepareStatement("SELECT COUNT(*) FROM sbox_deploy WHERE uid = ? AND ctx_nsid = ? "
				     + "AND ctx_name = ? AND param IS NULL;");
      else
      { // add context parameter to statement
	stmt = conn.prepareStatement("SELECT COUNT(*) FROM sbox_deploy WHERE uid = ? AND ctx_nsid = ? "
				     + "AND ctx_name = ? AND param = ?;");
	stmt.setString(4,context_param_ser);

      } // end else

      stmt.setInt(1,uid);
      stmt.setInt(2,context_nsid);
      stmt.setString(3,context_name);
      rs = stmt.executeQuery();
      int new_seq = SQLUtils.getReturnCountInt(rs,1);

      SQLUtils.shutdown(rs);   // prep for next statement
      rs = null;
      SQLUtils.shutdown(stmt);

      // Insert the new sidebox into the table.
      stmt = conn.prepareStatement("INSERT INTO sbox_deploy (uid, ctx_nsid, ctx_name, param, seq, sbid) "
				   + "VALUES (?, ?, ?, ?, ?, ?);");
      stmt.setInt(1,uid);
      stmt.setInt(2,context_nsid);
      stmt.setString(3,context_name);
      stmt.setString(4,context_param_ser);
      stmt.setInt(5,new_seq);
      stmt.setInt(6,sbid);
      stmt.executeUpdate();

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      MySQLUtils.unlockTables(conn);
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt2);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end addSidebox

  void copyUserConfig(int uid_from, int uid_to) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    Statement stmt2 = null;
    ResultSet rs = null;

    try
    { // get a connection
      conn = getConnection();

      // lock the deploy table
      stmt2 = conn.createStatement();
      stmt2.executeUpdate("LOCK TABLES sbox_deploy WRITE;");

      // start by getting the configuration for the existing user
      stmt = conn.prepareStatement("SELECT ctx_nsid, ctx_name, param, seq, sbid FROM sbox_deploy WHERE uid = ?;");
      stmt.setInt(1,uid_from);
      rs = stmt.executeQuery();
      LinkedList items = new LinkedList();
      while (rs.next())
      { // save in a cheap and simple fashion
	Object[] tmp = new Object[5];
	tmp[0] = rs.getObject(1);
	tmp[1] = rs.getObject(2);
	tmp[2] = rs.getObject(3);
	tmp[3] = rs.getObject(4);
	tmp[4] = rs.getObject(5);
	items.addLast(tmp);

      } // end while

      SQLUtils.shutdown(rs);   // prep for next statement
      rs = null;
      SQLUtils.shutdown(stmt);

      // erase existing config for destination user
      stmt = conn.prepareStatement("DELETE FROM sbox_deploy WHERE uid = ?;");
      stmt.setInt(1,uid_to);
      stmt.executeUpdate();

      // now copy over the configuration
      SQLUtils.shutdown(stmt);
      stmt = conn.prepareStatement("INSERT INTO sbox_deploy (uid, ctx_nsid, ctx_name, param, seq, sbid) "
				   + "VALUES (?, ?, ?, ?, ?, ?);");
      stmt.setInt(1,uid_to);
      while (!(items.isEmpty()))
      { // add the items in, one at a time
	Object[] tmp = (Object[])(items.removeFirst());
	stmt.setObject(2,tmp[0],Types.INTEGER);
	stmt.setObject(3,tmp[1],Types.VARCHAR);
	stmt.setObject(4,tmp[2],Types.VARCHAR);
	stmt.setObject(5,tmp[3],Types.INTEGER);
	stmt.setObject(6,tmp[4],Types.INTEGER);
	stmt.executeUpdate();

      } // end while

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      MySQLUtils.unlockTables(conn);
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt2);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end copyUserConfig

} // end class SideboxOps_mysql
