package context.apps.Tour;

import context.arch.BaseObject;
import context.arch.comm.DataObject;
import context.arch.handler.Handler;
import context.arch.InvalidMethodException;
import context.arch.MethodException;
import context.arch.comm.language.DecodeException;
import context.arch.subscriber.Subscriber;
import context.arch.subscriber.Callbacks;
import context.arch.subscriber.Callback;
import context.arch.storage.AttributeNameValues;
import context.arch.storage.AttributeNameValue;
import context.arch.storage.Attribute;
import context.arch.storage.RetrievalResults;
import context.arch.storage.Storage;
import context.arch.util.Error;
import context.arch.util.Constants;
import context.arch.util.Configuration;
import context.arch.util.ConfigObjects;
import context.arch.util.ConfigObject;
import context.arch.util.XMLURLReader;
import context.arch.util.SendMail;
import context.arch.widget.WTourEnd;
import context.arch.widget.WTourDemo;
import context.arch.widget.WTourRegistration;
import context.arch.server.STourId;

import java.util.Vector;
import java.util.Enumeration;
import java.util.Date;
import java.net.MalformedURLException;

/**
 * This class is an application that uses a WTourDemo, WTourEnd, and WTourRegistration.
 * It is a very simple context-aware tour guide that collects information about the user
 * displays information about the demo the user is in front of, and then when the user
 * is ready to leave, it emails a list of the demos (and corresponding information) that
 * the user visited. Also, for each demo, the application lists a number of other demos
 * that are related.
 *
 * @see context.arch.handler.Handler 
 * @see context.arch.widget.WTourEnd
 * @see context.arch.widget.WTourDemo
 * @see context.arch.widget.WTourRegistration
 */
public class TourApp implements Handler {

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

  /**
   * Subscription ID for callbacks
   */
  public static final String SUBSCRIBER_ID = "Tour";

  private int port;  
  private String userid;
  private DemoFile demo;
  private BaseObject server;
  private String intId;
  private String intHost;
  private int intPort;

  private String name;
  private String affiliation;
  private String email;
  private String interests;
  private Long initialTime;

  private DemoInterests demoInterests;
  private DemoVisits demoVisits;
  private TourAppFrame frame = null;

  public static final String NO_INTEREST = "noInterest";

  /**
   * Constructor that creates an instance of BaseObject at the DEFAULT_PORT.  It
   * subscribes to the various demo widgets.
   *
   * @param userid Id of the user 
   * @param configFile Configuration file containing widget information
   * @param demoFile File containing information about each demo
   *
   * @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)
   */
  public TourApp(String userid, String configFile, String demoFile) {
    this(DEFAULT_PORT, userid, configFile, demoFile);
  }

  /**
   * Constructor that creates an instance of BaseObject at the DEFAULT_PORT.  It
   * subscribes to the various demo widgets.
   *
   * @param localport Port the app is running on
   * @param userid Id of the user 
   * @param configFile Configuration file containing widget information
   * @param demoFile File containing information about each demo
   *
   * @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)
   */
  public TourApp(int localport, String userid, String configFile, String demoFile) {
    try {
      server = new BaseObject(localport);
      port = localport;
      this.userid = userid;
      demo = new DemoFile(demoFile);
      XMLURLReader reader = new XMLURLReader(configFile);
      DataObject data = reader.getParsedData();
      Configuration config = new Configuration(data);

      ConfigObjects interpreters = config.getInterpreterConfigurations();
      if (interpreters != null) {
        ConfigObject interpreter = (ConfigObject)interpreters.getEnumeration().nextElement();
        intId = interpreter.getId();
        intHost = interpreter.getHost();
        intPort = Integer.parseInt(interpreter.getPort());
      }

      ConfigObjects servers = config.getServerConfigurations();
      if (servers != null) {
        for (Enumeration e=servers.getEnumeration(); e.hasMoreElements(); ) {
          ConfigObject serverObj = (ConfigObject)e.nextElement();
          if (serverObj.getId().indexOf(userid) != -1) {
            String serverId = serverObj.getId();
            String serverHost = serverObj.getHost();
            int serverPort = Integer.parseInt(serverObj.getPort());
            DataObject callbacks = server.getWidgetCallbacks(serverHost, serverPort, serverId+"_"+userid);
            String callbacksError = new Error(callbacks).getError();
            Callbacks calls = null;
            if (callbacksError.equals(Error.NO_ERROR)) {
              calls = new Callbacks(callbacks);
            }

            for (int i=0; i<calls.numCallbacks(); i++) {
              Callback callback = calls.getCallbackAt(i);
              if (callback.getName().indexOf(WTourRegistration.UPDATE) != -1) {
                Error error = server.subscribeTo(this,port,SUBSCRIBER_ID,serverHost,serverPort,serverId,
                        callback.getName(),SUBSCRIBER_ID+Constants.SPACER+WTourRegistration.UPDATE);
              }
              else if (callback.getName().indexOf(WTourDemo.VISIT) != -1) {
                Error error = server.subscribeTo(this,port,SUBSCRIBER_ID,serverHost,serverPort,serverId,
                        callback.getName(),SUBSCRIBER_ID+Constants.SPACER+WTourDemo.VISIT);
              }
              else if (callback.getName().indexOf(WTourDemo.INTEREST) != -1) {
                Error error = server.subscribeTo(this,port,SUBSCRIBER_ID,serverHost,serverPort,serverId,
                        callback.getName(),SUBSCRIBER_ID+Constants.SPACER+WTourDemo.INTEREST);
              }
              else if (callback.getName().indexOf(WTourEnd.END) != -1) {
                Error error = server.subscribeTo(this,port,SUBSCRIBER_ID,serverHost,
                        serverPort,serverId,callback.getName(),SUBSCRIBER_ID+Constants.SPACER+WTourEnd.END);
              }
            }
          }
        } // for
      } // if servers
    } catch (MalformedURLException mue) {
        System.out.println("TourApp MalformedURL: "+mue);
    } catch (DecodeException de) {
        System.out.println("TourApp Decode: "+de); 
    }
  }

