package com.cisco.ipphone.sdk.wfapi;

import com.cisco.ipphone.sdk.*;
import java.io.*;
import java.util.*;

import com.cisco.wfapi.WFException;
import com.cisco.wfapi.WFSubsystemContainer;
import com.cisco.wfapi.WFTracingLog;
import com.cisco.wfapi.subsystem.WFSubsystem;
import com.cisco.wfapi.subsystem.WFGenericSubsystem;
import com.cisco.wfapi.subsystem.WFSubsystemException;
import com.cisco.wfapi.subsystem.WFSubsystemConfig;

import com.cisco.cti.util.Condition;
import com.cisco.jtapi.extensions.CiscoTerminal;

import javax.telephony.Terminal;
import javax.telephony.Provider;
import javax.telephony.JtapiPeer;
import javax.telephony.JtapiPeerFactory;
import javax.telephony.ProviderObserver;
import javax.telephony.CallObserver;
import javax.telephony.events.ProvEv;
import javax.telephony.events.ProvInServiceEv;
import javax.telephony.events.CallEv;
import javax.telephony.InvalidArgumentException;

/**
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2003</p>
 * <p>Company: </p>
 * @author not attributable
 * @version 1.0
 */

public class IPPhoneServicesSubsystem extends WFGenericSubsystem {
  static IPPhoneServicesSubsystem instance;  // package accessibility

  private WFTracingLog trace = null;
  private EventThread eventThread = new EventThread();

  Condition conditionInService = new Condition();
  Provider provider;

  static final private String CONFIG_FILE="ipphoneservices.properties";

  private Properties config = new Properties();

  String jtapiServer = "";
  String jtapiUserId = "";
  String jtapiPassword = "";

  String emUserId = "";
  String emPassword = "";

  String callManager = "";
  String callManagerUserId = "";
  String callManagerPassword = "";

  String ldapServer = "";
  int ldapPort = 0;
  String ldapRoot = "";
  String ldapUserId = "";
  String ldapPassword = "";
  String ldapSearchbase = "";

  String devicePrefix = "";

  LDAPProvider lp = null;
  EMProvider emp = null;
  IPAddressProvider ipp = null;

  String[] excludedIds = { jtapiUserId, emUserId };

  public IPPhoneServicesSubsystem() {instance = this;}

  protected void setState(int state)
  {
          super.setState(state);
  }

  class EventThread extends Thread implements CallObserver, ProviderObserver
  {
          public void run()
          {
              try {

                lp = new LDAPProvider(ldapServer, ldapUserId, ldapPassword, ldapPort, ldapRoot);
                ipp = new IPAddressProvider(callManager, callManagerUserId, callManagerPassword);
                emp = new EMProvider(callManager, callManagerUserId, callManagerPassword, emUserId, emPassword);

                trace.trace("Initializing IP Phone Services Jtapi");
                JtapiPeer peer = JtapiPeerFactory.getJtapiPeer(null);
                String providerString = jtapiServer + ";login=" + jtapiUserId + ";passwd=" + jtapiPassword;

                trace.trace("Opening IP Phone Services JTAPI provider connection to " + jtapiServer + "...");
                provider = peer.getProvider(providerString);
                provider.addObserver(this);
                conditionInService.waitTrue();

                // Add observer for each terminal controlled by the supplied appId
                Terminal[] terms = provider.getTerminals();
                for (int i = 0; i < terms.length; i++) {
                  System.out.println("Terminal:"+terms[i].getName());
                  terms[i].addCallObserver(this);
                }
                trace.trace("IP Phone Services JTAPI connection opened.");
                trace.trace("IP Phone Services Subsystem is running");
                setState(WFSubsystem.STATE_IN_SERVICE);
              }
              catch (Exception e) {
                trace.trace("IP Phone Services subsystem failed to start due to JTAPI connection failure.");
                setState(WFSubsystem.STATE_OUT_OF_SERVICE);
              }
          }

          public void providerChangedEvent ( ProvEv [] eventList ) {
            if ( eventList != null ) {
                for ( int i = 0; i < eventList.length; i++ ) {
                        if ( eventList[i] instanceof ProvInServiceEv ) {
                                conditionInService.set ();
                        }
                }
            }
          }

          public void callChangedEvent(CallEv[] ev) {}
  }

  public void init()
          throws WFSubsystemException
  {
          setState(STATE_INITIALIZING);
          super.init();

          initTracing();
          trace.trace("IP Phone Services Subsystem is initializing");
          if (loadConfiguration()) {
            eventThread.start();
          }
  }

  public void destroy()
  {
          eventThread.interrupt();
          setState(STATE_SHUTDOWN);
          trace.trace("IP Phone Services Subsystem is shut down");
  }

