package scone.accesstracking.applet;


import java.applet.Applet;
import java.applet.AppletContext;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.TextArea;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;

import netscape.javascript.JSObject;
import scone.ras.Connection;


/**
 * This Applet reads the applet parameters, concats them to one string and 
 * sends it to the scone Remote Access Server. The Applet gets the destination 
 * for the socket connection from parameters. <br>
 * Everytime the method logMsg is called by javascript, a corrisponding 
 * string is sent to RAS. <br>
 * Transmissions of the RAS can execute JavaScript commands
 * 
 * @author Torsten Hass
 * @author Harald Weinreich
 * @version 1.5, 22-Feb-2002
 */
public class LogApplet extends Applet {

    /*
     History
     -------
     Version 1.5
     Netscape's Back-Button is reported to EventDecoder now.
     A more restricted version of scone.ras.Connection made the old
     applet not to connect to RAS when applet was restartet
     Version 1.6
     AccessTrackingMeg now uses the parameter AppletControl to pass a bit 
     array to the applet. This bit array tells the applet whether to create 
     an output window or not. In version 1.5 and earlier the parameter was
     called "ShowDebugWindow".
     This bit array also tells the applet whether to send data to the Applet-
     Connector or not. This is needet to use the IESpy programm to collect 
     the information about the site and the user events. The applet then 
     only receives commands to request a new site or to execute a JavaScript 
     command.
     */

    /*
     Receiving Statussaver is only required if EventDecoder or AppletConnector
     are used like singleton patterns (one instance for multiple connections)
     or when one connection uses several instances.
     Init it with {IDLE} if you want to receive statussaver from EventDecoder
     Init it with {DISABLED} if EventDecoder does not send statussaver
     */
    // status of StatusSaverReceived
    private final int IDLE = 0;    // String was not received yet and logMsg was not called yet
    private final int RECEIVED = 1;    // String was received before logMsg was called
    private final int WAITRECV = 2;    // logMsg was called, waiting for String to be received
    private final int DISABLED = 3;    // receiving statussaver is disabled
    private int receiveStatus[] = {DISABLED}; // It is an Array because I had to use
    // synchronized with it.

    // constants definition for appletControl
    public final static int AC_SHOWAPPLET = 1 << 0;     // Tells the applet to create a output window
    public final static int AC_DISABLEACCESSTRACKING = 1 << 1;     // Tells the applet not to send data to 
    // AppletConnector, but only listen
    // data is collected by IESpy
    private int    appletControl = 0;   // controls the applet's behaviour

    private String urlString = null;
    private String portString = null;
    private int    port = 8472;         // default port for socket connection
    private String appletStartTime;     // applet start time as a string
    private String accessKey = "";      // String to send to ras-Server
    private String nachricht;           // String from JavaScript
    private String dbKey = "";

    private Connection con = null;
    private ReceiverThread receiverThread;
    private boolean appletRunning = false;    // to detect if applet was restarted
    private boolean startWasRunning = false;  // to detect if method start was executed already
    private boolean connected = false;        // was connection to server successful?
    private boolean logMsgIsWaiting = false;
    private String storedMsg;                 // stores the Msg of JavaScript
    // if start was running too late.
    private TextArea textArea;
    netscape.javascript.JSObject win_;

    public void init() {

        win_ = JSObject.getWindow(this);
        appletStartTime = String.valueOf(new Date().getTime());
        appletRunning = false;
        try {
            appletControl = Integer.parseInt(getParameter("AppletControl"));
        } catch (NumberFormatException e) {
            appletControl = 0;
            System.err.println("Could not convert parameter AppletControl to int.");
            System.err.println("AppletControl is set to 0");
        }
        // Create output window if necessary
        if ((appletControl & AC_SHOWAPPLET) > 0) {
            textArea = new TextArea("", 6, 90);
            textArea.setEditable(false);

            // Add Components to the Applet.
            setLayout(new FlowLayout());
            setFont(new Font("Helvetica", Font.PLAIN, 10));
            add(textArea);

            textArea.append("init gestartet\n");
        }
        // read the applet parameters and put them into a string
        dbKey = "AppletStartTime=" + appletStartTime + "\n"; // start time of applet (used in case of page start time is out of date because its from the browser's cache
        dbKey += addParam("UserId");          // read userId from parameter
        dbKey += addParam("NodeId");          // read node id from parameter
        dbKey += addParam("StartTime"); 	   // read start time from parameter
        dbKey += addParam("Frame");           // read frame from parameter
        dbKey += addParam("ATMegTime");       // time of AccessTrackingMeg for back button recognition
        dbKey += addParam("FrameCount");      // Number of Frames used

        accessKey = "params\n" + dbKey;
        accessKey += addParam("Frame");           // read frame from parameter
        accessKey += addParam("Fragment");        // read fragment from parameter
        accessKey += addParam("Query"); 	      // read query from parameter
        accessKey += addParam("Post");            // Data of a posted form
        accessKey += addParam("ParentNodeName");  // read parent node name from parameter
        accessKey += addParam("ParentFrame");     // read parent frame name from parameter
        accessKey += addParam("Referrer");        // read Referrer from parameter
        // Next line dropped because IE bug gives wrong values. Work aroud with BrowserHistory
        // accessKey += addParam("History");         // read history length from parameter
        accessKey += addParam("IsIE");            // Is the Browser the Internet Explorer?
        accessKey += addParam("IsNS");            // Does the user prefer Netscape 4?

        urlString = getParameter("Scone IP");      // read destination url for the
        if (urlString == null) {                   // socket connection from parameter
            urlString = "localhost";
        }
        // read socketport from parameter
        portString = getParameter("Acesstracking1: Scone port for AppletConnection");
        if (portString == null) {
            portString = "8472";
        }
        // convert socket port number to Int
        try {
            port = Integer.parseInt(portString);
        } catch (NumberFormatException e) {
            port = 8472;
            System.err.println("Could not parse parameter Port.");
            System.err.println("Using defalt Port value: " + port);
        }
        // init socket connection
        try {
            con = new Connection(urlString, port, "scone.accesstracking.AppletConnector", true);
        } catch (Exception e) {
            System.err.println("Exception in LogApplet:Init while getting an instance of Connection");
            e.printStackTrace();
        }
    }

