package scone.accesstracking;


import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import scone.netobjects.Link;
import scone.netobjects.LinkCache;
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 IESpyConnector implements ConnectionHandler {

    private Connection c;
    private Map[] dataMap = new Map[21];
    private Map[] eventDecoderMap = new Map[21];
    private IESpyLastAction[] lastAction = new IESpyLastAction[21];
    private IESpyEventDecoder constants; // an Instance of EventDecoder to access the constants
    private boolean showMessages;

    public IESpyConnector() {}

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

        String inputLine = null;
        String frameName = null;
        String timeString = null;
        IESpyEventDecoder theEventDecoder = null;
        IESpyData theIeData = null;
        int ieNumber = 0;
        int startPos;
        int endPos;
        int lastGlobalAction = 0;		// needed to save 'open in new window' state for next new window

        System.out.println("\n-> Scone: Connection established to IESpy");

        // 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
            } else {
                showMessages = false;
            }
        } catch (Exception exc) {
            ErrorLog.log(this, "AppletConnector: Error while reading Scone properties", "", exc);
        }

        try {
            while ((inputLine = c.read()) != null) {
                //System.out.println("Empf:" + inputLine);
                try {
                    ieNumber = Integer.parseInt(inputLine.substring(20, inputLine.indexOf(';', 20)));
                    if ((ieNumber >= 0) && (ieNumber <= 20)) {
                        if (dataMap[ieNumber] == null) {
                            dataMap[ieNumber] = new HashMap();
                            eventDecoderMap[ieNumber] = new HashMap();
                            lastAction[ieNumber] = new IESpyLastAction(constants.NEWWINDOW);
                        } 
                    }
                } catch (NumberFormatException e) {
                    ieNumber = -1;
                } catch (StringIndexOutOfBoundsException se) {
                    ieNumber = -1;
                }
                try {
                    timeString = inputLine.substring(0, 12);
                } catch (Exception  e) {
                    timeString = "";
                }   
                if ((inputLine.indexOf("BNAV; \"")) > -1) {} else if ((inputLine.indexOf("IEOF; "))
                        > -1) {
                    lastAction[ieNumber].set(constants.NEWWINDOW); // initialize lastAction Object for next Window
                } else if ((startPos = inputLine.indexOf("STXT; \"")) > -1) {
                    // Get Information from Applet via browser's status text line
                    // get Framename
                    if (inputLine.indexOf(":Scone") > -1) { // is it a scone message?
                        endPos = inputLine.indexOf(":Scone");
                        frameName = inputLine.substring(startPos + 7, endPos);
                        if ((ieNumber >= 0) && (ieNumber <= 20)) {
                            if (dataMap[ieNumber].containsKey(frameName)) {
                                theIeData = (IESpyData) dataMap[ieNumber].get(frameName);   // get objects
                                theEventDecoder = (IESpyEventDecoder) eventDecoderMap[ieNumber].get(frameName);
                            } else {
                                dataMap[ieNumber].put(frameName, new IESpyData());// create new objects
                                eventDecoderMap[ieNumber].put(frameName, new IESpyEventDecoder());
                                theIeData = (IESpyData) dataMap[ieNumber].get(frameName);   // get objects
                                theEventDecoder = (IESpyEventDecoder) eventDecoderMap[ieNumber].get(frameName);
                            }
                        }
                        startPos = endPos + 1;
                        endPos = inputLine.lastIndexOf("\";");
                        try {
                           inputLine = inputLine.substring(startPos, endPos);
                        } catch (StringIndexOutOfBoundsException siob) {
                           siob.printStackTrace();
                           System.out.println("Exception: startPos="+startPos);
                           System.out.println("Exception: endPos="+endPos);
                           System.out.println("Exception: inputLine="+inputLine);
                        }
   
                        // Get messages needed for appletStarted
                        if (inputLine.startsWith("SconeStartTime=")) {
                            Calendar calendar = Calendar.getInstance();

                            calendar.setTime(new Date());
                            calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(timeString.substring(0, 2)));
                            calendar.set(Calendar.MINUTE, Integer.parseInt(timeString.substring(3, 5)));
                            calendar.set(Calendar.SECOND, Integer.parseInt(timeString.substring(6, 8)));
                            calendar.set(Calendar.MILLISECOND, Integer.parseInt(timeString.substring(9, 12)));
                     
                            Date date = calendar.getTime();

                            theIeData.setStartTime(date.getTime());
                            if ((lastGlobalAction != 0)
                                    && (lastAction[ieNumber].get()
                                            == constants.NEWWINDOW)) {  // if user clicked on 'Open in new window' and this is a new window, use this new Action
                                lastAction[ieNumber].set(lastGlobalAction); 
                                lastGlobalAction = 0;
                            }	
                            printMsg("IESpyConnector: StartTime: " + theIeData.getStartTime());
                        } else if (inputLine.startsWith("SconeUserId=")) {
                            theIeData.setUserId(inputLine.substring((inputLine.indexOf("=") + 1))); 
                            printMsg("IESpyConnector: UserId: " + theIeData.getUserId());
                            theIeData.setFrameName(frameName); 
                            printMsg("IESpyConnector: FrameName: " + theIeData.getFrameName());
                        } else if (inputLine.startsWith("SconeNodeId=")) {
                            theIeData.setNodeId(inputLine.substring((inputLine.indexOf("=") + 1))); 
                            printMsg("IESpyConnector: NodeId: " + theIeData.getNodeId());
                        } else if (inputLine.startsWith("SconeFragment=")) {
                            theIeData.setFragment(inputLine.substring((inputLine.indexOf("=") + 1))); 
                            printMsg("IESpyConnector: Fragment:" + theIeData.getFragment());
                        } else if (inputLine.startsWith("SconeQuery=")) {
                            theIeData.setQuery(inputLine.substring((inputLine.indexOf("=") + 1))); 
                            printMsg("IESpyConnector: Query: " + theIeData.getQuery());
                        } else if (inputLine.startsWith("SconeParentNodeName=")) {
                            theIeData.setParentNodeName(inputLine.substring((inputLine.indexOf("=") + 1))); 
                            printMsg("IESpyConnector: ParentNodeName: " + theIeData.getParentNodeName());
                        } else if (inputLine.startsWith("SconeParentFrameName=")) {
                            theIeData.setParentFrameName(inputLine.substring((inputLine.indexOf("=") + 1))); 
                            printMsg("IESpyConnector: ParentFrameName: " + theIeData.getParentFrameName());
                        } else if (inputLine.startsWith("SconeReferrer=")) {
                            theIeData.setReferrer(inputLine.substring((inputLine.indexOf("=") + 1))); 
                            printMsg("IESpyConnector: Referrer: " + theIeData.getReferrer());
                        } else if (inputLine.startsWith("SconeFrameCount=")) {
                            int frameCountNumber;

                            try {
                                frameCountNumber = Integer.parseInt(inputLine.substring((inputLine.indexOf("=") + 1)));
                            } catch (Exception excep) {
                                excep.printStackTrace();
                                frameCountNumber = -1;
                            }
                            theIeData.setFrameCount(frameCountNumber); 
                            printMsg("IESpyConnector: FrameCount" + theIeData.getFrameCount());
                        } // Get message needed for pageLoaded
                        else if (inputLine.startsWith("SconeLoadTime=")) {
                            theEventDecoder.pageLoaded(Long.parseLong(inputLine.substring((inputLine.indexOf("=") + 1)))); 
                            printMsg("IESpyConnector: Page loaded");
                        } // Get message needed for LinkClicked
                        else if (inputLine.startsWith("SconeLinkId=")) {
                            long linkTime;
                            Calendar calendar = Calendar.getInstance();

                            calendar.setTime(new Date());
                            calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(timeString.substring(0, 2)));
                            calendar.set(Calendar.MINUTE, Integer.parseInt(timeString.substring(3, 5)));
                            calendar.set(Calendar.SECOND, Integer.parseInt(timeString.substring(6, 8)));
                            calendar.set(Calendar.MILLISECOND, Integer.parseInt(timeString.substring(9, 12)));
                            Date date = calendar.getTime();

                            linkTime = date.getTime();
                            // System.out.println( DateFormat.getDateInstance().format(date));

                            // look for the target of this link 
                            String linkId = inputLine.substring(inputLine.indexOf("=") + 1);

                            if (theIeData.getFrameCount() == 0) {
                                lastAction[ieNumber].set(constants.LINK);
                                theEventDecoder.linkClicked(linkId, linkTime); 
                                printMsg("IESpyConnector: Link Clicked, LinkId: " + linkId);
                            } else {   
                                Link link = LinkCache.getById(linkId);
                                String targetFrameName = link.getTarget().toLowerCase();

                                if (targetFrameName.equals("_parent")) {} else if (targetFrameName.equals("_top")) {} else if ((targetFrameName.equals(""))
                                        || (targetFrameName.equals("_self"))) { // normal link in same frame
                                    lastAction[ieNumber].set(constants.LINK);
                                    theEventDecoder.linkClicked(linkId, linkTime); 
                                    printMsg("IESpyConnector: Link in Frame Clicked, LinkId: " + linkId);
                                } else {  // targetFrameName contains a frameName
                                    // call linkClicked in that IESpyEventDecoder 
                                    Iterator iterator = eventDecoderMap[ieNumber].keySet().iterator();
                                    String frame = null;

                                    while (iterator.hasNext()) {
                                        frame = (String) iterator.next();
                                        // System.out.println(frameName);
                                        if (frame.toLowerCase().equals(targetFrameName)) {
                                            IESpyEventDecoder evDec = (IESpyEventDecoder) eventDecoderMap[ieNumber].get(frame);

                                            lastAction[ieNumber].set(constants.LINK);
                                            evDec.linkClicked(linkId, linkTime); 
                                            printMsg("IESpyConnector: Link in Frame: " + frame + " clicked, LinkId: " + linkId);
                                        }
                                    }                                 
                                }
                            }
                        } // Get message needed for FormSubmitted
                        else if (inputLine.startsWith("SconeFormAction=")) {
                            theEventDecoder.formSubmited(theEventDecoder.FORMSUBMIT, 
                                    inputLine.substring(inputLine.indexOf("=") + 1)); 
                            printMsg("IESpyConnector: Form Submited");
                        } // Get message needed for unloading page
                        else if (inputLine.startsWith("SconeStopTime=")) {
                            theEventDecoder.pageUnloaded(new Date().getTime()); 
                            printMsg("IESpyConnector: Applet Stopped");
                        }
                    }
                } else {
                    // System.out.println("Empf:" + inputLine);
                }
                if ((ieNumber >= 0) && (ieNumber <= 20)) {
                    if (dataMap[ieNumber] != null) {
                        if (dataMap[ieNumber].containsKey(frameName)) {
                            theIeData = (IESpyData) dataMap[ieNumber].get(frameName);   // get objects
                            theEventDecoder = (IESpyEventDecoder) eventDecoderMap[ieNumber].get(frameName);
                            if (theIeData.startedComplete()) {

                                // System.out.println("********** JavaScriptStarted");
                                theEventDecoder.javaScriptStarted(theIeData.getStartTime(),
                                        theIeData.getUserId(), 
                                        theIeData.getNodeId(),
                                        theIeData.getFragment(), 
                                        theIeData.getQuery(), 
                                        theIeData.getFrameName(),
                                        theIeData.getParentNodeName(), 
                                        theIeData.getParentFrameName(), 
                                        theIeData.getReferrer(), 
                                        theIeData.getFrameCount(),
                                        showMessages, 
                                        lastAction[ieNumber]);

                                theIeData.clearAppletStarted();
                            }
                        }
                        if ((inputLine.indexOf("ABED; ")) > -1) {
                            // only set to Addressbar, if LinkButton is not set already
                            // System.out.println(lastAction[ieNumber].get());
                            if (((lastAction[ieNumber].get()
                                                    & constants.LINKBUTTON)
                                            == 0)
                                    && ((lastAction[ieNumber].get()
                                                    & constants.BOOKMARK)
                                            == 0)
                                    && ((lastAction[ieNumber].get()
                                                    & constants.HOME)
                                            == 0)) {
                                lastAction[ieNumber].set(constants.ADDRESSBAR); 
                                printMsg("IESpyConnector: AdressBar");
                            }
                        }   
                        if ((inputLine.indexOf("FAVS; ")) > -1) {
                            lastAction[ieNumber].set(constants.BOOKMARK | constants.MAINMENU); 
                            printMsg("IESpyConnector: Bookmark");
                        }   
                        if ((inputLine.indexOf("LBIS; ")) > -1) {
                            lastAction[ieNumber].set(constants.LINKBUTTON); 
                            printMsg("IESpyConnector: LinkButton");
                        }   
                        if ((inputLine.indexOf("EBIS; ")) > -1) {
                            if (((lastAction[ieNumber].get()
                                                    & constants.NEWWINDOW)
                                            == 0)
                                    && ((lastAction[ieNumber].get()
                                                    & constants.LINKBUTTON)
                                            == 0)
                                    && ((lastAction[ieNumber].get()
                                                    & constants.BOOKMARK)
                                            == 0)
                                    && ((lastAction[ieNumber].get()
                                                    & constants.ADDRESSBAR)
                                            == 0)
                                    && ((lastAction[ieNumber].get()
                                                    & constants.HOME)
                                            == 0)) {
                                lastAction[ieNumber].set(constants.EXPLORERBAR); 
                                printMsg("IESpyConnector: ExplorerBar");
                            }
                        } else if ((inputLine.indexOf("TBBC; ")) > -1) { // Tool bar button clicked
                            if (inputLine.indexOf("Zur ck") > -1) {
                                lastAction[ieNumber].set(constants.BACK | constants.TBBUTTON); 
                                printMsg("IESpyConnector: Toolbar button: back");
                            } else if (inputLine.indexOf("Vor") > -1) {
                                lastAction[ieNumber].set(constants.NEXT | constants.TBBUTTON); 
                                printMsg("IESpyConnector: Toolbar button: next");
                            } else if (inputLine.indexOf("Aktualisieren") > -1) {
                                lastAction[ieNumber].set(constants.RELOAD | constants.TBBUTTON); 
                                printMsg("IESpyConnector: Toolbar button: reload");
                            } else if (inputLine.indexOf("Startseite") > -1) {
                                lastAction[ieNumber].set(constants.HOME | constants.TBBUTTON); 
                                printMsg("IESpyConnector: Toolbar button: home");
                            }
                        } else if ((inputLine.indexOf("MSTB; ")) > -1) { // Tool bar button clicked
                            if (inputLine.indexOf("Zur ck") > -1) {
                                startPos = inputLine.indexOf(';', inputLine.indexOf("Zur ck"))
                                        + 1;
                                endPos = inputLine.indexOf(';', startPos);
                                int steps;

                                try {
                                    steps = Integer.parseInt(inputLine.substring(startPos + 1, endPos));
                                } catch (NumberFormatException nfe) {
                                    steps = 1;
                                }
                                if (steps > 1) {  
                                    lastAction[ieNumber].set(constants.BACK | constants.TBMENU | constants.SKIPPED);
                                    lastAction[ieNumber].setStepsInHistory(steps);
                                    printMsg("IESpyConnector: Toolbar menu: back multi (" + steps + " step(s))");
                                } else {
                                    lastAction[ieNumber].set(constants.BACK | constants.TBMENU);
                                    lastAction[ieNumber].setStepsInHistory(steps);
                                    printMsg("IESpyConnector: Toolbar menu: back");
                                }
                            }
                            if (inputLine.indexOf("Vor") > -1) {
                                startPos = inputLine.indexOf(';', inputLine.indexOf("Vor"))
                                        + 1;
                                endPos = inputLine.indexOf(';', startPos);
                                int steps;

                                try {
                                    steps = Integer.parseInt(inputLine.substring(startPos + 1, endPos));
                                } catch (NumberFormatException nfe) {
                                    steps = 1;
                                }
                                if (steps > 1) {  
                                    lastAction[ieNumber].set(constants.NEXT | constants.TBMENU | constants.SKIPPED);
                                    lastAction[ieNumber].setStepsInHistory(steps);
                                    printMsg("IESpyConnector: Toolbar menu: next multi (" + steps + " step(s))");
                                } else {
                                    lastAction[ieNumber].set(constants.NEXT | constants.TBMENU);
                                    lastAction[ieNumber].setStepsInHistory(steps);
                                    printMsg("IESpyConnector: Toolbar menu: next");
                                }
                            }
                        } else if ((inputLine.indexOf("MSMM; ")) > -1) { // Menu select in main menu
                            if (inputLine.indexOf("/Zur ck") > -1) {
                                lastAction[ieNumber].set(constants.BACK | constants.MAINMENU); 
                                lastAction[ieNumber].setStepsInHistory(1);
                                printMsg("IESpyConnector: Main menu: back");
                            } else if (inputLine.indexOf("/Vor") > -1) {
                                lastAction[ieNumber].set(constants.NEXT | constants.MAINMENU); 
                                lastAction[ieNumber].setStepsInHistory(1);
                                printMsg("IESpyConnector: Main menu: next");
                            } else if (inputLine.indexOf("/Aktualisieren") > -1) {
                                lastAction[ieNumber].set(constants.RELOAD | constants.MAINMENU); 
                                printMsg("IESpyConnector: Main menu: reload");
                            } else if (inputLine.indexOf("/StartSeite") > -1) {
                                lastAction[ieNumber].set(constants.HOME | constants.MAINMENU); 
                                printMsg("IESpyConnector: Main menu: home");
                            } else if (inputLine.indexOf("Seiten zur ck") > -1) {
                                // 12:38:35.985; MSMM; 0; "Ansicht/Wechseln zu/<3 Seiten vor>";
                                startPos = inputLine.indexOf("zu/<") + 4;
                                int steps;

                                try {
                                    steps = Integer.parseInt(inputLine.substring(startPos, startPos + 2).trim());
                                } catch (NumberFormatException nfe) {
                                    steps = 1;
                                }
                                lastAction[ieNumber].set(constants.BACK | constants.SKIPPED | constants.MAINMENU); 
                                lastAction[ieNumber].setStepsInHistory(steps);
                                printMsg("IESpyConnector: Main menu: back, " + steps + " steps");
                            } else if (inputLine.indexOf("Seiten vor") > -1) {
                                startPos = inputLine.indexOf("zu/<") + 4;
                                int steps;

                                try {
                                    steps = Integer.parseInt(inputLine.substring(startPos, startPos + 2).trim());
                                } catch (NumberFormatException nfe) {
                                    steps = 1;
                                }
                                lastAction[ieNumber].set(constants.NEXT | constants.SKIPPED | constants.MAINMENU); 
                                lastAction[ieNumber].setStepsInHistory(steps);
                                printMsg("IESpyConnector: Main menu: next, " + steps + " steps");
                            } else if (inputLine.indexOf("Seite zur ck") > -1) {
                                lastAction[ieNumber].set(constants.BACK | constants.MAINMENU); 
                                lastAction[ieNumber].setStepsInHistory(1);
                                printMsg("IESpyConnector: Main menu: back");
                            } else if (inputLine.indexOf("Seite vor") > -1) {
                                lastAction[ieNumber].set(constants.NEXT | constants.MAINMENU); 
                                lastAction[ieNumber].setStepsInHistory(1);
                                printMsg("IESpyConnector: Main menu: next");
                            }
                        } else if ((inputLine.indexOf("MSHA; ")) > -1) { // Menu select in html area
                            if (inputLine.indexOf("Zurueck") > -1) {
                                lastAction[ieNumber].set(constants.BACK | constants.HTMLMENU); 
                                printMsg("IESpyConnector: html menu: back");
                            } else if (inputLine.indexOf("Vor") > -1) {
                                lastAction[ieNumber].set(constants.NEXT | constants.HTMLMENU); 
                                printMsg("IESpyConnector: html menu: next");
                            } else if (inputLine.indexOf("Aktualisieren") > -1) {
                                lastAction[ieNumber].set(constants.RELOAD | constants.HTMLMENU); 
                                printMsg("IESpyConnector: Main menu: reload");
                            }
                            // Empf:19:52:33.831; MSHA; 0; "In neuem Fenster oeffnen";
                            if (inputLine.indexOf("In neuem Fenster oeffnen")
                                    > -1) {
                                lastGlobalAction = (constants.NEWWINDOW
                                        | constants.HTMLMENU); 
                                printMsg("IESpyConnector: html menu: open link in new window");
                            }
                        } else if ((inputLine.indexOf("HOTK; ")) > -1) { // Menu select in html area
                            if (inputLine.indexOf("Zurueck") > -1) {
                                lastAction[ieNumber].set(constants.BACK | constants.HOTKEY); 
                                printMsg("IESpyConnector: hotkey: back");
                            } else if (inputLine.indexOf("Vor") > -1) {
                                lastAction[ieNumber].set(constants.NEXT | constants.HOTKEY); 
                                printMsg("IESpyConnector: hotkey: next");
                            } else if (inputLine.indexOf("Aktualisieren") > -1) {
                                lastAction[ieNumber].set(constants.RELOAD | constants.HOTKEY); 
                                printMsg("IESpyConnector: hotkey: reload");
                            }
                        }   
                    }
                }            

            }
            c.close();
        } catch (IOException e) {
            // System.out.println("IOException");
            // e.printStackTrace();
            System.out.println("\n-> Scone: Connection to IESpy closed");

        } catch (Exception ex) {
            System.out.println("Exception");
            ex.printStackTrace();
        }
    }

    /**
     * Sends a string back to the Applet, used for "AccessEventExisted"
     * and Browser name,
     * @param msgToSend The text string to send to applet
     */
    void writeToApplet(String msgToSend) {
        try {
            c.write(msgToSend);
        } catch (Exception e) {}
    }

    /**
     * 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 void sendToApplet(String command, String parameter1, String parameter2) {
        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 "";
        }
    }

    public void printMsg(String textMsg) {
        if (showMessages) {
            System.out.println(textMsg);
        }
    }

}