  /**
   * 	Initialize tracing object
   */
  private void initTracing()
  {
          WFSubsystemContainer subsysContainer = getContainer();
          trace = subsysContainer.addTracingService("SS_IPPHONESERVICES");
  }

  public String getInfo()
  {
          return "IP Phone Services Subsystem";
  }

  public boolean loadConfiguration()
  {
          try {
            FileInputStream configFile = new FileInputStream(CONFIG_FILE);

          if (configFile == null)
          {
                  throw new Exception("Cannot open config file.");
          }

          config.load(configFile);
          configFile.close();

          jtapiServer = config.getProperty("jtapiServer");
          jtapiUserId = config.getProperty("jtapiUserId");
          jtapiPassword = config.getProperty("jtapiPassword");

          callManager = config.getProperty("callManager");
          callManagerUserId = config.getProperty("callManagerUserId");
          callManagerPassword = config.getProperty("callManagerPassword");

          emUserId = config.getProperty("extensionMobilityUserId");
          emPassword = config.getProperty("extensionMobilityPassword");

          ldapServer = config.getProperty("ldapServer");
          ldapPort = Integer.parseInt(config.getProperty("ldapPort"));
          ldapRoot = config.getProperty("ldapRoot");
          ldapUserId = config.getProperty("ldapUserId");
          ldapPassword = config.getProperty("ldapPassword");
          ldapSearchbase = config.getProperty("ldapSearchbase");

          devicePrefix = config.getProperty("devicePrefix");

          } catch (Exception ex)
          {
                  trace.trace("Fail to load config file: " + CONFIG_FILE, ex);
                  return false;
          }

          trace.trace("Finished Loading configuration from: " + CONFIG_FILE);
          return true;
  }

  protected synchronized boolean pushToPhone(String phoneName, String xmlData) {
    trace.trace("IPPhoneServicesSubsystem.pushToPhone("+phoneName+")");
    CiscoTerminal term = null;
    if (provider == null) {
        trace.trace("Cannot pushToPhone - JTAPI provider is null");
        return false;
    }
    try {
      term = (CiscoTerminal)provider.getTerminal(phoneName);
    }
    catch (InvalidArgumentException ex) {
      trace.trace("Exception finding phone "+phoneName, ex);
      return false;
    }
    try {
      term.sendData(xmlData);
    }
    catch (Exception ex1) {
      trace.trace("Exception pushing to phone "+phoneName, ex1);
      return false;
    }
    return true;
  }

  protected synchronized boolean pushToExtension(String extension, String xmlData) {
    trace.trace("IPPhoneServicesSubsystem.pushToExtension("+extension+")");
    if (lp == null || excludedIds == null) {
        trace.trace("LDAPProvider not yet initialized - push failed.");
        return false;
    }
    Extension ext = Extension.getExtension(extension);
    User[] users = ext.getPrimaryUsers(lp, excludedIds);
    if (users == null || users.length == 0) {
      trace.trace("User with primary extension "+extension+" not found. Phone push failed.");
      return false;
    }
    if (users.length > 1) {
      trace.trace("WARNING: Multiple users have been configured with Primary extension "+extension+". This will cause pushToExtension() to function incorrectly.");
      for (int i=0; i < users.length; i++) {
        trace.trace("userId="+users[i].getUserId());
      }
    }

    if (emp == null) {
        trace.trace("Extension Mobility Provider not yet initialized - push failed");
        return false;
    }
    Phone phone = users[0].getCurrentExtMobPhone(emp);
    if (phone == null) {
      Phone[] phones = users[0].getControlledPhones(lp, devicePrefix);
      if (phones == null || phones.length == 0) {
        trace.trace("No controlled phones found for user "+users[0]+". Phone push failed.");
        return false;
      }
      else {
        if (phones.length > 1) {
          trace.trace("WARNING: User "+users[0].getUserId()+" has more than one phone matching the specified device prefix. This will cause pushToExtension() to function incorrectly.");
        }
        phone = phones[0];
      }
    }
    CiscoTerminal term;
    if (provider == null) {
        trace.trace("Cannot pushToExtension - JTAPI provider is null");
        return false;
    }
    try {
      term = (CiscoTerminal)provider.getTerminal(phone.getDeviceName());
    }
    catch (InvalidArgumentException ex) {
      trace.trace("Exception finding phone "+phone.getDeviceName());
      return false;
    }
    try {
      term.sendData(xmlData);
    }
    catch (Exception ex1) {
      trace.trace("Exception pushing to phone "+phone.getDeviceName(), ex1);
      return false;
    }
    return true;
  }
}
