/*
 * Scone - The Web Enhancement Framework
 * Copyright (C) 2009 Harald Weinreich, Volkert Buchmann, Frank Wollenweber, Torsten Haß
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package scone.accesstracking;


import java.io.IOException;

import scone.ras.Connection;
import scone.ras.ConnectionHandler;
import scone.util.ErrorLog;


/**
 * Every time, LogApplet connects to RAS (scone.ras.rasServer.java),
 * RAS starts an instance of this class that lives as long as the
 * connection of the applet.
 * This class communicates with the Applet, decodes received messages
 * and calls the corresponding methods of the EventDecoder class, that
 * creates and fills the Access events.
 *
 * @author Torsten Hass
 * @version 1.0b, 10/31/2001
 */
public class AppletConnector implements ConnectionHandler {
    private EventDecoder eventDecoder;        // decodes received data and creates Events
    private boolean decoded = false;          // check if received line was decoded
    private Connection c;

    // variables for appletStarted
    private long appletStartTime = 0;
    private String userId;
    private String nodeId;
    private long startTime = 0;
    private String frameName;
    private String fragment;
    private String query;
    private String parentFrameName;
    private String parentNodeName;
    private String referrer;
    private String isIE;
    private String isNS;
    private String post;
    private int frameCount;
    private String megTime; // time of JS creation
    // variables for "pageLoaded"
    private long loadedTime = 0;
    private String accessEventExistedString;
    private int browserName;
    // variables for "pageUnloaded"
    private long stoppedTime;
    // variables for "linkClicked"
    private long linkTime;
    private String linkId;
    // variables for "formSubmited"
    private long formTime = 0;
    private String formAction = "";

    public AppletConnector() {}

