import context.arch.BaseObject;
import context.arch.comm.DataObject;
import context.arch.widget.WContactPresence;
import context.arch.handler.Handler;
import context.arch.InvalidMethodException;
import context.arch.MethodException;
import context.arch.subscriber.Subscriber;
import context.arch.comm.language.DecodeException;
import context.arch.comm.language.InvalidDecoderException;
import context.arch.comm.language.EncodeException;
import context.arch.comm.language.InvalidEncoderException;
import context.arch.comm.protocol.ProtocolException;
import context.arch.comm.protocol.InvalidProtocolException;
import context.arch.storage.AttributeNameValues;
import context.arch.storage.AttributeNameValue;
import context.arch.storage.AttributeFunctions;
import context.arch.storage.Attributes;
import context.arch.storage.Attribute;
import context.arch.storage.Retrieval;
import context.arch.storage.Conditions;
import context.arch.storage.StorageObject;
import context.arch.storage.Storage;
import context.arch.storage.RetrievalResults;
import context.arch.util.Error;
import context.arch.subscriber.Callbacks;

import java.util.Vector;
import java.util.Hashtable;

/**
 * This class is a test application that uses the WContactPresence.  It
 * contains code that tests polling and conditionally subscribing to the widget and 
 * retrieving history data from the widget.
 *
 * @see context.arch.handler.Handler 
 * @see context.arch.widget.WContactPresence
 */
public class ContactPresenceApp implements Handler {

  /** 
   * Default port to start the BaseObject on is 5555
   */
  public static final int DEFAULT_PORT = 5555;

  /**
   * Subscription ID for WContactPresence UPDATE callback
   */
  public static final String SUBSCRIBER_ID = "ContactPresenceApp";

  private int numHandles = 0;
  
  /**
   * Constructor that creates an instance of BaseObject at the DEFAULT_PORT.  It
   * subscribes to the WContactPresence and prints out callback info and polls
   * the widget and prints out the results
   *
   * @param location Location of the WContactPresence
   * @param ppwHost Hostname the widget is running on
   * @param ppwPort Port the widget is running on
   * @see context.arch.BaseObject
   * @see context.arch.BaseObject#subscribeTo(context.arch.handler.Handler, int, java.lang.String,
		java.lang.String, int, java.lang.String, java.lang.String, java.lang.String)
   * @see context.arch.widget.WContactPresence
   */
  public ContactPresenceApp(String location, String ppwHost, int ppwPort) {
    this(location, DEFAULT_PORT, ppwHost, ppwPort);
  }

