package hyperscout2;


import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Vector;

import scone.netobjects.HtmlNode;
import scone.netobjects.HtmlNodeCache;
import scone.netobjects.Inclusion;
import scone.netobjects.InclusionCache;
import scone.netobjects.Link;
import scone.netobjects.LinkCache;
import scone.netobjects.LinkVector;
import scone.netobjects.NetNode;
import scone.netobjects.NetNodeCache;
import scone.netobjects.Server;
import scone.netobjects.ServerCache;
import scone.ras.Connection;
import scone.ras.ConnectionHandler;


public class LinkDataGenerator implements ConnectionHandler {

    public static final String COPYRIGHT = "(C) Harald Weinreich & Volkert Buchmann";
    private String linkId = null;
    private Link link = null;
    private NetNode baseNode = null;
    private NetNode toNode = null;
    private HtmlNode baseDoc = null;
    private HtmlNode doc = null;
    private Server server = null;
    private LinkVector links = null;

    private static Plugin plugin;

    /**
     * Init Linkdatagenerator
     */
    public static void init(Plugin p) {
        plugin = p;
    }

    /**
     * Handle RAS-Connection from applet...
     */
    public void handle(Connection c) {
        // System.out.println("LinkDataGenerator handle!");
        int i = 0;

        try {
            linkId = c.read();
            if (linkId != null && !linkId.equals("null")) // Errors may happen!
            {
                System.out.print("LDG: linkid " + linkId);
                link = LinkCache.getById(linkId);

                c.write("--begin");
                baseNode = link.getFromNode();
                baseDoc = HtmlNodeCache.check(baseNode);

                toNode = link.getToNode();
                if (toNode.getAccessStatus().equals("301")
                        && NetNodeCache.get(toNode.toDocString() + "/") != null
                        && NetNodeCache.get(toNode.toDocString() + "/").getAccessStatus().equals("200")) {
                    toNode = NetNodeCache.get(toNode.toDocString() + "/");
                }  // Bei Angabe eines Verzeichnisses wurde der abschliessende Slash vergessen!
                doc = HtmlNodeCache.check(toNode);
                server = ServerCache.get(toNode.getHost());

                // if(doc!=null &&   // Zieldokument gibt es schon in DB
                if (toNode.getProtocol().equals("http")
                        && (doc == null || doc.getTitle().length() == 0)) { // server.getTitle().length()==0)  // Server noch nicht gelesen...
                    c.write("noCache");
                }  // KEIN chachen des Papups
                else {
                    c.write("cache");
                }

                if (Integer.parseInt(toNode.getAccessStatus()) < 400) {
                    addBasicContentInfo(c);
                }  // Grundlegende Informationen zum Inhalt...

                addLinkInfo(c);        // Infos zum Link (Titel, alt-Text, typ, etc.)

                addAccessInfo(c);      // Infos zum Zugriff: Status, tempo, etc.

                if (plugin.getProperties().get("Add extended meta infos to popup").equals("true")
                        && Integer.parseInt(toNode.getAccessStatus()) < 400) {
                    addMetaContentInfo(c);
                }  // Erweiterte MEta-Informationen zum Inhalt: Sprache etc.

                if (plugin.getProperties().get("Add provider infos to popup").equals("true")) {
                    addProviderInfo(c);
                }    // Infos zum Anbieter.
                addUsageInfo(c);       // Infos zu der Benutzung.

                if (plugin.getProperties().get("Add format infos to popup").equals("true")) {
                    addFormatInfo(c);
                }      // Infos zum Format.

                addTopologicalInfo(c);   // Topologische Infos.

                addActionInfo(c);      // Aktion des Links: Frames, JavaScript, Mailto etc.

                // URL hinzufügen...
                if (plugin.getProperties().get("Add URI to popup").equals("true")) {
                    tableLine(c, "URL", toNode.toDocString(), "url.gif", "F0F0F0");
                    if (!baseNode.toDocString().equals(toNode.toDocString())
                            && // Sprung zu anderer Seite
                                    link.getFragment().length() > 0) {
                        tableLine(c, "Referenz ", "Sprung nach '" + link.getFragment() + "'", "down2.gif", "D0D8FF");
                    }
                }
                System.out.println(" OK.");
            } else {
                System.out.println("LDG: aborted");
            }

            c.write("--end");

        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }

    // END of handleRequest: And now for the subroutines...
    // ------------------------------------------------------------------------------------

    /**
     * Add information about the link.
     */
    private void addLinkInfo(Connection c) throws IOException {
        // Show link title
        if (link.getTitle().length() > 2
                && !(link.getAlt().indexOf(link.getTitle()) >= 0)
                && !(doc != null && doc.getTitle().length() > 10)) {
            tableLine(c, "Link Titel", link.getTitle(), "info.gif", "E0F0FF");
        }

        // Show alt text of Link
        if (link.getAlt().length() > 5 && !link.getTitle().equals(link.getAlt())
                &&  // Nur wenn neue info und min 5 Zeichen lang.
                        !(link.getTitle().indexOf(link.getAlt()) >= 0)
                && !(doc != null && doc.getTitle().length() > 10)) {
            tableLine(c, "Bild Titel", link.getAlt(), "info.gif", "E0F0FF");
        }
    }

    /**
     * Add information about the content of the referenced object.
     */
    private void addBasicContentInfo(Connection c) throws IOException {
        if (baseNode.toDocString().equals(toNode.toDocString())) {
            return;  // Same Document: No Content Information to be displayed...
        }

        // Display title of target object
        if (doc != null && doc.getTitle().length() > 0) {
            tableLine(c, "Titel", doc.getTitle(), "info.gif");
        }

        // display description
        if (doc != null && doc.getDescription().length() > 0
                && !doc.getDescription().equals(doc.getTitle()) &&   // Description is sometimes the same as the title :-(
                !doc.getDescription().equals(server.getDescription())) {    // The description is sometimes the same for all Pages :-(
            tableLine(c, "Beschr.", doc.getDescription(), "colors/blank.gif");
        } // display abstract if not description
        else if (doc != null && doc.getAbstract().length() > 0
                && !doc.getAbstract().equals(doc.getDescription())
                && !doc.isFrames()) {  // Bei Frames macht das meist keinen Sinn... "Der Browser unterstützt keine Frames..."
            tableLine(c, "Inhalt", doc.getAbstract(), "colors/blank.gif");
        }

        // Display first lines of Text of Document...
        // if (doc != null && doc.getBodyText().length()>20 && doc.getNumberOfWords().length()>0 )
        // {
        // int bodylength = doc.getBodyText().length();
        // tableLine(c, "Content", doc.getBodyText().substring(0,Math.min(200,bodylength-1)),"colors/blank.gif");
        // }

    }

    /**
     * Add information about the content of the referenced object.
     */
    private void addMetaContentInfo(Connection c) throws IOException {
        if (baseNode.toDocString().equals(toNode.toDocString())) {
            return;  // Same Document: No Content Information to be displayed...
        }

        // display language ---------------------------------
        if (doc != null && !doc.getLanguage().equals("")
                && !doc.getLanguage().equals(baseDoc.getLanguage())) {
            String lang = doc.getLanguage();

            if (!lang.equals("?")) {
                String image = "language.gif";

                if (lang.equals("en")) {
                    lang = "Englisch";
                    image = "english.gif";
                }
                if (lang.equals("de")) {
                    lang = "Deutsch";
                    image = "german.gif";
                }
                if (lang.equals("es")) {
                    lang = "Spanisch";
                    image = "spanish.gif";
                }
                if (lang.equals("fr")) {
                    lang = "Französisch";
                    image = "french.gif";
                }
                tableLine(c, "Sprache", lang, image, "E8E8E8");
            }
        }

        // display last-modified-info ---------------------------------
        if (doc != null && doc.getLastModified() != -1) {
            long now = (new Date()).getTime();
            long lastModified = toNode.getLastModified();

            if ((now - lastModified) < (1000l * 60l * 60l)) { // less than one hour ago!
                tableLine(c, "Neu!", "Geändert vor " + String.valueOf((now - lastModified) / 1000l / 60l) + " Minuten", "clock.gif", "E8E8E8");
            } else if ((now - lastModified) < (1000l * 60l * 60l * 24l * 2l)) { // less than two days ago!
                tableLine(c, "Neu", "Geändert vor " + String.valueOf((now - lastModified) / 1000l / 60l / 60l) + " Stunden " + String.valueOf((now - lastModified) / 1000 / 60 / 60) + " Minuten", "clock.gif", "E8E8E8");
            } else if ((now - lastModified) < (1000l * 60l * 60l * 24l * 90l)) { // less than three months!
                tableLine(c, "Aktuell", "Geändert vor " + String.valueOf((now - lastModified) / 1000l / 60l / 60l / 24l) + " Tagen", "clock.gif", "E8E8E8");
            } else {
                Date lastModifiedDate = new Date(lastModified);
                // SimpleDateFormat formatter = new SimpleDateFormat ("EEE, dd-MMM-yyyy HH:mm:ss", Locale.US); // 09-Nov-2030 23:59:00 GMT;
                SimpleDateFormat formatter = new SimpleDateFormat("EEEEEEEEE, dd. MMMMMMMMMMM yyyy", Locale.GERMANY); // 09-Nov-2030 23:59:00 GMT;
                String dateString = formatter.format(lastModifiedDate);

                if ((now - lastModified) < (1000l * 60l * 60l * 24l * 365l * 2l)) {   // less than two years months
                    tableLine(c, "Update am", dateString, "clock.gif", "E8E8E8");
                } else {  // Älter als zwei Jahre!
                    tableLine(c, "Alte Seite", dateString, "clock.gif", "E8E8E8");
                }
            }
        }

        /*// Display length of referenced object
         if(doc != null && doc.getNumberOfParagraphs().length()>0)
         tableLine(c, "Paragraphs",DBTableAdapter.escape(new StringBuffer(doc.getNumberOfParagraphs())),"colors/blank.gif","808080");

         // Display length of referenced object
         if(doc != null && doc.getNumberOfWords().length()>0)
         tableLine(c, "Wörter",DBTableAdapter.escape(new StringBuffer(doc.getNumberOfWords()))+" ~ "+doc.getNumberOfWords()+"000","colors/blank.gif","808080");

         // Display length of referenced object
         if(doc != null && doc.getNumberOfImages().length()>0)
         tableLine(c, "Bilder",DBTableAdapter.escape(new StringBuffer(doc.getNumberOfImages())),"colors/blank.gif","808080");

         // Display Space of Images
         if(doc != null && doc.getSpaceOfImages().length()>0)
         tableLine(c, "Space",DBTableAdapter.escape(new StringBuffer(doc.getSpaceOfImages())),"colors/blank.gif","808080");
         */
        // Display Space / Word Ratio
        if (doc != null && doc.getSpaceOfImages().length() > 2 && // 100 oder größer.
                doc.getNumberOfWords().length() > 0
                && (1.0
                                / (1.0
                                        + Long.parseLong(doc.getNumberOfWords())
                                                * 1000.0
                                                / Long.parseLong(doc.getSpaceOfImages())))
                        > 0.8) {
            tableLine(c, "Graphisch", String.valueOf(Math.round((1.0 / (1.0 + Long.parseLong(doc.getNumberOfWords()) * 1000.0 / Long.parseLong(doc.getSpaceOfImages()))) * 100)) + "% Graphiken", "eye.gif", "E8E8E8");
        }

        if (doc != null && doc.getSpaceOfImages().length() > 2 && // 100 oder größer.
                doc.getNumberOfWords().length() > 0
                && (1.0
                                / (1.0
                                        + Long.parseLong(doc.getNumberOfWords())
                                                * 1000.0
                                                / Long.parseLong(doc.getSpaceOfImages())))
                        < 0.1) {
            tableLine(c, "Textuell", String.valueOf(Math.round(100 - (1.0 / (1.0 + Long.parseLong(doc.getNumberOfWords()) * 1000.0 / Long.parseLong(doc.getSpaceOfImages()))) * 100)) + "% Text", "eye.gif", "E8E8E8");
        }

        // Is ref. document a frameset?
        if (doc != null && doc.isFrames() && !baseDoc.isFrames()) {  // Vorher keine Frames, dann Frames.
            tableLine(c, "Frames", "Seite mit Frames", "frames.gif", "E8E8E8");
        }

        if (doc != null && doc.isAnimation()) {
            tableLine(c, "Animation", "Seite mit Animationen", "video.gif", "E8E8E8");
        }

        if (doc != null && doc.isSound()) {
            tableLine(c, "Musik", "Seite spielt Hintergrundmusik", "sound.gif", "E8E8E8");
        }

        if (doc != null && doc.isForms()) {
            tableLine(c, "Formular", "Seite enthält Formularfelder", "forms.gif", "E8E8E8");
        }

    }

    /**
     * Add information about the provider of the referenced object.
     */
    private void addProviderInfo(Connection c) throws IOException {
        if (baseNode.toDocString().equals(toNode.toDocString())) {
            return;  // Same Document: No Author information to be displayed...
        }
        // display author
        if (doc != null && doc.getAuthor().length() > 0
                && !baseDoc.getAuthor().equals(doc.getAuthor())) {
            tableLine(c, "Autor", doc.getAuthor(), "author.gif", "E8E8E8");
        }

        // Display short title of Server Homepage  - wird beim Externen Link angezeigt.
        // if( !baseNode.getHost().equals(toNode.getHost()) &&   // Other Server
        // toNode.getPath().equals("/") && toNode.getFile().equals("") &&  // Not to homepage, dude!
        // server != null && server.getTitle().length()>0)
        // tableLine(c, "Server Titel",server.getTitle(),"author.gif","F0FFF0");
    }

    /**
     * Add information about the Usage of the referenced object.
     */
    private void addUsageInfo(Connection c) throws IOException {
        // Wann habe ich das Objekt bereits besucht?
        // Wie oft, in welchen Zeiträumen
        // War ich schon mal “in der Nähe” dieser Seite, also z.B. auf Seiten, die auf diese Verweisen.
        // Bookmarks
        // Anmerkungen

        if (baseNode.toDocString().equals(toNode.toDocString())) {
            return;  // Same Document: No usage Information to be displayed...
        }

        // display last visit
        if (toNode.getLastAccess() > 0L) {
            long now = (new Date()).getTime();
            long lastAccess = toNode.getLastAccess();

            if (now - lastAccess < 1000l * 60l * 60l) { // less than one hour ago!
                tableLine(c, "Zurück", "Vor " + String.valueOf((now - lastAccess) / 1000l / 60l) + " Minuten besucht, schon " + String.valueOf(toNode.getAccessCounter()) + " mal gesehen", "back.gif", "F0C8FF");
            } else if (now - lastAccess < 1000l * 60l * 60l * 48l) { // less than two days ago!
                tableLine(c, "Bekannt", "Vor " + String.valueOf((now - lastAccess) / 1000l / 60l / 60l) + "h " + String.valueOf((now - lastAccess) / 1000 / 60 / 60) + "min besucht, schon " + String.valueOf(toNode.getAccessCounter()) + " mal gesehen", "back.gif", "F0C8FF");
            } else if (now - lastAccess < 1000l * 60l * 60l * 24l * 90l) { // less than three months!
                tableLine(c, "Bekannt", "Vor " + String.valueOf((now - lastAccess) / 1000l / 60l / 60l / 24l) + " Tagen besucht, schon " + String.valueOf(toNode.getAccessCounter()) + " mal gesehen", "back.gif", "F0C8FF");
            } else {
                Date lastAccessDate = new Date(lastAccess);
                // SimpleDateFormat formatter = new SimpleDateFormat ("EEE, dd-MMM-yyyy HH:mm:ss", Locale.US); // 09-Nov-2030 23:59:00 GMT;
                SimpleDateFormat formatter = new SimpleDateFormat("EEEEEEEE, dd. MMMMMMMM yyyy", Locale.GERMANY);
                String dateString = formatter.format(lastAccessDate);

                tableLine(c, "Bekannt", "Zuletzt am " + dateString + " besucht, schon " + String.valueOf(toNode.getAccessCounter()) + " mal gesehen", "back.gif", "F0C8FF");
            }
        }

        // display access info for remote servers
        if ((toNode.getProtocol().equals("http")
                        || toNode.getProtocol().equals("ftp"))
                &&  // Only for http and ftp!
                        !baseNode.getHost().equals(toNode.getHost()))  // Other Server?
        {
            int i = NetNodeCache.getNetNodeCount("NetNodeTable, HtmlDocumentTable", "where NetNodeTable.nodeId=HtmlDocumentTable.nodeId && host='" + toNode.getHost() + "' && accessCounter>0 && numberOfLinks>0");

            if (i > 0) {
                String image = (toNode.getLastAccess() > 0L)
                        ? "colors/blank.gif"
                        : "back.gif";

                tableLine(c, "Server", "Bereits " + String.valueOf(i) + " Seiten besucht", image, "F0C8FF");
            }

        } // Display first visit
        // if (toNode.getFirstAccess() > 0L)
        {// tableLine(c, "Last visit",String.valueOf(toNode.getLastAccess()),"clock.gif");
            // Date firstAccess = new Date(toNode.getFirstAccess());
            // SimpleDateFormat formatter = new SimpleDateFormat ("EEE, dd.MMM.yyyy HH:mm:ss", Locale.US); // 09-Nov-2030 23:59:00 GMT;
            // String dateString = formatter.format(firstAccess);
            // tableLine(c, "First visit",dateString,"clock.gif");
        }

        // häufig zugegriffen? -> Jetzt beim letzten Besuch...
        // if (toNode.getAccessCounter() > 1)
        // {
        // tableLine(c, "Besuche","Bereits "+String.valueOf(toNode.getAccessCounter()) + " mal besucht","colors/blank.gif","F0C8FF");
        // }

        // -> Balken für Zugriffe??
        // tableLine(c, "Last visit","blabla","clock.gif","colors/yellow2brown.gif");
        // display dummy-bookmark
        // tableLine(c, "bookmarked:","July,4.,1999","bookmark.gif");

    }

    /**
     * Add information about the toplology
     */
    private void addTopologicalInfo(Connection c) throws IOException {

        // Lokaler oder entfernter Link
        // Dead End/ Sackgasse (Objekte ohne Hyperlinks)
        // Typ des Links:
        // Navigation: Zur Homepage, Landmark etc.
        // Assoziativ
        // Hierarchisch
        // Anzahl ein-/ ausgehender Links


        if (baseNode.toDocString().equals(toNode.toDocString())) {
            if (link.getFragment().length() == 0) {
                tableLine(c, "Identisch", "Zeigt zu diesem Dokument", "reload.gif", "F0C8FF");
                return;  // No more topological Information to be displayed...
            }
        } else if (Integer.parseInt(toNode.getAccessStatus()) < 400) // Show only if not same document and no error
        {
            // Is it a landmark page?
            if (!(toNode.getPath().equals("/") && toNode.getFile().equals(""))) {     // Don't check for homepages.
                int j = NetNodeCache.getNetNodeCount("where host='" + toNode.getHost() + "' && accessCounter>0");
                int k = LinkCache.getLinkCount("where toNodeId='" + toNode.getNodeId() + "' && (type&15=0)");  // Not same, no fragment, no query...

                // System.out.println("\n"+j);
                // System.out.println(k);
                if (k * 100 > j * 85 && j > 5) {  // Mindestens 80% der Seiten linken zu dieser Seite...
                    tableLine(c, "Landmark", "Viele der besuchten Seiten dieses Server linken hierher.", "landmark.gif", "FFFFD0");
                }
                // tableLine(c, "Landmark",String.valueOf(k*100 / j) + "% der besuchten Seiten dieses Server linken hierher.","landmark.gif","D0D8FF") ;
            }

            // Is ref. document a dead end?
            if (doc != null && doc.getNumberOfLinks().equals("0")
                    && !doc.isFrames() &&  // Bei Frames gilt das so nicht!
                    doc.getAbstract().length() > 0) {  // Um Falschausgaben zu vermeiden!
                tableLine(c, "Sackgasse", "Dokument ohne Links", "deadend.gif", "D0D8FF");
            }

            // Has ref. document loads of links?
            // Many external links?
            if (doc != null
                    && Integer.parseInt(doc.getNumberOfExternalLinks()) > 10) {
                tableLine(c, "Hub", doc.getNumberOfExternalLinks() + " externe Links", "multilink.gif", "FFFFD0");
            }

            // Many overall Links?
            if (doc != null
                    && (Integer.parseInt(doc.getNumberOfLinks())
                                    - Integer.parseInt(doc.getNumberOfExternalLinks()))
                            > 20
                    &&  // Mehr als 20 interne Links
                                    (Integer.parseInt(doc.getNumberOfLinks())
                                    - Integer.parseInt(baseDoc.getNumberOfLinks()))
                            > 10) {     // min. 10 Links mehr als diese Seite.
                // int k=LinkCache.getLinkCount("where fromNodeId='"+baseNode.getNodeId()+"' && (type&65=0)");  // Not same document, not external Link...
                // System.out.println(k);  // Geht nicht, da der Robot die Links nicht mit abspeichert...
                // if (k>20)
                // tableLine(c, "Index-Seite",String.valueOf(k)+" interne Links","index.gif","D0D8FF");
                tableLine(c, "Index-Seite", String.valueOf(Integer.parseInt(doc.getNumberOfLinks()) - Integer.parseInt(doc.getNumberOfExternalLinks())) + " interne Links", "index.gif", "FFFFD0");

            }
        }

        // display remote server info
        if ((toNode.getProtocol().equals("http")
                        || toNode.getProtocol().equals("ftp"))
                &&  // Only for http and ftp!
                        !baseNode.getHost().equals(toNode.getHost()))  // Other Server?
        {
            if (server != null && server.getTitle().length() > 0
                    &&                // Title available
                            !(toNode.getPath().equals("/")
                            && toNode.getFile().equals("")))  // Not server homepage!
            {
                String servertitel = server.getTitle();

                // Trim Title
                servertitel = stringReplaceIgnoreCase(servertitel, "Home page", "");
                servertitel = stringReplaceIgnoreCase(servertitel, "Homepage of ", "");
                servertitel = stringReplaceIgnoreCase(servertitel, "Homepage", "");
                servertitel = stringReplaceIgnoreCase(servertitel, "Welcome to ", "");
                servertitel = stringReplace(servertitel, "Startseite", "");
                servertitel = stringReplace(servertitel, " - ", " ");
                tableLine(c, "Extern", servertitel, "out.gif", "D0FFD0");
                tableLine(c, "", toNode.toHostString(), "colors/blank.gif", "D0FFD0");
            } else {
                tableLine(c, "Extern", toNode.toHostString(), "out.gif", "D0FFD0");
            }
            if (!toNode.getPath().equals("/") || !toNode.getFile().equals("")) {  // Nicht bei Homepage anzeigen.
                tableLine(c, "Datei", toNode.getPath() + toNode.getFile(), "colors/blank.gif", "D0FFD0");
            } else {
                tableLine(c, "Homepage", "Zur Homepage des Servers", "home.gif", "D0FFD0");
            }
        }

        // Nun sehr grundlegende Infos zur URL der Seite, etwas aufbereitet...
        // In Grau...
        // more or less details?
        // Fist check if it's the same Server...
        if (baseNode.getHost().equals(toNode.getHost()) &&        // Same Server
                !baseNode.toDocString().equals(toNode.toDocString()))  // Differenz document...
        {
            if (toNode.getPath().equals("/") && toNode.getFile().equals("")) {
                tableLine(c, "Homepage", "Zur Homepage des Servers", "home.gif", "D0D8FF");
            } // File in the same path?
            else if (baseNode.getPath().equals(toNode.getPath())) {
                if (toNode.getFile().equals("")) {    // Keine Datei oder index.html
                    tableLine(c, "Datei", "Hauptseite des Ordners", "root.gif", "D0D8FF");
                } else if (!baseNode.toDocString().equals(toNode.toDocString())) { // Nur Datei anzeigen, wenn andere als aktuelle Datei.
                    tableLine(c, "Datei", toNode.getFile(), "cluster.gif", "D0D8FF");
                }
            } // path shorter
            else if (baseNode.getPath().startsWith(toNode.getPath())
                    || (toNode.getPath().startsWith(baseNode.getPath())
                            && toNode.getPath().endsWith("/../"))) {
                tableLine(c, "Überblick", toNode.getPath(), "higher2.gif", "D0D8FF");
            } // path longer
            else if (toNode.getPath().startsWith(baseNode.getPath())) {
                tableLine(c, "Detailpfad", toNode.getPath().substring(baseNode.getPath().length()) + toNode.getFile(), "deeper2.gif", "D0D8FF");
            } else {
                tableLine(c, "Querverw.", "Pfad: " + toNode.getPath() + toNode.getFile(), "same2.gif", "D0D8FF");
            }
        }

        if (toNode.getQuery().length() > 0) {
            tableLine(c, "Query", toNode.getQuery(), "questionmark.gif", "D0D8FF");
        }

        // Show fragment Info
        if (link.getFragment().length() > 0) {
            if (link.getFragment().equalsIgnoreCase("top")
                    || link.getFragment().equalsIgnoreCase("head")
                    || link.getFragment().equalsIgnoreCase("up")
                    || link.getFragment().equalsIgnoreCase("seitenanfang")
                    || link.getFragment().equalsIgnoreCase("anfang")) {
                tableLine(c, "Referenz ", "Zum Kopf der Seite", "up2.gif", "D0D8FF");
            } else if (link.getFragment().equalsIgnoreCase("bottom")
                    || link.getFragment().equalsIgnoreCase("foot")
                    || link.getFragment().equalsIgnoreCase("down")
                    || link.getFragment().equalsIgnoreCase("seitenende")
                    || link.getFragment().equalsIgnoreCase("ende")) {
                tableLine(c, "Referenz ", "Zum Fuß der Seite", "down2.gif", "D0D8FF");
            } else {
                tableLine(c, "Referenz ", "Sprung nach '" + link.getFragment() + "'", "down2.gif", "D0D8FF");
            }
        }

    }

    /**
     * Add information about the Access to the referenced object.
     */
    private void addAccessInfo(Connection c) throws IOException {
        if (baseNode.toDocString().equals(toNode.toDocString())) {
            return;  // Same Document: No Content Information to be displayed...
        }

        // Status:
        // Objekt erhältlich?
        // Authentifizierung notwendig?
        // Zugriffszeit als Schätzung aus
        // Größe des Dokumentes
        // Bandbreite (über Testdaten oder Ping ermittelt

        // Display Status of target object
        String status = toNode.getAccessStatus();
        String statusText = statusDescription(status);

        if (status != null && status.length() != 0 && !status.equals("200")
                && !status.equals("304") && statusText.length() > 0) {
            tableLine(c, "Status", statusText, "flash.gif", "FFD0D0");
        }

        // Display size of target object
        int size;

        try {
            size = ((new Integer(toNode.getSize())).intValue());
        } catch (Exception ex) {
            size = 0;
        }

        // Display Long pages
        if (doc != null
                && (Long.parseLong(doc.getSpaceOfImages())
                                + Long.parseLong(doc.getNumberOfWords()) * 1000)
                        > 1500000) {
            tableLine(c, "Seitenlänge", "Sehr lange Seite", "long.gif", "E8E8E8");
        } else if (doc != null
                && (Long.parseLong(doc.getSpaceOfImages())
                                + Long.parseLong(doc.getNumberOfWords()) * 1000)
                        > 500000) { // 400000 )
            tableLine(c, "Seitenlänge", "Lange Seite", "long.gif", "E8E8E8");
        } else if (size > 40000) {
            size /= 1024;  // Display KBytes!
            tableLine(c, "Große Datei", (new Integer(size)).toString() + " kByte", "size.gif", "E8E8E8");
        }

        // Server Ping-Zeit....
        if ((toNode.getProtocol().equals("http")
                        || toNode.getProtocol().equals("ftp"))
                &&  // Only for http and ftp!
                        !baseNode.getHost().equals(toNode.getHost()))  // Other Server?
        {
            Server remoteServer = ServerCache.get(toNode.getHost());

            if (remoteServer.getAccessStatus().equals("err")) {
                tableLine(c, "Status", "Server is propably unreachable!", "flash.gif", "E8E8E8");
            } else {
                if (remoteServer.getAccessStatus().length() > 0
                        && !remoteServer.getDelay().equals("0")) {
                    String reply;
                    if (Integer.parseInt(remoteServer.getDelay()) < 1000) {
                        reply = "Schnell (unter 1 Sekunde)";
	                    //tableLine(c, "Antwortzeit", reply + " (" + remoteServer.getDelay() + "ms)", "rabbit.gif", "E8E8E8");
                    } else if (Integer.parseInt(remoteServer.getDelay()) > 5000) {
                        reply = "Langsam (über 5 Sekunden)";
	                    tableLine(c, "Antwortzeit", reply + " (" + remoteServer.getDelay() + "ms)", "rabbit.gif", "E8E8E8");
                    } else {
	                    reply = "Normal";
	                    //tableLine(c, "Antwortzeit", reply + " (" + remoteServer.getDelay() + "ms)", "rabbit.gif", "E8E8E8");
	                }
                }
            }
        }

    }

    /**
     * Add information about the Action of the link.
     */
    private void addActionInfo(Connection c) throws IOException {
        // Display email
        if (toNode.getProtocol().equals("mailto")) {
            tableLine(c, "E-Mail an", toNode.getOpaquePart(), "e-mail.gif", "F0FFFF");
        }

        if (toNode.getProtocol().equals("ftp")) {
            if (toNode.getFile().equals("")) {
                tableLine(c, "Aktion", "ftp-Verzeichnis", "ftpdir.gif", "F0FFFF");
            } else {
                tableLine(c, "Aktion", "Starte download", "download.gif", "F0FFFF");
            }
        }

        if (toNode.getProtocol().equals("https")) {
            tableLine(c, "Aktion", "Geschicherte Verbindung", "secure.gif", "F0FFFF");
        }

        if (toNode.getProtocol().equals("telnet")) {
            tableLine(c, "Aktion", "Telnet-Sitzung", "telnet.gif", "F0FFFF");
        }

        if (toNode.toDocString().toLowerCase().startsWith("javascript:")
                && toNode.toDocString().toLowerCase().indexOf("window") > 0) {
            tableLine(c, "Aktion", "JavaScript: öffnet neues Fenster", "new.gif", "F0FFFF");
        } else if (toNode.toDocString().toLowerCase().startsWith("javascript:")) {
            tableLine(c, "Aktion", "JavaScript!", "javascript.gif", "F0FFFF");
        } // Link opens a new Window
        else if (link.getTarget() != null && link.getTarget().length() != 0) {
            // System.out.println(link.getTarget());
            if (link.getTarget().equalsIgnoreCase("_blank")) {
                tableLine(c, "Aktion", "Öffnet neues Fenster", "new.gif", "F0FFFF");
            } else if (link.getTarget().equalsIgnoreCase("_parent")
                    || link.getTarget().equalsIgnoreCase("_top")) {

                // if (doc== null || (doc!=null && !doc.isFrames()) )  // Nur wenn es keine (neuen) Frames im Ziel gibt!
                // tableLine(c, "Aktion","Entfernt mögliche Frames","frames.gif", "F0FFFF");
            } else {
                String thisFrameName = "Unknown!";
                Vector inclusions = InclusionCache.getInclusions("where childNodeId='" + baseNode.getNodeId() + "' && tag='frame' limit 0,1");

                if (inclusions.size() > 0) {
                    Inclusion i = (Inclusion) inclusions.get(0);

                    if (i != null && i.getInfo().length() > 0) {
                        thisFrameName = i.getInfo();
                    }
                }
                // System.out.println("This Frame Name: "+thisFrameName);
                if (!link.getTarget().equalsIgnoreCase(thisFrameName)) {
                    tableLine(c, "Aktion", "Steuert anderen Frame", "colors/blank.gif", "F0FFFF");
                }
            }
        }

    }

    /**
     * Add information about the Format of the referenced Document.
     */
    private void addFormatInfo(Connection c) throws IOException {
        // Display file type of target object
        String mimeType = toNode.getMimeType();

        if (mimeType != null && !mimeType.equals("null") && // mimeType not defined
                mimeTypeDescription(mimeType).length() != 0 &&  // Description not available
                mimeType.indexOf("text/html") == -1)  // text/html is standard...
        {
            if (mimeType.indexOf("text") > 0) {
                tableLine(c, "Text", mimeTypeDescription(mimeType), "plaintext.gif");
            } else if (mimeType.indexOf("zip") > 0) {
                tableLine(c, "Zipped", mimeTypeDescription(mimeType), "compress.gif");
            } else if (mimeType.indexOf("image") > 0) {
                tableLine(c, "Bild", mimeTypeDescription(mimeType), "eye.gif");
            } else if (mimeType.indexOf("model") > 0) {
                tableLine(c, "Modell", mimeTypeDescription(mimeType), "vrmodel.gif");
            } else if (mimeType.indexOf("video") > 0) {
                tableLine(c, "Video", mimeTypeDescription(mimeType), "video.gif");
            } else if (mimeType.indexOf("audio") > 0) {
                tableLine(c, "Audio", mimeTypeDescription(mimeType), "sound.gif");
            } else if (mimeType.indexOf("pdf") > 0) {
                tableLine(c, "Dokument", mimeTypeDescription(mimeType), "pdf.gif");
            } else {
                tableLine(c, "Typ", mimeTypeDescription(mimeType), "eye.gif");
            }
        }

    }

    private void tableLine(Connection c, String title, String text, String image) throws IOException {
        tableLine(c, title, text, image, "FFFFFF");
    }

    private void tableLine(Connection c, String title, String text, String image, String color) throws IOException {
        c.write("--line");
        c.write(image);
        c.write(title);
        c.write(text);
        c.write(color);
    }

    /**
     Search a string for all instances of a substring and replace it with another string.
     @param source String
     @param search Substring to search for
     @param replace String to replace it with
     @return The source with all instances of <code>search</code> replaced by <code>replace</code>
     */
    public static String stringReplace(String source, String search, String replace) {

        int spot;
        String returnString;
        String origSource = new String(source);

        spot = source.indexOf(search);
        if (spot > -1) {
            returnString = "";
        } else {
            returnString = source;
        }
        while (spot > -1) {
            if (spot == source.length() + 1) {
                returnString = returnString.concat(source.substring(0, source.length() - 1).concat(replace));
                source = "";
            } else if (spot > 0) {
                returnString = returnString.concat(source.substring(0, spot).concat(replace));
                source = source.substring(spot + search.length(), source.length());
            } else {
                returnString = returnString.concat(replace);
                source = source.substring(spot + search.length(), source.length());
            }
            spot = source.indexOf(search);
        }
        if (!source.equals(origSource)) {
            return returnString.concat(source);
        } else {
            return returnString;
        }
    }

    /**
     Search a string for all instances of a substring and replace it with another string
     while ignoring the case of the search string.
     @param source String
     @param search Substring to search for
     @param replace String to replace it with
     @return The source with all instances of <code>search</code> replaced by <code>replace</code>
     */
    public static String stringReplaceIgnoreCase(String source, String search, String replace) {

        int spot;
        String returnString;
        String origSource = new String(source);

        search = search.toLowerCase();
        spot = source.toLowerCase().indexOf(search);
        if (spot > -1) {
            returnString = "";
        } else {
            returnString = source;
        }
        while (spot > -1) {
            if (spot == source.length() + 1) {
                returnString = returnString.concat(source.substring(0, source.length() - 1).concat(replace));
                source = "";
            } else if (spot > 0) {
                returnString = returnString.concat(source.substring(0, spot).concat(replace));
                source = source.substring(spot + search.length(), source.length());
            } else {
                returnString = returnString.concat(replace);
                source = source.substring(spot + search.length(), source.length());
            }
            spot = source.toLowerCase().indexOf(search);
        }
        if (!source.equals(origSource)) {
            return returnString.concat(source);
        } else {
            return returnString;
        }
    }

    // -----------------------
    private String mimeTypeDescription(String type) {
        String description = "";

        if (type != null) {
            description = (String) mimeTypeTable.get(type);
        }
        if (description == null) {
            description = "";
        }
        return description;
    }

    private String statusDescription(String status) {
        String description = "";

        if (status != null) {
            description = (String) statusTable.get(status);
        }
        if (description == null) {
            description = "Unknown status of page!";
        }
        return description;
    }

    private static Hashtable mimeTypeTable = new Hashtable();
    static {
        mimeTypeTable.put(new String("application/excel"), new String("Microsoft Excel Tabelle"));
        mimeTypeTable.put(new String("application/mac-binhex40"), new String("Macintosh Compressed File (Binhex)"));
        mimeTypeTable.put(new String("application/pdf"), new String("Adobe Acrobat Dokument (pdf)"));
        mimeTypeTable.put(new String("application/zip"), new String("ZIP-Komprimierte Datei"));
        mimeTypeTable.put(new String("image/gif"), new String("Graphik (gif)"));
        mimeTypeTable.put(new String("image/jpeg"), new String("Graphik (jpeg)"));
        mimeTypeTable.put(new String("image/png"), new String("Graphik (png)"));
        mimeTypeTable.put(new String("application/msword"), new String("Microsoft Word Dokument"));
        mimeTypeTable.put(new String("application/octet-stream"), new String("Daten"));
        mimeTypeTable.put(new String("application/java-vm"), new String("Java Programm"));
        mimeTypeTable.put(new String("application/pgp"), new String("Pretty Good Privacy (PGP) Schlüssel"));
        mimeTypeTable.put(new String("application/postscript"), new String("Postscript-Datei"));
        mimeTypeTable.put(new String("application/powerpoint"), new String("Microsoft Powerpoint Präsentation"));
        mimeTypeTable.put(new String("application/rtf"), new String("Rich Text Format Datei (rtf)"));

        mimeTypeTable.put(new String("application/x-perl"), new String("Dynamisch erstelltes Web Dokument"));
        mimeTypeTable.put(new String("application/x-sh"), new String("Dynamisch erstelltes Web Dokument"));

        mimeTypeTable.put(new String("application/wordperfect5.1"), new String("Word Perfect Document"));
        mimeTypeTable.put(new String("application/x-cdlink"), new String(""));
        mimeTypeTable.put(new String("application/x-compress"), new String("Komprimierte Datei (Unix)"));
        mimeTypeTable.put(new String("application/x-cpio"), new String(""));
        mimeTypeTable.put(new String("application/x-csh"), new String(""));
        mimeTypeTable.put(new String("application/x-debian-package"), new String(""));
        mimeTypeTable.put(new String("application/x-director"), new String(""));
        mimeTypeTable.put(new String("application/x-dvi"), new String(""));
        mimeTypeTable.put(new String("application/x-gtar"), new String("Komprimiertes Archiv (Unix)"));
        mimeTypeTable.put(new String("application/x-gzip"), new String("Komprimierte Datei (Unix, gzip)"));
        mimeTypeTable.put(new String("application/x-hdf"), new String(""));
        mimeTypeTable.put(new String("application/x-httpd-php"), new String("Dynamisch erstelltes Dokument"));
        mimeTypeTable.put(new String("application/x-JavaScript"), new String("JavaScript Programm"));
        mimeTypeTable.put(new String("application/x-latex"), new String("Latex Dokument"));
        // mimeTypeTable.put(new String("frm maker frame fm fb book fbdoc"), new String("application/x-maker"));
        mimeTypeTable.put(new String("application/x-mif"), new String(""));
        mimeTypeTable.put(new String("application/x-msdos-program"), new String("Ausführbares Programm (PCs/Windows)"));
        mimeTypeTable.put(new String("application/x-netcdf"), new String(""));
        mimeTypeTable.put(new String("application/x-netcdf"), new String(""));
        mimeTypeTable.put(new String("application/x-stuffit"), new String(""));
        mimeTypeTable.put(new String("application/x-tar"), new String("Unix Archiv (tar)"));
        mimeTypeTable.put(new String("application/x-tcl"), new String("TCL Programm"));
        mimeTypeTable.put(new String("application/x-tex"), new String("TEX Dokument"));
        mimeTypeTable.put(new String("application/x-texinfo"), new String(""));
        mimeTypeTable.put(new String("application/x-troff-man"), new String("Unix manual pages file"));
        mimeTypeTable.put(new String("application/x-troff-me"), new String(""));
        mimeTypeTable.put(new String("application/x-troff-ms"), new String(""));
        mimeTypeTable.put(new String("application/x-ustar"), new String(""));
        mimeTypeTable.put(new String("application/x-wais-source"), new String("Source File"));
        mimeTypeTable.put(new String("audio/basic"), new String("Audio file"));
        mimeTypeTable.put(new String("audio/midi"), new String("Audio file (midi)"));
        mimeTypeTable.put(new String("audio/mpeg"), new String("Audio file (mp3)"));
        mimeTypeTable.put(new String("audio/x-aiff"), new String("Audio file"));
        mimeTypeTable.put(new String("audio/x-pn-realaudio"), new String("Realaudio Datei"));
        mimeTypeTable.put(new String("audio/x-wav"), new String("Audio Datei (wav)"));
        mimeTypeTable.put(new String("image/ief"), new String("Graphik (ief)"));
        mimeTypeTable.put(new String("image/tiff"), new String("Graphik (tiff)"));
        mimeTypeTable.put(new String("image/x-cmu-raster"), new String(""));
        mimeTypeTable.put(new String("image/x-portable-anymap"), new String(""));
        mimeTypeTable.put(new String("image/x-portable-bitmap"), new String(""));
        mimeTypeTable.put(new String("image/x-portable-graymap"), new String(""));
        mimeTypeTable.put(new String("image/x-portable-pixmap"), new String(""));
        mimeTypeTable.put(new String("image/x-rgb"), new String(""));
        mimeTypeTable.put(new String("image/x-xbitmap"), new String(""));
        mimeTypeTable.put(new String("image/x-xpixmap"), new String(""));
        mimeTypeTable.put(new String("image/x-xwindowdump"), new String(""));
        mimeTypeTable.put(new String("model/iges"), new String(""));
        mimeTypeTable.put(new String("model/iges"), new String(""));
        mimeTypeTable.put(new String("model/mesh"), new String(""));
        mimeTypeTable.put(new String("model/vrml"), new String("3D world (VRML)"));
        mimeTypeTable.put(new String("text/css"), new String("Style Definition (CSS)"));
        mimeTypeTable.put(new String("text/html"), new String("Wold Wide Web Document"));
        mimeTypeTable.put(new String("text/plain"), new String("Plain Text Document"));
        mimeTypeTable.put(new String("text/richtext"), new String("Rich Text Format"));
        mimeTypeTable.put(new String("text/tab-separated-values"), new String("Text file"));
        mimeTypeTable.put(new String("text/x-setext"), new String(""));
        mimeTypeTable.put(new String("text/x-sgml"), new String("SGML file"));
        mimeTypeTable.put(new String("text/x-vCalendar"), new String(""));
        mimeTypeTable.put(new String("text/x-vCard"), new String(""));
        mimeTypeTable.put(new String("text/xml"), new String("XML file"));
        mimeTypeTable.put(new String("video/dl"), new String("Video file"));
        mimeTypeTable.put(new String("video/fli"), new String("Video file"));
        mimeTypeTable.put(new String("video/gl"), new String("Video file"));
        mimeTypeTable.put(new String("video/mpeg"), new String("Video file (mpeg)"));
        mimeTypeTable.put(new String("video/quicktime"), new String(""));
        mimeTypeTable.put(new String("video/x-msvideo"), new String("Microsoft video file"));
        mimeTypeTable.put(new String("video/x-sgi-movie"), new String(""));
        mimeTypeTable.put(new String("x-conference/x-cooltalk"), new String(""));
    }

    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(""));
        statusTable.put(new String("101"), new String(""));
        statusTable.put(new String("200"), new String(""));
        statusTable.put(new String("201"), new String(""));
        statusTable.put(new String("202"), new String(""));
        statusTable.put(new String("203"), new String(""));
        statusTable.put(new String("204"), new String(""));
        statusTable.put(new String("205"), new String(""));
        statusTable.put(new String("206"), new String(""));
        statusTable.put(new String("300"), new String(""));
        statusTable.put(new String("301"), new String("")); // Dokument dauerhaft mit neuer Adresse"));
        statusTable.put(new String("302"), new String("")); // Dokument dauerhaft mit neuer Adresse"));
        statusTable.put(new String("303"), new String(""));
        statusTable.put(new String("304"), new String("Dokument wurde seit letztem Besuch nicht geändert"));
        statusTable.put(new String("305"), new String(""));
        statusTable.put(new String("400"), new String("Server Name unbekannt.")); // Bad Request
        statusTable.put(new String("401"), new String("Zugriff nicht gestattet"));  // Unauthorized
        statusTable.put(new String("402"), new String("Zugriff kostenpflichtig"));   // Payment Required
        statusTable.put(new String("403"), new String("Zugriff zum Dokument verweigert."));   // Forbidden/Access Denied
        statusTable.put(new String("404"), new String("Dokument existiert nicht (mehr)!"));
        statusTable.put(new String("405"), new String("Error"));  // Method Not Allowed
        statusTable.put(new String("406"), new String("Error"));  // Method Not Acceptable
        statusTable.put(new String("407"), new String("Error"));  // Proxy Authentication Required
        statusTable.put(new String("408"), new String("Timeout der Anfrage: Server antwortet nicht."));   // Request Timeout
        statusTable.put(new String("409"), new String("Error"));  // Conflict
        statusTable.put(new String("410"), new String("Error"));  // Document Removed
        statusTable.put(new String("411"), new String("Error"));  // Length Required
        statusTable.put(new String("412"), new String("Error"));  // Precondition Failed
        statusTable.put(new String("413"), new String("Error"));  // Request Entity Too Large
        statusTable.put(new String("414"), new String("Error"));  // Request URI Too Large
        statusTable.put(new String("415"), new String("Error"));  // Unsupported Media Type
        statusTable.put(new String("500"), new String("Interner Server-Fehler")); // Internal Server Error
        statusTable.put(new String("501"), new String("Interner Server-Fehler")); // Not Implemented
        statusTable.put(new String("502"), new String("Interner Server-Fehler")); // Bad Gateway
        statusTable.put(new String("503"), new String("Interner Server-Fehler")); // Service Unavailable
        statusTable.put(new String("504"), new String("Interner Server-Fehler")); // Gateway Timeout
        statusTable.put(new String("505"), new String("Interner Server-Fehler: Browser nicht unterstützt.")); // HTTP Version Not Supported
    }

    /* 100 Continue
     101 Switching Protocols
     200 OK
     201 Created
     202 Accepted
     203 Non-Authoritative Information
     204 No Content
     205 Reset Content
     206 Partial Content
     300 Multiple Choices
     301 Moved Permanently
     302 Moved Temporarily
     303 See Other
     304 Not Modified
     305 Use Proxy
     400 Bad Request
     401 Unauthorized
     402 Payment Required
     403 Forbidden
     404 Not Found
     405 Method Not Allowed
     406 None Acceptable
     407 Proxy Authentication Required
     408 Request Timeout
     409 Conflict
     410 Gone
     411 Length Required
     412 Unless True
     500 Internal Server Error
     501 Not Implemented
     502 Bad Gateway
     503 Service Unavailable
     504 Gateway Timeout
     */
}