  /**
   * This method is required to implement the Handler interface.  It is called when
   * the callbacks this object subscribes to are triggered.  
   *
   * @param callback Name of the callback to handle
   * @param data DataObject containing the callback event data
   *
   * @see context.arch.handler.Handler
   * @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(SUBSCRIBER_ID+Constants.SPACER+WTourRegistration.UPDATE)) {
      // get interests info, contact info and store for later
      demoInterests = new DemoInterests();
      demoVisits = new DemoVisits();
      AttributeNameValues atts = new AttributeNameValues(data);
      interests = (String)atts.getAttributeNameValue(WTourRegistration.INTERESTS).getValue();
      AttributeNameValue att = atts.getAttributeNameValue(WTourRegistration.CONTACT_INFO);
      AttributeNameValues atts2 = (AttributeNameValues)att.getValue();
      name = (String)atts2.getAttributeNameValue(WTourRegistration.NAME).getValue();
      affiliation = (String)atts2.getAttributeNameValue(WTourRegistration.AFFILIATION).getValue();
      email = (String)atts2.getAttributeNameValue(WTourRegistration.EMAIL).getValue();
      initialTime = new Long((String)atts.getAttributeNameValue(WTourRegistration.TIMESTAMP).getValue());

      AttributeNameValues input = new AttributeNameValues();
      input.addAttributeNameValue(WTourRegistration.INTERESTS,interests);
      input.addAttributeNameValue(Demos.DEMOS,demo.getDemos().toAttributeNameValues(),Attribute.STRUCT);
      String result = askInterpreter(input);

      if (frame != null) {
        frame.dispose();
      }
      frame = new TourAppFrame(result);
      frame.pack();
      frame.setResizable(true);
      frame.show();
    }
    else if (callback.equals(SUBSCRIBER_ID+Constants.SPACER+WTourDemo.VISIT)) {
      AttributeNameValues atts = new AttributeNameValues(data);
      String demourl = (String)atts.getAttributeNameValue(WTourDemo.DEMO_URL).getValue();
      String demoerurl = (String)atts.getAttributeNameValue(WTourDemo.DEMOER_URL).getValue();
      String demoName = (String)atts.getAttributeNameValue(WTourDemo.DEMO_NAME).getValue();
      String timestamp = (String)atts.getAttributeNameValue(WTourDemo.TIMESTAMP).getValue();
      demoInterests.addDemoInterest(demoName);
      frame.setUrls(demourl,demoerurl);
      demoVisits.addDemoVisit(demoName,demourl,demoerurl,timestamp);
      AttributeNameValues input = new AttributeNameValues();
      for (Enumeration e = demoInterests.getEnumeration(); e.hasMoreElements(); ) {
        DemoInterest demoInterest = (DemoInterest)e.nextElement();
        input.addAttributeNameValue(WTourDemo.DEMO_NAME,demoInterest.getDemo());
        if (demoInterest.getInterest() == null) {
          input.addAttributeNameValue(WTourDemo.INTEREST_LEVEL,NO_INTEREST);
        }
        else {
          input.addAttributeNameValue(WTourDemo.INTEREST_LEVEL,demoInterest.getInterest());
        }
      }
      input.addAttributeNameValue(WTourRegistration.INTERESTS,interests);
      input.addAttributeNameValue(Demos.DEMOS,demo.getDemos().toAttributeNameValues(),Attribute.STRUCT);
      String result = askInterpreter(input);
      frame.setRecommendation(result);
    }
    else if (callback.equals(SUBSCRIBER_ID+Constants.SPACER+WTourDemo.INTEREST)) {
      AttributeNameValues atts = new AttributeNameValues(data);
      String interest = (String)atts.getAttributeNameValue(WTourDemo.INTEREST_LEVEL).getValue();
      String demoName = (String)atts.getAttributeNameValue(WTourDemo.DEMO_NAME).getValue();
      demoInterests.addDemoInterest(demoName,interest);
      AttributeNameValues input = new AttributeNameValues();
      input.addAttributeNameValue(DemoInterests.DEMO_INTERESTS,demoInterests.toAttributeNameValues(),Attribute.STRUCT);
      input.addAttributeNameValue(WTourRegistration.INTERESTS,interests);
      input.addAttributeNameValue(Demos.DEMOS,demo.getDemos().toAttributeNameValues(),Attribute.STRUCT);
      String result = askInterpreter(input);
      frame.setRecommendation(result);

      DemoVisit dv = demoVisits.getDemoVisit(demoName);
      if (dv != null) {
        dv.setInterest(interest);
      }
    }
    else if (callback.equals(SUBSCRIBER_ID+Constants.SPACER+WTourEnd.END)) {
      frame.dispose();

      AttributeNameValues input = new AttributeNameValues();
      for (Enumeration e = demoInterests.getEnumeration(); e.hasMoreElements(); ) {
        DemoInterest demoInterest = (DemoInterest)e.nextElement();
        input.addAttributeNameValue(WTourDemo.DEMO_NAME,demoInterest.getDemo());
        if (demoInterest.getInterest() == null) {
          input.addAttributeNameValue(WTourDemo.INTEREST_LEVEL,NO_INTEREST);
        }
        else {
          input.addAttributeNameValue(WTourDemo.INTEREST_LEVEL,demoInterest.getInterest());
        }
      }
      input.addAttributeNameValue(WTourRegistration.INTERESTS,interests);
      input.addAttributeNameValue(Demos.DEMOS,demo.getDemos().toAttributeNameValues(),Attribute.STRUCT);
      String result = askInterpreter(input);

      StringBuffer message = new StringBuffer(name+", thank you for visiting the FCL lab and taking our tour!\n\n");
      message.append("Following is a summary of your tour:\n\n");
      for (int i=0; i<demoVisits.numDemoVisits(); i++) {
        DemoVisit dv = demoVisits.getDemoVisitAt(i);
        message.append("At "+new Date(dv.getTime()).toString()+", you visited the "+dv.getDemoName()+" demo.\n");
        message.append("Your level of interest in this demo was: "+dv.getInterest()+".\n");
        message.append("If you would like more information on this demo, please go to the demo's web page at\n");
        message.append(dv.getDemoUrl()+", or go the demoer's web page at "+dv.getDemoerUrl()+".\n\n");
      }
      message.append("Based on your interests which you used to register with the tour guide program, we think\n");
      message.append("the following demos might also be interesting to you: \n"+result);

//      String serverId = config.getComponentId(userid);
//      String serverHost = config.getComponentHost(userid);
//      int serverPort = Integer.parseInt(config.getComponentPort(userid));
//      DataObject dobj = server.retrieveDataFrom(serverHost, serverPort, serverId+"_"+userid,WTourRegistration.TIMESTAMP,
//                               Storage.GREATERTHANEQUAL, initialTime);
//      RetrievalResults retrieveData = new RetrievalResults(dobj);
//      if (retrieveData != null) {
//       // retrieve all data before initialTime and put in message - AKD
//       String message = retrieveData.toString();
        SendMail sm = new SendMail();
        sm.sendMail(server.getHostAddress(),"chagall.cc.gatech.edu","anind@cc.gatech.edu",email,"FCL Demo Trip Summary",message.toString());
//      }
//      else {
//        String message = new String("error in retrieving data: query was TIMESTAMP >= "+initialTime.toString());
//        SendMail sm = new SendMail();
//        sm.sendMail(server.getHostAddress(),"chagall.cc.gatech.edu","anind@cc.gatech.edu","anind@cc.gatech.edu","GVU Demo Trip Summary - ERROR",message);
//      }
    }
    else {
      throw new InvalidMethodException(Error.UNKNOWN_CALLBACK_ERROR);
    }
    return null;
  }

  private String askInterpreter(AttributeNameValues input) {
    DataObject result = server.askInterpreter(intHost,intPort,intId,input);
    AttributeNameValues atts = new AttributeNameValues(result);
    if (atts == null) {
      return null;
    }
    else if (atts.numAttributeNameValues() == 0) {
      return new String("There are no more demos to recommend.");
    }
    StringBuffer sb = new StringBuffer();
    for (int i=0; i<atts.numAttributeNameValues(); i++) {
      sb.append((String)atts.getAttributeNameValueAt(i).getValue()+", ");
    }
    return sb.toString();
  }

  /**
   * Main method that creates an instance of TourApp
   */
  public static void main (String argv[]) {
    if (argv.length == 3) {
      System.out.println("Attempting to create a TourApp on "+DEFAULT_PORT+" for "+argv[0]);    
      TourApp ta = new TourApp(argv[0], argv[1], argv[2]);
    }
    else if (argv.length == 4) {
      System.out.println("Attempting to create a TourApp on "+argv[1]+" for "+argv[0]);    
      TourApp ta = new TourApp(Integer.parseInt(argv[1]), argv[0], argv[2], argv[3]);
    }
    else {
      System.out.println("USAGE: java TourApp <userid> [port] <config file> <demo info file>");
    }
  }

}
