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

/**
 * The information that represents a single security "scope" within Venice.  A security "scope"
 * defines two subranges of the 16-bit range for security levels, the "lowband" range and the "highband"
 * range.  Scopes are numbered from 0 to 15; for any pair of scopes, <EM>x</EM> and <EM>y</EM>, if
 * <EM>x&lt;y</EM>, that means that all values in the lowband of scope <EM>x</EM> are less than all
 * values in the lowband of scope <EM>y</EM>, and all values in the highband of scope <EM>x</EM> are
 * greater than all values in the highband of scope <EM>y</EM>.  In general, security scopes with
 * higher indexes are used for more specific objects.  The system itself, for instance, uses scope 0;
 * communities use scope 3; conferences and other objects inside communities use scope 6.<P>
 * Three levels are defined that are outside the scope system: the "not in list" value, which is defined
 * as the (integer) value -1; the "no access" value, which is greater than any value in the lowband or
 * highband of any scope; and the "unrestricted user" value, which is greater than any value in the lowband
 * of any scope, but less than any value in the highband of any scope.  (Administrator levels are generally
 * defined within the highband of their scope, allowing administrators to exert control over any enclosed
 * objects as well.)
 *
 * @author Eric J. Bowersox &lt;erbo@silcom.com&gt;
 * @version X
 */
