/*
 * 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.examples.observertest;


import java.util.Enumeration;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import java.util.Hashtable;

import scone.Plugin;
import scone.accesstracking2.AccessTracking;
import scone.netobjects.AccessCache;
import scone.netobjects.AccessEvent;
import scone.netobjects.DBTableAdapter;
import scone.netobjects.HtmlNode;
import scone.netobjects.HtmlNodeCache;
import scone.netobjects.Inclusion;
import scone.netobjects.InclusionCache;
import scone.netobjects.LinkCache;
import scone.netobjects.LinkVector;
import scone.netobjects.NetNode;
import scone.netobjects.NetNodeCache;


public class ObserverTestPlugin extends Plugin implements Observer {

    // requirements
    public int getRequirements() {
        if (this.getProperties().get("Parse Links and Inclusions").equals("true")) {
            return ACCESSTRACKING | PARSEDOCUMENT | CONSIDERLINKS
                    | CONSIDERINCLUSIONS | CONSIDERKEYWORDS;
        } else {
            if (this.getProperties().get("Use Accesstracking 2 without Applet").equals("true")) {
                return ACCESSTRACKING2 | PARSEDOCUMENT | CALCFINGERPRINT; // ACCESSTRACKING2 ist faster but has less features.
            } else {
                return ACCESSTRACKING | PARSEDOCUMENT | CALCFINGERPRINT;
            }
        }
    }

    public ObserverTestPlugin() {}

    // This method is always called when a plugin is installed in Scone (i.e., during startup).
    public void init() {

        // diesen Observer anmelden
        if (this.getProperties().get("Observe NetNodes").equals("true")) {
            NetNodeCache.putObserver(this);
        }
        if (this.getProperties().get("Observe HtmlNodes").equals("true")) {
            HtmlNodeCache.putObserver(this);
        }
        if (this.getProperties().get("Observe Page Accesses").equals("true")) {
            AccessCache.putObserver(this);
        }
        if (this.getProperties().get("Show Known Backlinks").equals("true")) {
            AccessCache.putObserver(this);
        }

        // notify every 30 seconds:
        HtmlNodeCache.setObservePeriod(1000 * 30);
    }

    // it can be assumed, that o is an instance of a scone.netobjects.XXXCache-Class is.
    // arg is an enumeration if netobjects
    public void update(Observable o, Object arg) {
        // System.out.println("Event for ObserverTestFast!");

        // yes, the NetNodeCache notifies us!
        if (o instanceof NetNodeCache) {
            NetNode netNodeBuffer;

            // just print all new NetNode objects to the screen
            for (Enumeration e = (Enumeration) arg; e.hasMoreElements();) {
                netNodeBuffer = ((NetNode) e.nextElement());
                System.out.println("OT: New NetNode(" + netNodeBuffer.getNodeId() + "): " + netNodeBuffer.getUri());
            }

        } else if (o instanceof HtmlNodeCache) {
            HtmlNode htmlNodeBuffer;

            // just print all new HtmlNode objects to the screen
            for (Enumeration e = (Enumeration) arg; e.hasMoreElements();) {
                htmlNodeBuffer = ((HtmlNode) e.nextElement());
                System.out.println("OT: New HtmlNode(" + htmlNodeBuffer.getNodeId() + "): " + htmlNodeBuffer.getUri());
            }
        } else if (o instanceof AccessCache) {
            AccessEvent e = (AccessEvent) arg;

            // Special Firefox events...
            if (this.getProperties().get("Use Accesstracking 2 without Applet").equals("true") &&
                ((e.getAccess().getAction() & 1 << 27) > 0) ) {
                System.out.print("\nOT: User <");
                System.out.print(e.getAccess().getUserId());
                System.out.print(">");
                System.out.println(" [firefox event]");
                System.out.println("   Event: "+e.getAccess().getEvent());
                System.out.println("  nodeID: "+e.getAccess().getNodeId());
                if (e.getNode() != null) {
                    System.out.println("     URL: " + e.getNode().toString());
                    HtmlNode hNode = HtmlNodeCache.check(e.getNode());
                    if (hNode != null) {
                        System.out.println("          " + hNode.getTitle());
                    }
                }                                        
                System.out.println("   Frame: " + e.getAccess().getFrameName());
           		System.out.println("    Time: " + e.getAccess().getTime() + "ms");
               
            // Does the Netnode with the most important infomation (still) exist!?
            } else if (e.getNode() == null) {
                if (this.getProperties().get("Show verbose information").equals("true")) {
                    if (DBTableAdapter.useDb()) {
                        System.out.println("OT: Unknown NetNode! Please restart Scone and clear cache of browser!");
                    } else {
                        System.out.println("OT: Unknown NetNode! Database is deactivated, NetNode was already removed by GC.");
                    }
                    System.out.print("    "+e.getAccess().getNodeId()+":  ");
                    System.out.println(NetNodeCache.getById(e.getAccess().getNodeId()));
                }
            // All other events createed by scone...
            } else {   // it does :-)
                HtmlNode hNode = HtmlNodeCache.check(e.getNode());

                // Show the event
                if (!e.getAccess().getShown()) {  // Was this event already displayed?
                    e.getAccess().setShown(true); // Now it was... :-)
                    // Show user
                    if (e.getUser() != null
                            && !e.getUser().getUserId().equals("0")
                            && !e.getUser().getUserName().equals("")) {
                        System.out.print("\nOT: " + e.getUser().getUserName());
                    } else {
                        System.out.print("\nOT: User <");
                        System.out.print(e.getAccess().getUserId());
                        System.out.print(">");
                    }
                    // Show URI
                    System.out.println(" accessed");
                    System.out.println(" " + e.getNode().toString());

                    if ((e.getAccess().getAction() & 1 << 1) > 0) { // Link with "#"
                        System.out.println(" Fragment: " + e.getAccess().getFragment());
                    }
                    
                    // Show Title or Mime Type (for non-html documents)
                    if (hNode != null) {
                        System.out.println(" " + hNode.getTitle());
                        // Show parent frame information 
                        if (!e.getAccess().getParentFrameName().equals("") || !e.getAccess().getParentFrameNodeId().equals("")) {
                            System.out.println(" Frame  : " + e.getAccess().getFrameName());
                            System.out.println(" Parent : " + e.getAccess().getParentFrameName());
                            NetNode parent = NetNodeCache.getById(e.getAccess().getParentFrameNodeId());
                            if (parent==null)
                                System.out.println("      ID: " + e.getAccess().getParentFrameNodeId());
                            else
                                System.out.println("     URI: " + parent.toString());
                        } else { // No frames
                            System.out.println(" Window : " + e.getAccess().getFrameName());
                        }
                    } else {
                        System.out.println(" Type   : " + e.getNode().getMimeType());
                    }

                    // Show status for error pages...
                    if (!e.getNode().getAccessStatus().equals("200") && !e.getNode().getAccessStatus().equals("")) {
                        System.out.print("  Status: " + e.getNode().getAccessStatus()+ " - ");
                        System.out.println(statusTable.get(e.getNode().getAccessStatus()));
                    }

                    // Show referrer
                    if (this.getProperties().get("Show verbose information").equals("true") &&
                    		!e.getAccess().getReferrerNodeId().equals("")) {
                        NetNode referrer = NetNodeCache.getById(e.getAccess().getReferrerNodeId());
                        if (referrer==null)
                            System.out.println(" Referer: " + e.getAccess().getReferrerNodeId());
                        else
                            System.out.println(" Referer: " + referrer.toString());
                    }

                    // Show user action -> See EventDecoder
                    System.out.print(" Action :"); // + Integer.toBinaryString(e.getAccess().getAction()));
                    // System.out.print(" " + Integer.toBinaryString(e.getAccess().getAction()) +" ");
                    if ((e.getAccess().getAction() & 1 << 0) > 0) {
                        System.out.print(" link");
                    }
                    if ((e.getAccess().getAction() & 1 << 1) > 0) { // Link with "#"
                        System.out.print(" #fragment");
                    }
                    if ((e.getAccess().getAction() & 1 << 2) > 0) {
                        System.out.print(" samePage");
                    }
                    if ((e.getAccess().getAction() & 1 << 3) > 0) {
                        System.out.print(" submit");
                    }
                    if ((e.getAccess().getAction() & 1 << 4) > 0) {
                        System.out.print(" reloaded");
                    }
                    if ((e.getAccess().getAction() & 1 << 5) > 0) {
                        System.out.print(" back");
                    }
                    if ((e.getAccess().getAction() & 1 << 6) > 0) {
                        System.out.print(" next");
                    }
                    if ((e.getAccess().getAction() & 1 << 7) > 0) {
                        System.out.print(": " + e.getAccess().getStepsInHistory() + " steps");
                    }
                    if ((e.getAccess().getAction() & 1 << 8) > 0) {
                        System.out.print(" new window");
                    }
                    if ((e.getAccess().getAction() & 1 << 9) > 0) {
                        System.out.print(" bookmark");
                    }
                    if ((e.getAccess().getAction() & 1 << 10) > 0) {
                        System.out.print(" linkbutton");
                    }
                    if ((e.getAccess().getAction() & 1 << 11) > 0) {
                        System.out.print(" adressbar");
                    }
                    if ((e.getAccess().getAction() & 1 << 12) > 0) {
                        System.out.print(" home");
                    }
                    if ((e.getAccess().getAction() & 1 << 13) > 0) {
                        System.out.print(" history or favorites");
                    }
                    if ((e.getAccess().getAction() & 1 << 14) > 0) {
                        System.out.print(" new frame");
                    }
                    if ((e.getAccess().getAction() & 1 << 15) > 0) {
                        if (e.getAccess().getParentFrameName().equals(""))
                            System.out.print(" typed URL or Bookmark");
                        else
                            System.out.print(" frame load");
                    }
                    if ((e.getAccess().getAction() & 1 << 22) > 0) {
                        System.out.print(" by hotkey");
                    }
                    if ((e.getAccess().getAction() & 1 << 23) > 0) {
                        System.out.print(" by html menu");
                    }
                    if ((e.getAccess().getAction() & 1 << 24) > 0) {
                        System.out.print(" by main menu");
                    }
                    if ((e.getAccess().getAction() & 1 << 25) > 0) {
                        System.out.print(" by toolbar button");
                    }
                    if ((e.getAccess().getAction() & 1 << 26) > 0) {
                        System.out.print(" by toolbar menu");
                    }
                    if ((e.getAccess().getAction() & 1 << 30) > 0) {
                        System.out.print(" (by heuristic)");
                    }
                    if ((e.getAccess().getAction() & 1 << 27) > 0) {
                      	// For old Accesstracking... (2 uses this for firefox events...!)
                        System.out.print(" [netscape 4]");
                    }
                    if ((e.getAccess().getAction() & 1 << 28) > 0) {
                        System.out.print(" [ms explorer]");
                    }
                    if ((e.getAccess().getAction() & 1 << 29) > 0) {
                        System.out.print(" [gecko]");
                    }
                    System.out.println("");

                    if (this.getProperties().get("Show verbose information").equals("true")) {
                    	if (e.getAccess().getLinkType()!=0) {
                    		System.out.print(  " L-Type : ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.SAMEDOCUMENT ) > 0) System.out.print("sameDocument ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.FRAGMENT     ) > 0) System.out.print("fragment ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.QUERY        ) > 0) System.out.print("query ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.EXTERNAL     ) > 0) System.out.print("external ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.INTERNAL     ) > 0) System.out.print("internal ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.FROMHOME     ) > 0) System.out.print("fromHome ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.TOHOME       ) > 0) System.out.print("toHome ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.DOWN         ) > 0) System.out.print("down ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.UP           ) > 0) System.out.print("up ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.SAMECLUSTER  ) > 0) System.out.print("sameCluster ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.FROMCLUSTERINDEX ) > 0) System.out.print("fromIndex ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.TOCLUSTERINDEX ) > 0) System.out.print("toIndex ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.NOHTTP       ) > 0) System.out.print("noHTTP ");
                    		if ((e.getAccess().getLinkType() & scone.netobjects.Link.NOHTML       ) > 0) System.out.print("noHTML ");
                    		System.out.println();
                    	}

                    	if (!e.getAccess().getLinkAnchor().equals("")) {
                    		System.out.println(  " L-Text : " + e.getAccess().getLinkAnchor());
                    	}
                    	
                    	if (!e.getAccess().getLinkTitle().equals("")) {
                    		System.out.println(  " L-Title: " + e.getAccess().getLinkTitle());
                    	}

                    	if (!e.getAccess().getLinkPos().equals("")) {
                    		System.out.println(  " L-Pos  : " + e.getAccess().getLinkPos()+ " (" + e.getAccess().getLastWindowSize() + ")");
                    	}
                    
                    	if ((e.getAccess().getAction() & 1 << 3) > 0) {
                    		if (e.getAccess().getPost()!=null && !e.getAccess().getPost().equals(""))
                    			System.out.println("    POST: "+e.getAccess().getPost());
                    		if (e.getAccess().getQuery()!=null && !e.getAccess().getQuery().equals(""))
                    			System.out.println("     GET: "+e.getAccess().getQuery());
                    	}
                    }

                }

                if (this.getProperties().get("Show verbose information").equals("true")) {
              		// Second access-event: loadtime set - only show if time is set and this is the load event or load events are disabled.
                	if (e.getAccess().getLoadTime() != 0 && (e.getAccess().getStayTime() == 0 || AccessTracking.props.get("Create extra event after page is loaded").equals("false"))) {  
                		System.out.println(" Load   : " + e.getAccess().getLoadTime() + "ms");
                    	if (hNode != null)
                  			System.out.println(" F-Print: " + hNode.getFingerprint());
                	}
                    // Last access-event: staytime set
                	if (e.getAccess().getStayTime() != 0) {  
                		System.out.println(" Stay   : " + e.getAccess().getStayTime() + "ms");
                	} 
                }

                // Show known backlinks...
                if (this.getProperties().get("Show Known Backlinks").equals("true")
                        && e.getAccess().getAction() != 0
                        && e.getAccess().getStayTime() != 0) {
                    LinkVector lv = LinkCache.getLinksTo(e.getNode());

                    for (int i = 0; i < lv.size(); i++) {
                        HtmlNode fromNode = HtmlNodeCache.check(lv.linkAt(i).getFromNode());

                        if (fromNode == null) {
                            System.out.println(lv.linkAt(i).getFromNode().getUri());
                        } else {
                            System.out.println(fromNode.getTitle());
                        }
                    }
                }
                // Show known inclusions...
                if (this.getProperties().get("Show Inclusions").equals("true")
                        && e.getAccess().getAction() != 0
                        && e.getAccess().getStayTime() != 0) {
                    InclusionCache.clean();  // Store to DB...
                    Vector v = InclusionCache.getIncludedObjects(e.getNode());

                    for (int i = 0; i < v.size(); i++) {
                        System.out.println(((Inclusion) v.elementAt(i)).getTag() + " - " + ((Inclusion) v.elementAt(i)).getChildNode().getUri());
                    }
                    System.out.println(v.size() + " inclusions found");
                } // this.getProperties().get("Show Inclusions").equals("true")...
            }
        }
    }

    private static Hashtable statusTable = new Hashtable();
    static {
        statusTable.put(new String(""), new String(""));
        statusTable.put(new String("-1"), new String(""));
        statusTable.put(new String("100"), new String("Continue"));
        statusTable.put(new String("101"), new String("Switching Protocols"));
        statusTable.put(new String("200"), new String("OK"));
        statusTable.put(new String("201"), new String("Created"));
        statusTable.put(new String("202"), new String("Accepted"));
        statusTable.put(new String("203"), new String("Non-Authoritative Information"));
        statusTable.put(new String("204"), new String("No Content"));
        statusTable.put(new String("205"), new String("Reset Content"));
        statusTable.put(new String("206"), new String("Partial Content"));
        statusTable.put(new String("300"), new String("Multiple Choices"));
        statusTable.put(new String("301"), new String("Moved Permanently")); // Dokument dauerhaft mit neuer Adresse"));
        statusTable.put(new String("302"), new String("Moved Temporarily")); // Dokument dauerhaft mit neuer Adresse"));
        statusTable.put(new String("303"), new String("See Other"));
        statusTable.put(new String("304"), new String("Not Modified (Document unchanged since last request)"));
        statusTable.put(new String("305"), new String("Use Proxy"));
        statusTable.put(new String("400"), new String("Bad Request (Server name unknown)")); // Bad Request
        statusTable.put(new String("401"), new String("Unauthorized (Password required)"));  // Unauthorized
        statusTable.put(new String("402"), new String("Payment Required"));   // Payment Required
        statusTable.put(new String("403"), new String("Forbidden (Access to document denied)"));   // Forbidden/Access Denied
        statusTable.put(new String("404"), new String("Not Found (Document was deleted)"));
        statusTable.put(new String("405"), new String("Method Not Allowed"));  // Method Not Allowed
        statusTable.put(new String("406"), new String("None Acceptable"));  // Method Not Acceptable
        statusTable.put(new String("407"), new String("Proxy Authentication Required"));  // Proxy Authentication Required
        statusTable.put(new String("408"), new String("Request Timeout (Server doesn't repond / server down)"));   // Request Timeout
        statusTable.put(new String("409"), new String("Conflict"));  // Conflict
        statusTable.put(new String("410"), new String("Gone"));  // Document Removed
        statusTable.put(new String("411"), new String("Length Required"));  // Length Required
        statusTable.put(new String("412"), new String("Precondition Failed"));  // Precondition Failed
        statusTable.put(new String("413"), new String("Request Entity Too Large"));  // Request Entity Too Large
        statusTable.put(new String("414"), new String("Request URI Too Large"));  // Request URI Too Large
        statusTable.put(new String("415"), new String("Unsupported Media Type"));  // Unsupported Media Type
        statusTable.put(new String("500"), new String("Internal Server Error")); // Internal Server Error
        statusTable.put(new String("501"), new String("Not Implemented")); // Not Implemented
        statusTable.put(new String("502"), new String("Bad Gateway")); // Bad Gateway
        statusTable.put(new String("503"), new String("Service Unavailable")); // Service Unavailable
        statusTable.put(new String("504"), new String("Gateway Timeout")); // Gateway Timeout
        statusTable.put(new String("505"), new String("HTTP Version Not Supported (Maybe try other browser)")); // HTTP Version Not Supported
    }

}