    /**
     * RAS handler for the RAS-Connection from LogApplet. This
     * method is invoked by RASServer when LogApplet connects to
     * RAS.
     *
     * @param c The Connection object to communicate with LogApplet
     */
    public void handle(Connection c) {
        boolean showMessages = false;
        boolean useIESpy = false;

        // System.out.println("AppletConnector gestartet");
        this.c = c;
        FrameAccess frameAccess = FrameAccess.getInstance();

        // Ask Scone properties if status messages should be displayed
        try {
            if (AccessTracking.props.get("Acesstracking1: Show status messages").equals("true")) {
                showMessages = true; // show status messages to std out
            }
        } catch (Exception exc) {
            ErrorLog.log(this, "AppletConnector: Error while reading Scone properties", "", exc);
        }
        try {
            if (AccessTracking.props.get("Acesstracking1: Disable Applet").equals("true")) {
                useIESpy = true; // Don't call EventDecoder, just register at FrameAccess
            }
        } catch (Exception exc) {
            ErrorLog.log(this, "AppletConnector: Error while reading Scone properties", "", exc);
        }

        String inputLine = null;

        if (!useIESpy) { // call EventDecoder only if useIESpy = false
            eventDecoder = new EventDecoder();
        }
        try {
            while ((inputLine = c.read()) != null && !inputLine.equals("null")) {// Errors may happen!
                decoded = false;
                // receiving the applet's data from it's start
                if (inputLine.startsWith("params")) {
                    // Get applet start time from string
                    appletStartTime = Long.parseLong(getParameter(inputLine, "AppletStartTime"));
                    // System.out.println("AppletStartTime: " + appletStartTime);
                    // Get userId
                    userId = getParameter(inputLine, "UserId");
                    // System.out.println("UserId = " + userId);
                    // Get nodeId
                    nodeId = getParameter(inputLine, "NodeId");
                    // System.out.println("NodeId = " + nodeId);
                    // nodeName = NetNodeCache.getById(nodeId).getUri();
                    // Get start time
                    startTime = Long.parseLong(getParameter(inputLine, "StartTime"));
                    // System.out.println("StartTime = " + startTime);
                    // Get frameName
                    frameName = getParameter(inputLine, "Frame");
                    // System.out.println("FrameName = " + frameName);
                    // Get fragment
                    fragment = getParameter(inputLine, "Fragment");
                    // System.out.println("Fragment = " + fragment);
                    // Get query
                    query = getParameter(inputLine, "Query");
                    // System.out.println("Query = " + query);
                    // Get Posted form data
                    post = getParameter(inputLine, "Post");
                    // Get ParentNodeName
                    parentNodeName = getParameter(inputLine, "ParentNodeName");
                    // System.out.println("Parent node name : " + parentNodeName);
                    // Get parentFrameName
                    parentFrameName = getParameter(inputLine, "ParentFrame");
                    // System.out.println("ParentFrameName: " + parentFrameName);
                    // Now that we have the parent frame's name we can

                    // register this AppletConnector in FrameAccess
                    frameAccess.add(parentFrameName, frameName, this);

                    // Get referrer
                    referrer = getParameter(inputLine, "Referrer");
                    // System.out.println("Referrer node: " + referrer);
                    // Get HistoryLenght
                    // System.out.println("History length: " + getParameter(inputLine, "History"));
                    // Get isIE
                    isIE = getParameter(inputLine, "IsIE");
                    // System.out.println("IsIE: " + isIE);
                    // Get isNs
                    isNS = getParameter(inputLine, "IsNS");
                    // System.out.println("IsNS: " + isNS);
                    // Get Number of frames
                    frameCount = Integer.parseInt(getParameter(inputLine, "FrameCount"));
                    // System.out.println("Number of Frames: " + frameCount);
                    // Get AccessTrackingMeg time
                    megTime = getParameter(inputLine, "ATMegTime");
                    // System.out.println("ATMegTime: " + megTime);
                    // Call method appletStarted with all collected info
                    // System.out.println("this: " + this.toString() + " Meg: " + megTime);
                    if (!useIESpy) { // call EventDecoder only if useIESpy = false
                        eventDecoder.appletStarted(appletStartTime, userId, nodeId,
                                startTime, frameName, fragment,
                                query, post, parentNodeName,
                                parentFrameName, referrer, isIE,
                                isNS, frameCount, megTime,
                                showMessages, this);
                    }
                    decoded = true;
                }
                // page is loaded now
                if (inputLine.startsWith("loaded")) {

                    /* appletStartTime = Long.parseLong(getParameter(inputLine, "AppletStartTime"));
                     userId = getParameter(inputLine, "UserId");
                     nodeId = getParameter(inputLine, "NodeId");
                     frameName = getParameter(inputLine, "Frame");
                     startTime = Long.parseLong(getParameter(inputLine, "StartTime"));
                     */
                    loadedTime = Long.parseLong(getParameter(inputLine, "LoadedTime"));

                    /* accessEventExistedString = getParameter(inputLine, "AccessEventExisted");
                     browserName = Integer.parseInt(getParameter(inputLine, "BrowserName"));
                     */
                    // System.out.println("this: " + this.toString() + " Meg: " + megTime);
                    if (!useIESpy) { // call EventDecoder only if useIESpy = false
                        eventDecoder.pageLoaded(appletStartTime, userId, nodeId,
                                startTime, frameName, loadedTime

                                /* accessEventExistedString,
                                 browserName */);
                    }
                    decoded = true;
                }
                // page is going to be unloaded
                if (inputLine.startsWith("stopped")) {
                    // Get key values for access event and stoppedTime for stay time calculation
                    /* appletStartTime = Long.parseLong(getParameter(inputLine, "AppletStartTime"));
                     userId = getParameter(inputLine, "UserId");
                     nodeId = getParameter(inputLine, "NodeId");
                     startTime = Long.parseLong(getParameter(inputLine, "StartTime"));
                     frameName = getParameter(inputLine, "Frame");
                     */

                    // unregister this AppletConnector in FrameAccess
                    frameAccess.remove(parentFrameName, frameName);

                    stoppedTime = Long.parseLong(getParameter(inputLine, "StoppedTime"));

                    /* accessEventExistedString = getParameter(inputLine, "AccessEventExisted");
                     */
                    // System.out.println("this: " + this.toString() + " Meg: " + megTime);
                    if (!useIESpy) { // call EventDecoder only if useIESpy = false
                        eventDecoder.pageUnloaded(appletStartTime, userId, nodeId,
                                startTime, frameName, stoppedTime

                                /* accessEventExistedString,
                                 browserName*/);
                    }
                    decoded = true;
                }
                // the user clicked a link
                if (inputLine.startsWith("link")) {
                    // get all needed Data from the inputLine string
                    /* appletStartTime = Long.parseLong(getParameter(inputLine, "AppletStartTime"));
                     userId = getParameter(inputLine, "UserId");
                     nodeId = getParameter(inputLine, "NodeId");
                     startTime = Long.parseLong(getParameter(inputLine, "StartTime"));
                     frameName = getParameter(inputLine, "Frame");
                     frameCount = Integer.parseInt(getParameter(inputLine, "FrameCount"));
                     megTime = getParameter(inputLine, "ATMegTime");
                     */
                    linkTime = Long.parseLong(getParameter(inputLine, "LinkTime"));
                    linkId = getParameter(inputLine, "LinkId");

                    /* accessEventExistedString = getParameter(inputLine, "AccessEventExisted");
                     browserName = Integer.parseInt(getParameter(inputLine, "BrowserName"));
                     */
                    // System.out.println("this: " + this.toString() + " Meg: " + megTime);
                    if (!useIESpy) { // call EventDecoder only if useIESpy = false
                        eventDecoder.linkClicked(appletStartTime, userId, nodeId,
                                startTime, frameName, frameCount,
                                megTime, linkTime, linkId

                                /* accessEventExistedString,
                                 browserName*/);
                    }
                    decoded = true;
                }
                // the submit button of a form was clicked
                if (inputLine.startsWith("form")) {

                    /* appletStartTime = Long.parseLong(getParameter(inputLine, "AppletStartTime"));
                     userId = getParameter(inputLine, "UserId");
                     nodeId = getParameter(inputLine, "NodeId");
                     startTime = Long.parseLong(getParameter(inputLine, "StartTime"));
                     frameName = getParameter(inputLine, "Frame");
                     */
                    formTime = Long.parseLong(getParameter(inputLine, "FormTime"));
                    formAction = getParameter(inputLine, "Action");

                    /* accessEventExistedString = getParameter(inputLine, "AccessEventExisted");
                     */
                    // System.out.println("this: " + this.toString() + " Meg: " + megTime);
                    if (!useIESpy) { // call EventDecoder only if useIESpy = false
                        eventDecoder.formSubmited(appletStartTime, userId, nodeId,
                                startTime, frameName,
                                formTime, formAction

                                /* accessEventExistedString,
                                 browserName*/);
                    }
                    decoded = true;
                }

                // another log message was sent. Used for the scone.evaluator
                if (inputLine.startsWith("logMessage")) {

                    String className = getParameter(inputLine, "className");
                    String method = getParameter(inputLine, "method");
                    String param = getParameter(inputLine, "parameter");

                    eventDecoder.logMessage(className, method, param);
                    decoded = true;
                }

                if (!decoded) {
                    System.out.println("Could not decode: " + inputLine);
                }
            } // while
            // unregister this AppletConnector in FrameAccess
            // if the browser did not call stopped
            frameAccess.remove(parentFrameName, frameName);
            c.close();
        } catch (IOException e) {
            frameAccess.remove(parentFrameName, frameName);
        } catch (Exception ex) {
            frameAccess.remove(parentFrameName, frameName);
        }
        frameAccess.remove(parentFrameName, frameName);
        // System.out.println("Verbindung beendet");
    }

