// bergmark - telephony project - Spring 1999

package cnrg.itx.gtwy.pbx;

// PBXSignaling is the PBX driver.  It executes all the JTAPI functions.
// It keeps track of which Dialers are in progress and which "Calls"
// correspond to which desktop signaling sequences.  PBXSignaling methods
// are invoked by PBXSignalingServer, depending on the type of packet
// received.  PBXSignaling dispatches a separate thread to handle
// telephone dialing, because it is important to return as quickly as
// possible to the PBXSignalingServer, who is handling requests from
// one or more gateways.  The dispatched thread suspends until a hangup
// request comes in.

// PBXSignaling sends three kinds of packets back to the gateway:
// ACCEPT, BUSY, and REJECT result packets (via Dialer).

// This code is based on that written for CS519 in Fall 1998 by
// Madhav Ranjan and Abhideep Singh.  

import java.io.*;
import java.util.*;
import java.net.*;
import cnrg.itx.ds.*;  
import cnrg.itx.signal.*;       // for InvitePacket
import cnrg.itx.gtwy.Line;      // for Line object

// Telephony stuff
import javax.telephony.Provider;

/**
 * The Signaling Component that controls the PBX.
 * This component only handles calls to the Telephone Network 
 * There is one PBXSignaling object per PBXSignalingServer object
 */
public class PBXSignaling{

    private static final String ME = "PBXSignaling: ";
    private boolean LOG = false;
    private JtapiHandler myJtapiHandler = null;

    // Structures to keep track of what calls are in progress
    // (A hashtable might be better)
    private static final int MAXLINES = 4;  // dialogic lines
    // This is a vector of Dialers.  Dialer at index "ind" using line "ind"
    private Vector theCallsInProgressVector;


    /**
     * Creates a PBXSignaling object and gets a provider
     * @param log_option true for logging to sysout
     * @param l To authenticate with Tserver
     * @param p To authenticate with Tserver
     * @exception PBXSignalingException means we could not get a Provider
     */

    public PBXSignaling (boolean log_option, String l, String p) 
    throws PBXSignalingException {

       LOG = log_option;

       // Get a provider
       myJtapiHandler = new JtapiHandler(log_option,l,p);

       // If above did not throw an exception, then we've got one
       if (LOG) System.out.println(ME + " MASC Ready.");

       // Initialize the calls in progress
       theCallsInProgressVector = new Vector (MAXLINES);
       for (int i=0; i<MAXLINES; i++) 
	  theCallsInProgressVector.addElement(null);
    }

   /**
    * This method returns our Jtapi Provider, which is non-null
    */

   protected Provider getProvider () {
      return myJtapiHandler.provider;
   }

   /**
    * method handles a Dial INVITE packet.  The InivtePacket
    * contains the telephone number we are to dial, as well
    * as the Gateway line being used to dial that.  
    * @param sp the InvitePacket
    * @param gateSocket - the socket over which this invitePacket came in
    *    This is null if we are running STANDALONE
    * @exception PBXSignalingException is thrown if Dialer can't be made
    * or if the PBX Provider died and cannot be resuscitated
    */
  protected void handleDialInvite ( InvitePacket sp, Socket gateSocket ) 
  throws PBXSignalingException {
     if (LOG) System.out.println (ME + "handleDialInvite has been called");

     // Make sure our provider is alive (throw exception if not)
     if ( ! checkProvider() )
        throw new PBXSignalingException (ME + "could not finish " +
	   "handling the Dial invitation because our PBX Provider died " +
	   "and could not be resuscitated");

     // Look up the calling and called telephone numbers
     // The source is the Line in the Custom object and the
     // destination is the destUID in the InvitePacket.
     Line gline = (Line)sp.getCustomObject();
     String from = gline.getPhoneNumber();  // Should be a Gateway line
     String to = (sp.getDestination()).toString();

     // Create a Dialer
     Dialer myDialer = null;
     try {
        myDialer = new Dialer (this, LOG, sp, from, to, gateSocket);
     } catch ( DialingException e ) {
        e.printStackTrace();
        throw new PBXSignalingException (ME+"Could not get a Dialer because " +
	   e.toString() );
     } 
     int m = gline.getLineNumber();
     theCallsInProgressVector.insertElementAt(myDialer,m);
     if (LOG) System.out.println (ME + "has a Dialer" + myDialer +
       ", number " + m );
     // Dialer attempts the call from a separate thread
     myDialer.start();
     if (LOG) System.out.println (ME + "finish handleDialInvite" );
  }

  /**
   * This method handles a Hangup request (SigPacket.isHangup() true).
   * @param  sp  InvitePacket containing the HANGUP request
   */
  protected void handleHangupInvite ( InvitePacket sp ) {
     if (LOG) System.out.println ( ME + "handleHangupInvite has been called");

     // lookup which Dialer corresponds to this phoning sequence
     //   map m to 0..MAXLINES-1, the line from which Dialer is calling
     int  m = ((Line)sp.getCustomObject()).getLineNumber();
     if (LOG) System.out.println ( ME + "line ID number is " + m );
     hangupThisLine ( m );

  }

  /**
   * This method is called when PBX Signaling is being shut down.
   */
  protected void stopDialers () {

     // make all Dialers in theCallsInProgressVector hangup
     for (int m=0; m < MAXLINES; m++) 
	hangupThisLine ( m );
     
  }



 //==========  Private Methods for PBXSignaling ================

 /**
  * This is a utility method to stop the Dialer calling from a given
  * gateway line.
  * @param  m   a Line ID
  */
 private void hangupThisLine ( int m ) {
	Dialer d = (Dialer) theCallsInProgressVector.elementAt(m);
	if (d != null) {
	   if ( LOG ) System.out.println ( ME +
	   " making Dialer " + d + " hangup");
	   if (d.callInProgress()) 
	      if ( checkProvider() ) d.resume();  // hangup and die
	   else 
	      d.stop();    // NO Hangup is necessary
           // Delete this thread from the list
           theCallsInProgressVector.removeElementAt(m);
           theCallsInProgressVector.insertElementAt(null,m);
	   if ( LOG ) System.out.println ( ME + " Dialer " + d + " destroyed");
	}
 }

 /**
  * This method just checks that our provider hasn't quit on us
  * @returns true if Provider is still running or was successfully
  * put back into service.
  */
 public boolean checkProvider () {

     Provider p = getProvider();

     // Since we have a JtapiHandler, we must have a non-null Provider
     // If this is not the case, something has gone terribly wrong
     if ( p == null ) {
	System.out.println ( ME +
           "impossible condition in checkProvider: Provider is null");
	return false;
     }

     // See if our provider is still alive
     if ( p.getState() != Provider.IN_SERVICE ) {
	System.out.println (ME+"\n\n#&*$ Jtapi Provider died on us!");
	try {
	   myJtapiHandler.kickProvider();
	} catch ( PBXSignalingException e ) {
	   return false;
	}
     }
     return true;
 }

}