    public void start() {
        // Connect to RasServer
        try {
            con.wake();
        } catch (Exception e) {
            System.err.println("Exception in LogApplet:Start while connecting to RasServer");
            e.printStackTrace();
        }
        // Start Thread to receive data from server
        receiverThread = new ReceiverThread(con, this);
        receiverThread.start();
        // Are we connected to rasserver?
        try {
            if (!con.isClosed()) {
                connected = true;
            }
        } catch (Exception e) {
            System.err.println("Exception in LogApplet:start while testing if connection is established");
            e.printStackTrace();
        } finally {
            if ((appletControl & AC_SHOWAPPLET) > 0) {
                if (connected) {
                    textArea.append("Connected to " + urlString + "\n");
                } else {
                    textArea.append("Not connected to " + urlString + "\n");
                }
            }
        }
        // write data from parameters to socket
        if ((appletControl & AC_SHOWAPPLET) > 0) {
            textArea.append(accessKey + "\n");
        }
        try {
            con.write(accessKey);
        } catch (Exception e) {
            System.err.println("Exception in LogApplet:start while sending params");
            e.printStackTrace();
        }
        if ((appletControl & AC_SHOWAPPLET) > 0) {
            if (appletRunning) {
                textArea.append("Applet restarted (Back or Next used)\n");
            } else {
                textArea.append("Applet started\n");
            }
        }
        appletRunning = true;
        startWasRunning = true;
        if (logMsgIsWaiting) {
            logMsg(storedMsg);
        }
    }

    public void stop() {
        startWasRunning = false;
        try {
            con.sleep();
        } catch (Exception e) {
            System.err.println("Exception in LogApplet:stop");
            e.printStackTrace();
        }
    }

    public void destroy() {
        try {
            con.close();
        } catch (Exception e) {// System.err.println("Exception in LogApplet:destroy");
            // e.printStackTrace();
        }
    }

    /**
     * This method is called by ReceiverThread when a String from
     * the server was received. If logMsg was not called yet, it is
     * stored. Otherways, the received data is sent to the server
     * with additional information, received by logMsg from JavaScript
     *
     * @param receivedData The received StatusSaver string
     */
    public void statusSaverReceived(String receivedData) {
        dbKey += receivedData;
        synchronized (receiveStatus) {
            if (receiveStatus[0] == WAITRECV) {
                receiveStatus[0] = RECEIVED;     // For further calls of logMsg
                sendMsg();
            } else {
                receiveStatus[0] = RECEIVED;
            }
        }
    }

    /**
     * This method is called by an JavaScript function when the page
     * is completly loaded, a link was clicked, a form submitted or
     * the page is unloaded.
     */
    public void logMsg(String nachricht) {
        this.nachricht = nachricht;

        // Test if method start was already executed (IE sometimes calls
        // method loaded before method start)
        // System.out.println("LogMsg aufgerufen mit " + nachricht);
        if (startWasRunning) { // method start was running already
            if ((appletControl & AC_SHOWAPPLET) > 0) {
                textArea.append("logMsg: start was running\n");
            }
            // System.out.println("Applet wurde bereits ausgeführt");

            // Test if ReceiverThread already received the StatusSaver String
            // that holds information which has to be sent to the server
            synchronized (receiveStatus) {
                if (receiveStatus[0] != DISABLED) {
                    if (receiveStatus[0] == RECEIVED) { // StatusSaver was received already
                        sendMsg();
                        logMsgIsWaiting = false;
                    } else {    // StatusSaver was not received yet
                        receiveStatus[0] = WAITRECV;
                    }
                } else {
                    sendMsg();
                    logMsgIsWaiting = false;
                }
            }
        } else { // start was not running, yet
            logMsgIsWaiting = true;
            storedMsg = nachricht;
        }

    }