  /**
   * Constructor that creates an instance of BaseObject at the given port.  It
   * subscribes to the WContactPresence and prints out callback info and polls
   * the widget and prints out the results.
   *
   * @param localport Port the app is running on
   * @param ppwHost Hostname the widget is running on
   * @param ppwPort Port the widget is running on
   * @see context.arch.BaseObject
   * @see context.arch.BaseObject#subscribeTo(context.arch.handler.Handler, int, java.lang.String,
		java.lang.String, int, java.lang.String, java.lang.String, java.lang.String)
   * @see context.arch.widget.WContactPresence
   */
  public ContactPresenceApp(String location, int localport, String ppwHost, int ppwPort) {
    BaseObject server = new BaseObject (localport);
    int port = localport;
    String widgetId = WContactPresence.CLASSNAME+"_"+location;

    // query the widget's attributes with correct id
    DataObject attributes2 = server.getWidgetAttributes(ppwHost, ppwPort, widgetId);
    String attributesError2 = new Error(attributes2).getError();
    Attributes atts2 = null;
    if (attributesError2.equals(Error.NO_ERROR)) {
      atts2 = new Attributes(attributes2);
    }
    System.out.println("Attributes with valid id: "+attributes2+"\n");
    System.out.println("error = "+attributesError2+", attributes = "+atts2);
    System.out.println("**********************************************\n");

    // query the widget's callbacks with correct id
    DataObject callbacks = server.getWidgetCallbacks(ppwHost, ppwPort, widgetId);
    String callbacksError = new Error(callbacks).getError();
    Callbacks calls = null;
    if (callbacksError.equals(Error.NO_ERROR)) {
      calls = new Callbacks(callbacks);
    }
    System.out.println("Callbacks with valid id: "+callbacks+"\n");
    System.out.println("error = "+callbacksError+", callbacks = "+calls);
    System.out.println("**********************************************\n");

    //  new subscription with no conditions or attributes
    Error error = server.subscribeTo(this, port, SUBSCRIBER_ID, ppwHost, ppwPort, widgetId,
                 WContactPresence.UPDATE, "presenceUpdate");
    System.out.println("Subscription with no attributes/conditions: "+error.getError());
    System.out.println("**********************************************\n");

    System.out.println("Need to dock "+(3-numHandles)+" more times");
    while (numHandles < 3) {
      try {
        Thread.sleep(500);
      } catch (InterruptedException ite) {
          System.out.println("Interrupted thread: "+ite);
      }
    }

    //  new subscription with no conditions and some attributes
    Attributes atts = new Attributes();
    atts.addAttribute(WContactPresence.CONTACT_INFO);
    atts.addAttribute(WContactPresence.USERID);
    Error error2 = server.subscribeTo(this, port, SUBSCRIBER_ID, ppwHost, ppwPort, widgetId,
                 WContactPresence.UPDATE, "presenceUpdate", atts);
    System.out.println("Subscription with some attributes (contact,userid) and no conditions: "+error2.getError());
    System.out.println("**********************************************\n");

    System.out.println("Need to dock "+(6-numHandles)+" more times");
    while (numHandles < 6) {
      try {
        Thread.sleep(500);
      } catch (InterruptedException ite) {
          System.out.println("Interrupted thread: "+ite);
      }
    }

    Attributes atts3 = new Attributes();
    atts3.addAttribute(WContactPresence.CONTACT_INFO+Attributes.SEPARATOR_STRING+WContactPresence.NAME);
    atts3.addAttribute(WContactPresence.LOCATION);
    Error error3 = server.subscribeTo(this, port, SUBSCRIBER_ID, ppwHost, ppwPort, widgetId,
                 WContactPresence.UPDATE, "presenceUpdate", atts3);
    System.out.println("Subscription with some attributes (contactinfo.name,location) and no conditions: "+error3.getError());
    System.out.println("**********************************************\n");

    System.out.println("Need to dock "+(9-numHandles)+" more times");
    while (numHandles < 9) {
      try {
        Thread.sleep(500);
      } catch (InterruptedException ite) {
          System.out.println("Interrupted thread: "+ite);
      }
    }

    Attributes atts4 = new Attributes();
    atts4.addAttribute(WContactPresence.CONTACT_INFO+Attributes.SEPARATOR_STRING+WContactPresence.NAME);
    atts4.addAttribute(WContactPresence.LOCATION);
    Conditions conds4 = new Conditions();
    conds4.addCondition(WContactPresence.CONTACT_INFO+Attributes.SEPARATOR_STRING+WContactPresence.NAME, Storage.EQUAL, "Anind");
    conds4.addCondition(WContactPresence.USERID,Storage.EQUAL, "16AC850600000044");
    Error error4 = server.subscribeTo(this, port, SUBSCRIBER_ID, ppwHost, ppwPort, widgetId,
                 WContactPresence.UPDATE, "presenceUpdate", conds4, atts4);
    System.out.println("Subscription with some attributes (contactinfo.name,location) and some conditions (contactinfo.name=Anind,userid=first): "+error4.getError());
    System.out.println("**********************************************\n");

    System.out.println("Need to dock "+(12-numHandles)+" more times");
    while (numHandles < 12) {
      try {
        Thread.sleep(500);
      } catch (InterruptedException ite) {
          System.out.println("Interrupted thread: "+ite);
      }
    }

    // query the widget with valid attributes
    Attributes prePollAtts = new Attributes();
    prePollAtts.addAttribute(WContactPresence.CONTACT_INFO);
    DataObject poll = server.pollWidget(ppwHost, ppwPort, widgetId, prePollAtts);
    String pollError = new Error(poll).getError();
    AttributeNameValues pollAtts = null;
    if (pollError.equals(Error.NO_ERROR)) {
      pollAtts = new AttributeNameValues(poll);
    }
    System.out.println("Poll (contactInfo): "+poll+"\n");
    System.out.println("error = "+pollError+", attributes = "+pollAtts);
    System.out.println("**********************************************\n");

    // query the widget with valid attributes
    Attributes prePollAtts2 = new Attributes();
    prePollAtts2.addAttribute(WContactPresence.CONTACT_INFO+Attributes.SEPARATOR_STRING+WContactPresence.NAME);
    DataObject poll2 = server.pollWidget(ppwHost, ppwPort, widgetId, prePollAtts2);
    String pollError2 = new Error(poll2).getError();
    AttributeNameValues pollAtts2 = null;
    if (pollError2.equals(Error.NO_ERROR)) {
      pollAtts2 = new AttributeNameValues(poll2);
    }
    System.out.println("Poll (contactInfo.name): "+poll2+"\n");
    System.out.println("error = "+pollError2+", attributes = "+pollAtts2);
    System.out.println("**********************************************\n");

    // query the widget with invalid attributes
    Attributes prePollAtts3 = new Attributes();
    prePollAtts3.addAttribute(WContactPresence.CONTACT_INFO+Attributes.SEPARATOR_STRING+"blah");
    DataObject poll3 = server.pollWidget(ppwHost, ppwPort, widgetId, prePollAtts3);
    String pollError3 = new Error(poll3).getError();
    AttributeNameValues pollAtts3 = null;
    if (pollError3.equals(Error.NO_ERROR)) {
      pollAtts3 = new AttributeNameValues(poll3);
    }
    System.out.println("Poll (contactInfo.blah): "+poll3+"\n");
    System.out.println("error = "+pollError3+", attributes = "+pollAtts3);
    System.out.println("**********************************************\n");

    // retrieve all data from the widget
    DataObject retrieve3 = server.retrieveDataFrom(ppwHost, ppwPort, widgetId);
    String retrieveError3 = new Error(retrieve3).getError();

    RetrievalResults retrieveData3 = null;
    if (retrieveError3.equals(Error.NO_ERROR)) {
      retrieveData3 = new RetrievalResults(retrieve3);
    }
    System.out.println("Retrieval: "+retrieve3+"\n");
    System.out.println("error = "+retrieveError3+", retrieval3 = "+retrieveData3);
    System.out.println("**********************************************\n");

    // retrieve data with attributes and conditions
    AttributeFunctions retAtts = new AttributeFunctions();
    retAtts.addAttributeFunction(WContactPresence.CONTACT_INFO+Attributes.SEPARATOR_STRING+WContactPresence.NAME);
    Conditions retConds = new Conditions();
    retConds.addCondition(WContactPresence.CONTACT_INFO+Attributes.SEPARATOR_STRING+WContactPresence.NAME, Storage.EQUAL, "Anind");
    retConds.addCondition(WContactPresence.USERID,Storage.EQUAL, "16AC850600000044");
    DataObject retrieve4 = server.retrieveDataFrom(ppwHost, ppwPort, widgetId, new Retrieval(retAtts,retConds));
    String retrieveError4 = new Error(retrieve4).getError();

    RetrievalResults retrieveData4 = null;
    if (retrieveError4.equals(Error.NO_ERROR)) {
      retrieveData4 = new RetrievalResults(retrieve4);
    }
    System.out.println("Retrieval with atts and conds: "+retrieve4+"\n");
    System.out.println("error = "+retrieveError4+", retrieval4 = "+retrieveData4);
    System.out.println("**********************************************\n");

  }

