/*
 * Aleph Toolkit
 *
 * Copyright 1997, Brown University, Providence, RI.
 * 
 *                         All Rights Reserved
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose other than its incorporation into a
 * commercial product is hereby granted without fee, provided that the
 * above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Brown University not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.
 * 
 * BROWN UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ANY
 * PARTICULAR PURPOSE.  IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE FOR
 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package aleph.dir.hybrid;

import java.util.Hashtable;
import aleph.GlobalObject;
import aleph.AlephDebug;
import aleph.PE;
import aleph.dir.DirectoryManager;

public class HybridDirectory extends DirectoryManager {

  Hashtable statusTable = new Hashtable();      
  Hashtable directionTable = new Hashtable();

  static final boolean DEBUG = false;
  static HybridDirectory manager;

  public HybridDirectory() {
    manager = this;
    AlephDebug.register("Hybrid Directory", this);
    if (DEBUG)
      AlephDebug.println(this.toString());
  }

  public synchronized void newObject(GlobalObject key, Object object,
      String hint) {
    statusTable.put(key, new Status(object, true, false));
    directionTable.put(key, PE.thisPE());
  }
  
  public synchronized Object open(GlobalObject key, String mode) {
    Status status = (Status) statusTable.get(key);

    if (status == null) {                       // object unknown
      status = new Status(null, false, true);
      statusTable.put(key, status);
      new FindMessage(key).send(key.getHome());
    } else if (status.valid == false && status.requested == false) {         
      // object elsewhere, hasn't been requested yet
      status.requested = true;
      new FindMessage(key).send(key.getHome());
    }

    status.waiters++;
    while ((! status.valid) || status.busy) {
      try { wait(); } catch (InterruptedException e) {}
    }
    status.busy = true;
    status.waiters--;
    return status.object;
  }

  public synchronized void release(GlobalObject key) {
    Status status = (Status) statusTable.get(key);
    status.busy = false;
    if (status.waiters > 0) {           // local suitors
      notifyAll();
    }
    else if (status.pending != null) {  // remote suitor
      new GrantMessage(key, status.object).send(status.pending);
      status.pending = null;
      status.object = null;
      status.valid = false;
    }
  }

  public String getLabel() {
    return "Hybrid Directory Manager";
  }

  public synchronized String toString() {
    return "Status table: " + statusTable + "\nDirection table: " +
      directionTable;
  }
  
  synchronized void handleFindMessage(GlobalObject key, PE from) {
    PE direction = (PE) directionTable.get(key);
    new ReleaseMessage(key, from).send(direction);
    directionTable.put(key, from);
  }
  
  synchronized void handleGrantMessage(GlobalObject key, Object object) {
    Status status = (Status) statusTable.get(key);
    status.object = object;
    status.valid = true;
    status.requested = false;
    notifyAll();
  }
  
  synchronized void handleReleaseMessage(GlobalObject key, PE suitor) {
    Status status = (Status) statusTable.get(key);
    if (status.busy || (status.waiters > 0)) {  // object still in use
      status.pending = suitor;
    }
    else {
      new GrantMessage(key, status.object).send(suitor);
      status.object = null;
      status.valid = false;
    }
  }
}