public final class ScopeInfo implements Cloneable, Comparable
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static final int[] LB_LOW =   // Scope values, lowband, low end of range
      { 0, 2000, 4000, 6000, 8000, 10000, 12000, 14000, 16000, 18000, 20000, 22000, 24000, 26000, 28000,
	30000 };
  private static final int[] LB_HIGH =  // Scope values, lowband, high end of range
      { 1999, 3999, 5999, 7999, 9999, 11999, 13999, 15999, 17999, 19999, 21999, 23999, 25999, 27999, 29999,
	31999 };
  private static final int[] HB_LOW =   // Scope values, highband, low end of range
      { 63000, 61000, 59000, 57000, 55000, 53000, 51000, 49000, 47000, 45000, 43000, 41000, 39000, 37000,
	35000, 33000 };
  private static final int[] HB_HIGH =  // Scope values, highband, high end of range
      { 64999, 62999, 60999, 58999, 56999, 54999, 52999, 50999, 48999, 46999, 44999, 42999, 40999, 38999,
	36999, 34999 };

  /**
   * The defined "not there" value.
   */
  public static final int L_NOT_THERE = -1;

  /**
   * The defined "unrestricted user" value.
   */
  public static final int L_UNRESTRICTED = 32500;

  /**
   * The defined "no access" value.
   */
  public static final int L_NO_ACCESS = 65500;

  /**
   * A value which represents the difference between the low end and high end of a single band.
   */
  public static final int BAND_WIDTH = 1999;

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

  int scope;                 // the scope value

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

  /**
   * Creates a new <CODE>ScopeInfo</CODE> object.
   *
   * @param scope The scope value.
   * @exception java.lang.IndexOutOfBoundsException The given scope value is out of range.  It must be
   *            between 0 and 15.
   */
  public ScopeInfo(int scope)
  {
    if ((scope<0) || (scope>=LB_LOW.length))
      throw new IndexOutOfBoundsException("invalid scope value");
    this.scope = scope;

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Overrides from class Object
   *--------------------------------------------------------------------------------
   */

  /**
   * Tests this object for equality with another object.  Two <CODE>ScopeInfo</CODE> objects are equivalent
   * iff the scopes that they enclose are equal.
   *
   * @param obj The other object to compare to this one.
   * @return <CODE>true</CODE> if the two objects are equal.
   */
  public boolean equals(Object o)
  {
    if ((o==null) || !(o instanceof ScopeInfo))
      return false;
    ScopeInfo other = (ScopeInfo)o;
    return (scope==other.scope);

  } // end equals

  /**
   * Returns a hash code that can be used as an index for this object in a hash table.  In keeping
   * with the definition of <CODE>equals</CODE>, a <CODE>ScopeInfo</CODE>'s hash code is its scope.
   *
   * @return The hash code for this object.
   * @see #equals(java.lang.Object)
   */
  public int hashCode()
  {
    return scope;

  } // end hashCode

  /**
   * Returns a "debugging" string equivalent for this <CODE>ScopeInfo</CODE>.
   *
   * @return The string equivalent for this <CODE>ScopeInfo</CODE>.
   */
  public String toString()
  {
    StringBuffer buf = new StringBuffer("{ScopeInfo(");
    buf.append(scope).append("): ranges [").append(LB_LOW[scope]).append('-').append(LB_HIGH[scope]);
    buf.append("], [").append(HB_LOW[scope]).append('-').append(HB_HIGH[scope]).append("]}");
    return buf.toString();

  } // end toString

  /*--------------------------------------------------------------------------------
   * Implementations from interface Comparable
   *--------------------------------------------------------------------------------
   */

  /**
   * Compares one <CODE>ScopeInfo</CODE> object to another.  A <CODE>ScopeInfo</CODE> is logically less
   * than another one if its scope is less than the other one's scope.
   *
   * @param obj The other <CODE>ScopeInfo</CODE> object to compare to.
   * @return A value less than 0 if this <CODE>ScopeInfo</CODE> is logically "less than" the specified one; a
   *         value greater than 0 if this <CODE>ScopeInfo</CODE> is logically "greater than" the other one;
   *         0 if the two are equal.
   * @exception java.lang.ClassCastException If <CODE>obj</CODE> is not a <CODE>ScopeInfo</CODE> object.
   */
  public int compareTo(Object o)
  {
    if (o==null)
      throw new NullPointerException("can't compare to a null object");
    ScopeInfo other = (ScopeInfo)o;  // may throw ClassCastException - that's OK
    return scope - other.scope;

  } // end compareTo

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

  /**
   * Returns the scope associated with this <CODE>ScopeInfo</CODE> object.
   *
   * @return The scope associated with this <CODE>ScopeInfo</CODE> object.
   */
  public final int getScope()
  {
    return scope;

  } // end getScope

  /**
   * Returns the lower limit of the lowband of this scope.
   *
   * @return The lower limit of the lowband of this scope.
   */
  public final int getLowBandLow()
  {
    return LB_LOW[scope];

  } // end getLowBandLow

  /**
   * Returns the upper limit of the lowband of this scope.
   *
   * @return The upper limit of the lowband of this scope.
   */
  public final int getLowBandHigh()
  {
    return LB_HIGH[scope];

  } // end getLowBandHigh

  /**
   * Returns the lower limit of the highband of this scope.
   *
   * @return The lower limit of the highband of this scope.
   */
  public final int getHighBandLow()
  {
    return HB_LOW[scope];

  } // end getHighBandLow

  /**
   * Returns the upper limit of the highband of this scope.
   *
   * @return The upper limit of the highband of this scope.
   */
  public final int getHighBandHigh()
  {
    return HB_HIGH[scope];

  } // end getHighBandHigh

  /**
   * Computes a new level within the scope.
   *
   * @param highband <CODE>true</CODE> to return a value within the highband of this scope, <CODE>false</CODE>
   *                 to return a value within a lowband of this scope.
   * @param offset Offset from the lower limit of the band.  If this value is negative, it is viewed as
   *               an offset from the upper limit of the band.
   * @return The computed level.
   * @exception java.lang.IllegalArgumentException If the computed level would be outside this scope.
   */
  public final int getLevel(boolean highband, int offset)
  {
    int rc;  // return from this method

    if (highband)
    { // it's in the highband
      if (offset<0)
      { // negative offset from high end of highband
	rc = HB_HIGH[scope] + offset;
	if (rc<HB_LOW[scope])
	  throw new IllegalArgumentException("value out of scope");

      } // end if
      else
      { // positive offset from low end of highband
	rc = HB_LOW[scope] + offset;
	if (rc>HB_HIGH[scope])
	  throw new IllegalArgumentException("value out of scope");

      } // end else

    } // end if (highband)
    else
    { // it's in the lowband
      if (offset<0)
      { // negative offset from high end of lowband
	rc = LB_HIGH[scope] + offset;
	if (rc<LB_LOW[scope])
	  throw new IllegalArgumentException("value out of scope");

      } // end if
      else
      { // positive offset from low end of lowband
	rc = LB_LOW[scope] + offset;
	if (rc>LB_HIGH[scope])
	  throw new IllegalArgumentException("value out of scope");

      } // end else

    } // end else (lowband)

    return rc;

  } // end getLevel

  /**
   * Returns <CODE>true</CODE> if the specified level represents a value within this scope,
   * <CODE>false</CODE> if not.
   *
   * @param value The level value to be tested.
   * @return <CODE>true</CODE> if the specified level represents a value within this scope.
   */
  public final boolean isInScope(int value)
  {
    if ((value>=LB_LOW[scope]) && (value<=LB_HIGH[scope]))
      return true;
    if ((value>=HB_LOW[scope]) && (value<=HB_HIGH[scope]))
      return true;
    return false;

  } // end isInScope

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

  /**
   * Returns <CODE>true</CODE> if the specified index represents a valid scope, <CODE>false</CODE> if not.
   *
   * @param s The scope index to be tested.
   * @return <CODE>true</CODE> if the specified index represents a valid scope.
   */
  public static final boolean isValidScope(int s)
  {
    return ((s>=0) && (s<LB_LOW.length));

  } // end isValidScope

  /**
   * Given a level value, returns the index of the scope that would contain it.
   *
   * @param value The level value to be tested.
   * @return The index of the scope that contains this level.  If this value lies outside any scope,
   *         returns -1.
   */
  public static final int getScopeOf(int value)
  {
    if ((value<LB_LOW[0]) || (value>HB_HIGH[0]))
      return -1;  // quick test to eliminate most of the range

    for (int i=0; i<LB_LOW.length; i++)
    { // look in each scope in turn
      if ((value<=LB_HIGH[i]) || (value>=HB_LOW[i]))
	return i;

    } // end for

    return -1;  // not in a scope

  } // end getScopeOf

} // end class ScopeInfo