  /**
   * This method is required to implement the Handler interface.  It is called when
   * the callbacks this object subscribes to are triggered.  In this case, it is
   * called when an IButton is docked with the reader in the WPersonPresence.
   *
   * @param callback Name of the callback to handle
   * @param data DataObject containing the callback event data
   * @see context.arch.handler.Handler
   * @see context.arch.widget.WContactPresence
   * @exception context.arch.InvalidMethodException if the callback specified isn't recognized	
   * @exception context.arch.MethodException if the callback specified can't be handled successfully
   */
  public DataObject handle(String callback, DataObject data) throws InvalidMethodException, MethodException {
    if (callback.equals("presenceUpdate")) {
      AttributeNameValues attributes = new AttributeNameValues(data);
      System.out.println("Subscription: "+attributes+"\n\n");
     numHandles ++;
   }
    else {
      numHandles ++;
      throw new InvalidMethodException(Error.UNKNOWN_CALLBACK_ERROR);
    }
    return null;
  }


  /**
   * Main method that creates an instance of ContactPresenceApp
   */
  public static void main (String argv[]) {
    if (argv.length == 3) {
      System.out.println("Attempting to create a ContactPresenceApp on "+DEFAULT_PORT+" at "+argv[0]);    
      ContactPresenceApp cpa = new ContactPresenceApp(argv[0], argv[1], Integer.parseInt(argv[2]));
    }
    else if (argv.length == 4) {
      System.out.println("Attempting to create a ContactPresenceApp on "+argv[2]+" at "+argv[0]);    
      ContactPresenceApp cpa = new ContactPresenceApp(argv[0], Integer.parseInt(argv[1]), argv[2], Integer.parseInt(argv[3]));
    }
    else {
      System.out.println("USAGE: java ContactPresenceApp <location> [port] <WContactPresenceHost/ip> <WContactPresencePort>");
    }
  }

}

