/*
 *  bondFaultDetectionMH.java
 *     @contains an incoming message handler.
 *      See README in this directory for detail.
 *     @author Kyungkoo Jun 
 *     Bond group, CS, Purdue Univ.
 *     created on Nov 14, 2000.
 *
 *  modified by Kyungkoo Jun, Dec. 22, 2000
 *    -- added file logging capability.
 *  modified by Kyungkoo Jun, Dec.4, 2000
 *    -- The agents that monitor each other form
 *       a ring monitoring topology that does not
 *       have the Jellyfish configuration.
 */


package bond.agent.FaultDetection;

import bond.core.*;
import java.util.*;

public class bondFaultDetectionMH
extends bondFaultDetectionExecutable
{

  private LinkedList mqueue;

  public bondFaultDetectionMH(bondFaultStatus fs) {
    super(fs);
    mqueue = new LinkedList();
    thread.setPriority(Thread.MAX_PRIORITY);
    go();
  }

  public void run() {

    bondMessage msg;

    while (!finish) {
      if (getMsgQueueSz() > 0) {
	synchronized (mqueue) {
	  msg = (bondMessage)mqueue.removeFirst();
	}
	try {
	  FaultDetectionPH(msg);
	}
	catch (Exception e) {
	  e.printStackTrace();
	  bondFaultStatus.Log(fs.getMyID(), "exception "+msg.compose(), false);
	  System.exit(0);
	}
      }
      else {
	try {
	  synchronized (this) {
	    this.wait();
	  }
	}
	catch (InterruptedException e) {
	}
      }
    }
  }

  public int getMsgQueueSz() {
    synchronized(mqueue) {
      return mqueue.size();
    }
  }

  public boolean FaultDetectionPH(bondMessage msg) 
  throws Exception
  {
    
    //------> subprotocol check
    String sp = msg.getSubprotocol();
    if (sp == null || !sp.equals("FaultDetection"))
      return false;

    //------> obtain reply shadow
    bondShadow bs;
    bondObject bo = msg.getSender();
    // for debugging
    if (bo == null) {
      System.out.println("bondFaultDetectionMH::should not be null-2");
      System.exit(0);
    }
    if (!(bo instanceof bondShadow)) {
      bs = new bondShadow(bo);
    }
    else {
      bs = (bondShadow)bo;
    }

    //------> "test-msg" 
    if (msg.content.equals("test-msg")) {
      bondMessage rep = msg.createReply("(tell :content fine)");
      
      filelogger.log(fs.getMyID()+" replyTest");

      bs.say(rep, this);
      return true;
    }

    if (msg.content.equals("stop-monitoring")) {

      fs.setMonitoredBy(null);
      bondMessage rep = msg.createReply("(tell :content fine)");

      String candidate = (String)msg.getParameter(":candidate");
      if (candidate != null) {
	fs.setFaulty(candidate, false);
      }
      fs.startMonitorSearcher(false);
      filelogger.log(fs.getMyID()+" ackYouAreOrphan");

      bs.say(rep, this);
      return true;
    }
    
    if (msg.content.equals("rejoin-msg")) {
      if (fs.amIorphan()) {
	fs.startMonitorSearcher(true);
      }
      return true;
    }
    //------> "info-msg"
    if (msg.content.equals("info-msg")) {

      String id = (String)msg.getParameter(":ID");
      Integer status = (Integer)msg.getParameter(":status");
      Vector remains = (Vector)msg.getParameter(":remains");
      String source = (String)msg.getParameter(":source");
      // for debugging
      if (id == null ||
	  status == null) { // remains can be null
	System.out.println("bondFaultDetectionMH:: should not be null-3");
	System.exit(0);
      }
      fs.startInfoHandler(id, status, remains, source);
      bondMessage rep = msg.createReply("(tell :content received)");

      bs.say(rep, this);
      //fs.startInfoHandler(id, status, remains, source, rep, bs);
      return true;
    }
    //------> "testme-msg" from faulty-free neighbor
    if (msg.content.equals("testme-msg")) {
      String id = (String)msg.getParameter(":ID");
      
      // for debugging, 
      if (id == null) {
	System.out.println("bondFaultDetectionMH::should not be null-1");
	System.exit(0);
      }

      //-------> can monitor you
      if ( fs.getFaultStatus(id) != null &&
	   fs.startMonitor(id, false)) {
	bondFaultStatus.Log(fs.getMyID(), "will monitor(for testme) "+id, false);
	bondMessage rep = msg.createReply("(tell :content i-will-monitor-you)");
	rep.setParameter(":ID", fs.getMyID());

	filelogger.log(fs.getMyID()+" acceptMonitoring");

	bs.say(rep, this);
      }
      //------> can't, I'm busy
      else {
	bondFaultStatus.Log(fs.getMyID(), "decline monitor request"+id, false);
	bondMessage rep = msg.createReply("(tell :content i-am-busy)");
	rep.setParameter(":ID", fs.getMyID());

	filelogger.log(fs.getMyID()+" denyMonitoring");

	bs.say(rep, this);
      }
      return true;
    }
    //------> "testmejoin-msg" from fault-recovered or inited neighbor
    if (msg.content.equals("testmejoin-msg")) {

      String id = (String)msg.getParameter(":ID");

      // for debugging
      if (id == null) {
	System.out.println("bondFaultDetectionMH::should not be null-2");
	System.exit(0);
      }

      // remove j's test
      if (fs.beMonitoredBy(id)) {
	fs.setMonitoredBy(null);  
      }

      //------> can monitor you
      if (fs.startMonitor(id, true)) {
	bondFaultStatus.Log(fs.getMyID(), "will monitor "+id, false);

	// event counter update
	//fs.setFaulty(id, false); // set as fault-free

	bondMessage rep = msg.createReply("(tell :content i-will-monitor-you)");
	rep.setParameter(":ID", fs.getMyID());
	rep.setParameter(":status", fs.getStatus());

	filelogger.log(fs.getMyID()+" acceptJoin");

	bs.say(rep, this);


	// disseminate
	fs.disseminateInfo(id);

      }
      //-----> can't, I'm busy
      else {
	bondFaultStatus.Log(fs.getMyID(), "decline monitor request"+id, false);
	bondMessage rep = msg.createReply("(tell :content i-am-busy)");
	rep.setParameter(":status", fs.getStatus());
	rep.setParameter(":ID", fs.getMyID());

	filelogger.log(fs.getMyID()+" denyJoin");

	bs.say(rep, this);
      }

      //------> am I orphan?
      if (fs.amIorphan())
      	fs.startMonitorSearcher(false);

      return true;
    }
    if (msg.content.equals("joinnotest")) {
      return true;
    }

    return false;
  }


  public void say(bondMessage m, bondObject sender) {

    synchronized (mqueue) {
      mqueue.addLast(m);
    }
    synchronized (this) {
      this.notify();
    }

  }

}