    /**
     * Sends an empty String to an applet only used to check whether this applet exists or not.
     * This can be used as a hint whether the window exists or not.
     *
     * It will return true if applet exists,
     * false and a java.lang.NullPointerException if not.
     */
    public boolean testIsAppletExistent(){
      boolean returnValue = true;
      try {
          c.write("");
      } catch (Exception e) {
          returnValue = false;
          System.out.println("AppletConnector.writeToApplet: Applet does not exist - sending was not possible. "+e.toString());
      }
      return returnValue;
    }

    /**
     * Sends a string back to the Applet, used for "AccessEventExisted"
     * and Browser name,
     * @param msgToSend The text string to send to applet
     */
    boolean writeToApplet(String msgToSend) {
        boolean returnValue = true;
        try {
            c.write(msgToSend);
        } catch (Exception e) {
            returnValue = false;
            System.out.println("AppletConnector.writeToApplet: Could not send \"" + msgToSend + "\" to Applet. "+e.toString());
        }
        return returnValue;
    }

    /**
     * Sends a string back to the Applet, containing the command to be executed
     * by the applet, an url if needed and a target if needed.
     * <BR>
     * e. g.
     * sendToApplet("OpenURL", "www.uni-hamburg.de", "_self");
     * <BR>
     * tells the applet to call another page in the browser.
     * <BR>
     * sendToApplet("BringToFront", "", "");
     * <BR>
     * tells the browser to become the top level window.
     *
     * @param command The command for the Applet (BringToFront or OpenURL) (not casesensitive)
     * @param url     The url needed for OpenURL. Send an empty String for other commands.
     * @param target  The target needet for OpenURL. Send an empty String for other commands.
     */
    public boolean sendToApplet(String command, String parameter1, String parameter2) {
        return writeToApplet("command=" + command.toLowerCase().trim() + "\nparameter1=" + parameter1.trim() + "\nparameter2=" + parameter2.trim() + "\n");
    }

    /**
     * This Method gets the value of the parameter paramName from the String
     * sent by the applet.
     * @param paramName The name of the parameter
     * @return The requested value
     */
    public String getParameter(String inputLine, String paramName) {
        int startPos;
        int endPos;

        if (inputLine.indexOf("\n" + paramName + "=") > -1) {
            startPos = inputLine.indexOf("\n" + paramName + "=")
                    + paramName.length() + 2;
            endPos = inputLine.indexOf("\n", startPos);
            return inputLine.substring(startPos, endPos);
        } else {
            System.out.println("Gesuchter Parameter " + paramName + " ist nicht im String " + inputLine + " enthalten!");
            return "";
        }
    }
}