    /**
     * Sends a message to the server, corresponding to the string
     * received by logMsg
     */
    public void sendMsg() {
        if (nachricht.equals("loaded")) {
            // If the data is collected by IESpy, the applet must not send this event
            if ((appletControl & AC_DISABLEACCESSTRACKING) > 0) {
                return; // Don't care about JavaScript events if IESpy is used
            }
            nachricht += "\nLoadedTime=" + String.valueOf(new Date().getTime())
                    + "\n";
            if (receiveStatus[0] != DISABLED) {
                nachricht += dbKey;
            }
        }
        if (nachricht.equals("stopped")) {
            nachricht += "\nStoppedTime=" + String.valueOf(new Date().getTime())
                    + "\n";
            if (receiveStatus[0] != DISABLED) {
                nachricht += dbKey;
            }
        }
        if (nachricht.startsWith("link")) {
            // If the data is collected by IESpy, the applet must not send this event
            if ((appletControl & AC_DISABLEACCESSTRACKING) > 0) {
                return; // Don't care about JavaScript events if IESpy is used
            }
            nachricht = "link\nLinkId=" + nachricht.substring(4) + "\n";
            nachricht += "LinkTime=" + String.valueOf(new Date().getTime())
                    + "\n";
            if (receiveStatus[0] != DISABLED) {
                nachricht += dbKey;
            }
        }
        if (nachricht.startsWith("form")) {
            // If the data is collected by IESpy, the applet must not send this event
            if ((appletControl & AC_DISABLEACCESSTRACKING) > 0) {
                return; // Don't care about JavaScript events if IESpy is used
            }
            nachricht = "form\nAction=" + nachricht.substring(4) + "\n";
            nachricht += "FormTime=" + String.valueOf(new Date().getTime())
                    + "\n";
            if (receiveStatus[0] != DISABLED) {
                nachricht += dbKey;
            }
        }
        if ((appletControl & AC_SHOWAPPLET) > 0) {
            textArea.append(nachricht + "\n");
        }
        try {
            con.write(nachricht);
            if ((appletControl & AC_SHOWAPPLET) > 0) {
                textArea.append("... gesendet\n");
            }
        } catch (Exception e) {
            System.err.println("Exception in LogApplet:sendMsg while sending a String to RAS");
            e.printStackTrace();
        }
    }

    /**
     * This method reads the applet parameter and concatinates the
     * parameter name,  a "=", the parameter value and a "\n"
     * @param paramName The name of the parameter
     * @return the concatinated string like "Name=Value\n"
     */
    protected String addParam(String paramName) {
        String myParameter = "";

        if (getParameter(paramName) != null) {            // test if parameter exists
            myParameter = paramName + "=" + getParameter(paramName) + "\n";
        } else {                                              // no parameter!
            myParameter = paramName + "=\n";
            
        }
        return myParameter;
    }

    /**
     * This method asks the browser to open a new URL immidiately
     *
     * @param url The url to open in browser
     * @param target The target frame to open the new url in
     */
    public void openURL(String url, String target) {
        System.out.println("LogApplet: openURL " + url + " " + target);
        AppletContext appletContext = getAppletContext();

        try {
            URL Url = new URL(url);

            appletContext.showDocument(Url, target);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    } 
    
    /**
     * This method asks the browser to open a new URL<BR>
     * This time JavaScript is used and the URL is open delayed, e.g. 
     * to allow a bringToFront first.
     *
     * @see scone.accesstracking.AccessTrackingMeg for the delay time etc.
     * @param url The url to open in browser
     */
    public void openURLDelayed(String url) {
        System.out.println("LogApplet: openURLDelayed " + url);
        win_.call("_scone_openURL", new String[] {url});
    }

    /**
     * This method asks the browser to bring it's window to front
     */
    public void bringToFront() {
        System.out.println("LogApplet: bringToFront");
        win_.call("_scone_bringWindowToFront", null);
    }

    /**
     * This method asks the browser to blur it's window (put it into background)
     */
    public void blurBrowser() {
        System.out.println("LogApplet: blurBrowser");
        win_.call("_scone_blurBrowser", null);
    }

    /**
     * This method asks the browser to close it's current window
     */
    public void closeBrowser() {
        System.out.println("LogApplet: closeBrowser");
        win_.call("_scone_closeBrowser", null);
    }

    /**
     * This method asks the browser to call any function
     */
    public void anyFunction(String function) {
        System.out.println("LogApplet: anyFunction");
        win_.call("_scone_anyFunction", new String[] {function});
    }

}

