/*
 * 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 CommunityAdmin extends VeniceServlet
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static final String DELETE_CONFIRM_ATTR = "servlets.CommunityAdmin.delete.confirm";
  private static final String DELETE_CONFIRM_PARAM = "confirm";

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

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

  private CommunityAdminTop makeCommunityAdminTop() throws ServletException
  {
    final String desired_name = "CommunityAdminTop";
    MenuPanelCache cache = MenuPanelCache.getMenuPanelCache(getServletContext());

    if (!(cache.isCached(desired_name)))
    { // create a template and save it off
      CommunityAdminTop template = new CommunityAdminTop();
      cache.saveTemplate(template);

    } // end if

    // return a new copy
    return (CommunityAdminTop)(cache.getNewMenuPanel(desired_name));

  } // end makeCommunityAdminTop

  private EditCommunityProfileDialog makeEditCommunityProfileDialog(SecurityInfo sinf) throws ServletException
  {
    final String desired_name = "EditCommunityProfileDialog";
    DialogCache cache = DialogCache.getDialogCache(getServletContext());

    if (!(cache.isCached(desired_name)))
    { // create a template and save it off
      EditCommunityProfileDialog template = new EditCommunityProfileDialog(sinf);
      cache.saveTemplate(template);

    } // end if

    // return a new copy
    return (EditCommunityProfileDialog)(cache.getNewDialog(desired_name));

  } // end makeEditCommunityProfileDialog

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

  public String getServletInfo()
  {
    String rc = "CommunityAdmin servlet - Administrative functions for communities\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 context
    CommunityContext comm = getCommunityParameter(request,user,true,"top");
    changeMenuCommunity(request,comm);
    setMyLocation(request,"sigadmin?" + request.getQueryString());
    String on_error = "sigadmin?sig=" + comm.getCommunityID();

    if (logger.isDebugEnabled())
      logger.debug("CommunityAdmin/doGet operating on community \"" + comm.getName() + "\" ("
		   + comm.getCommunityID() + ")");

    // now decide what to do based on the "cmd" parameter
    String cmd = getStandardCommandParam(request);
    if (logger.isDebugEnabled())
      logger.debug("CommunityAdmin/doGet command value = " + cmd);

    if (cmd.equals("P"))
    { // "P" = "Edit Profile"
      if (!(comm.canModifyProfile()))
      { // no access - sorry, dude
	logger.error("tried to call up community profile screen without access...naughty naughty!");
	return new ErrorBox("Unauthorized","You do not have access to modify this community's profile.",
			    on_error);

      } // end if

      // construct the edit profile dialog and load it up for use
      EditCommunityProfileDialog dlg = makeEditCommunityProfileDialog(comm.getSecurityInfo());

      try
      { // load the values for this dialog
	dlg.setupDialog(engine,comm);

      } // end try
      catch (DataException de)
      { // we could not load the values because of a data exception
	logger.error("DB error loading community profile: " + de.getMessage(),de);
	return new ErrorBox("Database Error","Database error retrieving profile: " + de.getMessage(),on_error);

      } // end catch
      catch (AccessError ae)
      { // we don't have enough privilege
	logger.error("Access error loading community profile: " + ae.getMessage(),ae);
	return new ErrorBox("Unauthorized","You do not have access to modify this community's profile.",
			    on_error);

      } // end catch

      return dlg;  // display me!

    } // end if ("P" command)

    if (cmd.equals("T"))
    { // "T" = "Set Category"
      if (!(comm.canModifyProfile()))
      { // no access - sorry man
	logger.error("tried to call up community category set screen without access...naughty naughty!");
	return new ErrorBox("Unauthorized","You do not have access to modify this community's profile.",
			    on_error);

      } // end else

      // did they actually send a "set" parameter?
      String p = request.getParameter("set");
      if (!(StringUtil.isStringEmpty(p)))
      { // OK, we're setting the category ID...
	try
	{ // get the new category ID and set it
	  int catid = Integer.parseInt(p);
	  if (!(engine.isValidCategoryID(catid)))
	    throw new NumberFormatException();  // dump the category if it's not valid

	  // change the category ID
	  comm.setCategoryID(catid);

	} // end try
	catch (NumberFormatException nfe)
	{ // we got an invalid category value...
	  return new ErrorBox("Invalid Input","Invalid category ID passed to category browser.",on_error);

	} // end catch
	catch (DataException de)
	{ // unable to update the community properly
	  logger.error("DB error updating community: " + de.getMessage(),de);
	  return new ErrorBox("Database Error","Database error updating community: " + de.getMessage(),
			      on_error);

	} // end catch
	catch (AccessError ae)
	{ // database access error - display an error box
	  logger.error("Access error updating community: " + ae.getMessage(),ae);
	  return new ErrorBox("Access Error",ae.getMessage(),on_error);

	} // end catch

	// now that it's set, go back to the admin menu
	throw new RedirectResult(on_error);

      } // end if (setting category ID)
      else
      { // we're browsing - try and figure out what to display
	try
	{ // get the "go" parameter to see what the current category ID is
	  p = request.getParameter("go");
	  int curr_catid;
	  if (StringUtil.isStringEmpty(p))
	    curr_catid = comm.getCategoryID();
	  else
	    curr_catid = Integer.parseInt(p);

	  if (!(engine.isValidCategoryID(curr_catid)))
	    throw new NumberFormatException();  // dump the category if it's not valid

	  // create the browser panel and let it rip
	  return new CommunityCategoryBrowseData(user,comm,curr_catid);

	} // end try
	catch (NumberFormatException nfe)
	{ // we got an invalid category value...
	  return new ErrorBox("Invalid Input","Invalid category ID passed to category browser.",on_error);

	} // end catch
	catch (DataException de)
	{ // unable to get the categories properly
	  logger.error("DB error browsing categories: " + de.getMessage(),de);
	  return new ErrorBox("Database Error","Database error browsing categories: " + de.getMessage(),
			      on_error);

	} // end catch

      } // end else

    } // end if ("T" command)

    if (cmd.equals("M"))
    { // "M" = Set Membership
      if (!(comm.canModifyProfile()))
      { // no access - sorry man
	logger.error("tried to call up membership set screen without access...naughty naughty!");
	return new ErrorBox("Unauthorized","You do not have access to modify this community's membership.",
			    on_error);

      } // end else

      try
      { // display the conference member list!
	CommunityMembership m = new CommunityMembership(engine,comm);
	m.doCommunityMemberList();
	setMyLocation(request,"sigadmin?sig=" + comm.getCommunityID() + "&cmd=M");
	return m;

      } // end try
      catch (DataException de)
      { // something wrong in the database
	return new ErrorBox("Database Error","Database error listing community members: " + de.getMessage(),
			    on_error);

      } // end catch

    } // end if ("M" command)

    if (cmd.equals("A"))
    { // "A" = "Display Audit Records"
      try
      {
	int offset = 0;
	try
	{ // convert the offset parameter
	  String s_ofs = request.getParameter("ofs");
	  if (!StringUtil.isStringEmpty(s_ofs))
	    offset = Integer.parseInt(s_ofs);

	} // end try
	catch (NumberFormatException nfe)
	{ // if it's untranslatable, set it at 0
	  offset = 0;

	} // end catch

	// generate the lists
	List audit_list = comm.getAuditRecords(offset,engine.getNumAuditRecordsPerPage());
	int audit_count = comm.getAuditRecordCount();

	// return the audit viewer
	return new AuditDataViewer(engine,audit_list,offset,audit_count,"Audit Records for Community \""
				   + comm.getName() + "\"","sigadmin?sig=" + comm.getCommunityID()
				   + "&cmd=A&ofs=%");

      } // end try
      catch (AccessError ae)
      { // you don't have access
	return new ErrorBox("Access Error",ae.getMessage(),on_error);

      } // end catch
      catch (DataException de)
      { // something wrong in the database
	return new ErrorBox("Database Error","Database error getting audit records: " + de.getMessage(),
			    on_error);

      } // end catch

    } // end if ("A" command)

    if (cmd.equals("DEL"))
    { // "DEL" = "Delete Community" (requires a confirmation)
      if (!(comm.canDelete()))
      { // we can't delete the community, so what are we doing here?
	logger.error("you can't delete the community - not gonna do it, wouldn't be prudent");
	return new ErrorBox("Access Error","You do not have permission to delete this community.",on_error);

      } // end if

      if (ConfirmBox.isConfirmed(request,DELETE_CONFIRM_ATTR,DELETE_CONFIRM_PARAM))
      { // we are confirmed - delete the community!
	try
	{ // delete the community!
	  comm.delete();

	} // end try
	catch (DataException de)
	{ // something wrong in the database
	  logger.error("Database error deleting community: " + de.getMessage(),de);
	  return new ErrorBox("Database Error","Database error deleting community: " + de.getMessage(),
			      on_error);

	} // end catch
	catch (AccessError ae)
	{ // access error changing the community values
	  logger.error("Access error deleting community: " + ae.getMessage(),ae);
	  return new ErrorBox("Access Error",ae.getMessage(),on_error);

	} // end catch

	// the community is now GONE - there's noplace else to go but back to the Top page
	throw new RedirectResult("top");

      } // end if
      else
      { // throw up a confirm box to let the user think it over
	String message = "You are about to permanently delete the \"" + comm.getName() + "\" community, "
	               + "including all conferences and other resources it contains! Are you sure you want "
	               + "to do this?";
	return new ConfirmBox(request,DELETE_CONFIRM_ATTR,DELETE_CONFIRM_PARAM,"Delete Conference",message,
			      on_error + "&cmd=DEL",on_error);

      } // end else

    } // end if ("DEL" command)

    // all unknown requests get turned into menu display requests
    if (!(comm.canAdministerCommunity()))
    { // no access - sorry buddy
      logger.error("tried to call up community admin menu without access...naughty naughty!");
      return new ErrorBox("Access Error","You do not have access to administer this community.",on_error);

    } // end if

    // create a community administration menu
    CommunityAdminTop menu = makeCommunityAdminTop();
    menu.setCommunity(comm);
    return menu;

  } // end doVeniceGet

  protected VeniceContent doVenicePost(HttpServletRequest request, VeniceEngine engine,
				       UserContext user, RenderData rdat)
    throws ServletException, IOException, VeniceServletResult
  {
    // get the community context
    CommunityContext comm = getCommunityParameter(request,user,true,"top");
    changeMenuCommunity(request,comm);
    String on_error = "sigadmin?sig=" + comm.getCommunityID();
    setMyLocation(request,on_error);

    if (logger.isDebugEnabled())
      logger.debug("CommunityAdmin/doPost operating on community \"" + comm.getName() + "\" ("
		   + comm.getCommunityID() + ")");

    // now decide what to do based on the "cmd" parameter
    String cmd = getStandardCommandParam(request);
    if (logger.isDebugEnabled())
      logger.debug("CommunityAdmin/doPost command value = " + cmd);

    if (cmd.equals("P"))
    { // "P" = "Edit Profile"
      if (!(comm.canModifyProfile()))
      { // no access - sorry, dude
	logger.error("tried to call up community profile screen without access...naughty naughty!");
	return new ErrorBox("Unauthorized","You do not have access to modify this community's profile.",
			    on_error);

      } // end if

      // construct the edit profile dialog and load it up for use
      EditCommunityProfileDialog dlg = makeEditCommunityProfileDialog(comm.getSecurityInfo());
      dlg.setupDialogBasic(engine,comm);

      if (dlg.isButtonClicked(request,"cancel"))
	throw new RedirectResult(on_error);  // go back - they canceled out

      if (dlg.isButtonClicked(request,"update"))
      { // begin updating the community's contents
	dlg.loadValues(request);  // do value loading now

	try
	{ // attempt to change the community profile now...
	  dlg.doDialog(comm);

	  // now jump back to the main menu
	  clearMenu(request);
	  throw new RedirectResult(on_error);

	} // end try
	catch (ValidationException ve)
	{ // simple validation exception
	  if (logger.isDebugEnabled())
	    logger.debug("validation failure: " + ve.getMessage());
	  dlg.resetOnError(comm,ve.getMessage() + " Please try again.");

	} // end catch
	catch (DataException de)
	{ // database error updating the community values
	  logger.error("DB error updating community: " + de.getMessage(),de);
	  return new ErrorBox("Database Error","Database error updating community: " + de.getMessage(),
			      on_error);

	} // end catch
	catch (AccessError ae)
	{ // access error changing the community values
	  logger.error("Access error updating community: " + ae.getMessage(),ae);
	  return new ErrorBox("Access Error",ae.getMessage(),on_error);

	} // end catch

	return dlg;

      } // end if ("update" pressed)

      // what the hell was that button?
      logger.error("no known button click on CommunityAdmin.doPost, cmd=P");
      return new ErrorBox("Internal Error","Unknown command button pressed",on_error);

    } // end if ("P" command)

    if (cmd.equals("M"))
    { // "M" = Modify Community Membership
      on_error += "&cmd=M";
      setMyLocation(request,on_error);

      if (!(comm.canModifyProfile()))
      { // no access - sorry, dude
	logger.error("tried to call up community membership screen without access...naughty naughty!");
	return new ErrorBox("Unauthorized","You do not have access to modify this community's membership.",
			    on_error);

      } // end if

      if (isImageButtonClicked(request,"update"))
      { // the "update" command that changes all the security levels
	HashMap org_vals = new HashMap();
	HashMap new_vals = new HashMap();
	try
	{ // retrieve all parameters and filter them for the levels
	  Enumeration p_names = request.getParameterNames();
	  while (p_names.hasMoreElements())
	  { // examine each parameter name in turn
	    String p_name = (String)(p_names.nextElement());
	    if (p_name.startsWith("zxcur_"))
	    { // this is a current value (from a hidden field)
	      int uid = Integer.parseInt(p_name.substring(6));
	      int level = Integer.parseInt(request.getParameter(p_name));
	      org_vals.put(new Integer(uid),new Integer(level));

	    } // end if
	    else if (p_name.startsWith("zxnew_"))
	    { // this is a new value (from a dropdown list box)
	      int uid = Integer.parseInt(p_name.substring(6));
	      int level = Integer.parseInt(request.getParameter(p_name));
	      new_vals.put(new Integer(uid),new Integer(level));

	    } // end else if

	  } // end while

	  if (org_vals.size()!=new_vals.size())
	  { // we read the wrong number of parameters - this is bad
	    logger.error("level parameters mismatch (" + org_vals.size() + " old, " + new_vals.size()
                         + " new)");
	    return new ErrorBox(null,"Invalid parameters.",on_error);

	  } // end if

	} // end try
	catch (NumberFormatException nfe)
	{ // error converting the parameters
	  logger.error("got number format exception parsing a level parameter");
	  return new ErrorBox(null,"Invalid parameter conversion.",on_error);

	} // end catch

	try
	{ // loop through the hashmaps and set the value
	  Iterator it = org_vals.keySet().iterator();
	  while (it.hasNext())
	  { // extract the UID, old level, and new level
	    Integer uid = (Integer)(it.next());
	    Integer org_level = (Integer)(org_vals.get(uid));
	    Integer new_level = (Integer)(new_vals.get(uid));
	    if (new_level==null)
	    { // whoops
	      logger.error("new community level not found for uid " + uid.intValue());
	      return new ErrorBox(null,"Invalid new level parameter.",on_error);

	    } // end if

	    int new_level_x = new_level.intValue();
	    if (new_level_x==0)
	      new_level_x = -1;

	    // call down to set the membership level
	    if (org_level.intValue()!=new_level_x)
	    { // we need to reset the community membership
	      if (logger.isDebugEnabled())
		logger.debug("resetting community member level for uid " + uid.intValue() + "(old = "
			     + org_level.intValue() + ", new = " + new_level_x + ")");
	      comm.setMembership(uid.intValue(),new_level_x);

	    } // end if

	  } // end while

	} // end try
	catch (AccessError ae)
	{ // some sort of access error - display an error dialog
	  logger.error("AccessError on comm.setMembership: " + ae.getMessage(),ae);
	  return new ErrorBox("Access Error",ae.getMessage(),on_error);

	} // end catch
	catch (DataException de)
	{ // database error creating the conference
	  logger.error("DataException on comm.setMembership: " + de.getMessage(),de);
	  return new ErrorBox("Database Error","Database error setting memberships: " + de.getMessage(),
			      on_error);

	} // end catch

	// trap back to the community membership display
	throw new RedirectResult(on_error);

      } // end if ("update" clicked)

      if (   isImageButtonClicked(request,"search") || isImageButtonClicked(request,"previous")
	  || isImageButtonClicked(request,"next"))
      { // create the new dialog box
	CommunityMembership m = new CommunityMembership(engine,comm);

	try
	{ // perform the search!
	  m.doSearch(request);

	} // end try
	catch (ValidationException ve)
	{ // validation error - throw it back to the user
	  return new ErrorBox(null,ve.getMessage() + " Please try again.",on_error);

	} // end catch
	catch (AccessError ae)
	{ // some sort of access error - display an error dialog
	  return new ErrorBox("Access Error",ae.getMessage(),on_error);

	} // end catch
	catch (DataException de)
	{ // database error creating the conference
	  return new ErrorBox("Database Error","Database error searching for users: " + de.getMessage(),
			      on_error);

	} // end catch

	return m;

      } // end if (search function clicked)

      // we don't know what button was pressed
      logger.error("no known button click on CommunityAdmin.doPost, cmd=M");
      return new ErrorBox("Internal Error","Unknown command button pressed",on_error);

    } // end if ("M" command)

    // on unknown command, redirect to the GET function
    return doVeniceGet(request,engine,user,rdat);

  } // end doVenicePost

} // end class CommunityAdmin
