package senseiTests.randomCounter.rmi;

import sensei.middleware.gms.GroupHandler;
import sensei.middleware.gms.GroupMemberImpl;
import sensei.middleware.gms.Message;
import sensei.middleware.gms.View;
import sensei.middleware.gmns.GroupHandlerFactory;
import sensei.middleware.gmns.GroupMembershipNamingService;
import sensei.middleware.gmns.GroupHandlerFactoryImpl;

import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;

public class Server extends GroupMemberImpl
{
  public Server(GroupMembershipNamingService service, String groupName) throws Exception
  {
    randomCounterServer = new RandomCounterServerImpl();
    GroupHandlerFactory ghFactory=new GroupHandlerFactoryImpl(theGroupMember()).theGroupHandlerFactory();
    if (service.findAndJoinGroup(groupName, ghFactory, "noname", randomCounterServer)==null)
    {
      System.out.println("I couldn't join the group");
      System.exit(0);
    }
  }

  int generateNumber() throws RemoteException
  {
    System.out.println("Sending request to generate number1...");
    int messageId = 0;
    synchronized(this){messageId=++currentMessageId;}

    groupHandler.castMessage(new RandomCounterMessageImpl(set.getRandom(), messageId));

    //now, block until the request is received
    while(processedMessageId!=messageId)
    {
      synchronized(this)
      {
        try{wait();}catch(InterruptedException ie){}
      }
    }
    processedMessageId=0;
    int ret = lastGeneratedNumber;
    synchronized(this){notifyAll();}
    System.out.println("The number generated on this request is: " + lastGeneratedNumber);
    return ret;
  }


  /**
    * The member receives a group message to be processed
    * @param message The message to process
    **/
  public void processCastMessage(int sender, Message msg)
  {
    System.out.println("Received request to generate number");
    RandomCounterMessage message = (RandomCounterMessage) msg;
    lastGeneratedNumber = set.getFreeNumber(message.randomNumber);
    System.out.println("Number generated: " + lastGeneratedNumber);
    if (sender==memberId)
    {
      processedMessageId=message.messageId;
      synchronized(this){notifyAll();}
      //wait to the calling thread as well
      while(processedMessageId!=0)
      {
        synchronized(this)
        {
          try{wait();}catch(InterruptedException ie){}
        }
      }
    }

  }

  /**
    * The member is accepted in the group, and receives its identity and the
    *  first view. It receives as well the group handler to use for communications
    **/
  public void memberAccepted(int identity, GroupHandler handler, View theView)
  {
    System.err.println("Server in group, starting serving requests");
    memberId = identity;
    groupHandler = handler;
  }

  /**
    * The member is excluded from the group, because it has requested it or because
    * it's considered to be faulty.
    **/
  public void excludedFromGroup()
  {
    System.err.println("Server excluded from its own group. Exiting...");
    System.exit(0);
  }

  public void changingView(){}
  public void installView(View theView){}
  public void processPTPMessage(int sender, Message msg){}

  RandomCounterServer randomCounterServer;
  SetNumbers set = new SetNumbers();
  GroupHandler groupHandler;
  int lastGeneratedNumber, processedMessageId, currentMessageId;
  int memberId;

  //*************************************************************************************//
  //**************************** INNER CLASSES *******************************************//
  //*************************************************************************************//

  public class RandomCounterServerImpl extends UnicastRemoteObject implements RandomCounterServer
  {
    public RandomCounterServerImpl() throws Exception{}
    public int getNumber() throws RemoteException
    {
      return generateNumber();
    }
  }

  static public class RandomCounterMessageImpl extends RandomCounterMessage
  {
    public RandomCounterMessageImpl (int random, int id) {randomNumber=random;messageId=id;}
  }


}