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


import adc.parser.HtmlStreamTokenizer;
import adc.parser.HtmlTag;


/**
 * a <code>Link</code> represents a links-set from the database
 *
 * @author Harald Weinreich
 * @author Volkert Buchmann
 */

public class Link extends TableRecord {
    public static final String COPYRIGHT = "(C) Harald Weinreich & Volkert Buchmann";

    // Same page
    public final static int SAMEDOCUMENT = 1 << 0;     // SAMEDOCUMENT
    // Fragment defined 
    public final static int FRAGMENT    = 1 << 1;     // FRAGMENT
    // Query defined 
    public final static int QUERY       = 1 << 2;     // QUERY
    // External Link
    public final static int EXTERNAL    = 1 << 6;     // EXTERNAL
    // Internal Link - same server
    public final static int INTERNAL    = 1 << 7;     // INTERNAL
        // External Link, but only to same top-level-domain, i.e. www.sun.com -> java.sun.com
        // Attention: Problems with some ISO-country Domains: co.uk, ac.uk, edu.tw, org.tw etc.
    //public final static int = 1 << 8;       // Same TLD
    // bit 10: From Home: Coming from homepage of server
    public final static int FROMHOME    = 1 << 10;    // FROMHOME
    // bit 11: From Home: Pointing to Homepage of server
    public final static int TOHOME      = 1 << 11;    //TOHOME
    // Down: Pointing down the hierarchy of the URL - longer path name, same branch
    public final static int DOWN =  1 << 14;    // DOWN
    // Up: Pointing up - shorter path name, same branch.
    public final static int UP = 1 << 15;    // UP
    // Same directory of server
    public final static int SAMECLUSTER = 1 << 20;    // SAMECLUSTER
    // bit 21: From Cluster root: from collection root, page name is index.html, default.htm etc.
    public final static int FROMCLUSTERINDEX =  1 << 21;    // FROMCLUSTERINDEX
    // bit 22: To Cluster root: to collection root
    public final static int TOCLUSTERINDEX = 1 << 22;    // TOCLUSTERINDEX
    // bit 24: Other protocol: Link destination is not http but mail, ftp etc.
    public final static int NOHTTP = 1 << 24;    // NOHTTP
    // bit 25: Other Mime-Type: Destination of link is not HTML-document.
    public final static int NOHTML = 1 << 25;    // NOHTML

    protected static DBTableAdapter dbTable;

    protected NetNode fromNode = null;
    protected NetNode toNode = null;

    // set sql meta-data
    static {
        dbTable = new DBTableAdapter("LinkTable");

        /*
         dbTable=new DBTableAdapter("LinkTable",11);

         dbTable.addField("linkId",    dbTable.STRING,"-1",dbTable.KEY);

         dbTable.addField("fromNodeId",dbTable.STRING,  "",dbTable.FIELD);
         dbTable.addField("toNodeId",  dbTable.STRING,  "",dbTable.FIELD);
         dbTable.addField("fragment",  dbTable.STRING,  "",dbTable.FIELD);

         dbTable.addField("linkText",  dbTable.STRING,  "",dbTable.FIELD);
         dbTable.addField("target",    dbTable.STRING,  "",dbTable.FIELD);
         dbTable.addField("title",     dbTable.STRING,  "",dbTable.FIELD);
         dbTable.addField("alt",       dbTable.STRING,  "",dbTable.FIELD);
         dbTable.addField("rev",       dbTable.STRING,  "",dbTable.FIELD);
         dbTable.addField("rel",       dbTable.STRING,  "",dbTable.FIELD);
         dbTable.addField("type",      dbTable.NUMBER, "0",dbTable.FIELD);
         */
    }

    public Link() {
        // System.out.println("Link!");
        dbTable.init(this);           // Set values to default
    }

    // new link object from resultset
    public Link(java.sql.ResultSet results) {
        this();
        dbTable.fill(this, results);
      
        if (hasRecordInDB) {                          // Object references are not filled by dbCheck.
            fromNode = NetNodeCache.getById(fieldValues.get("fromNodeId"));
            toNode = NetNodeCache.getById(fieldValues.get("toNodeId"));
        }

    }

    // returns link with the specified id
    public Link(String linkId) {
        this();           // Set values to default
        setLinkId(linkId);
        dbTable.dbCheck(this);     // Read from DB
        if (hasRecordInDB) {                          // Object references are not filled by dbCheck.
            fromNode = NetNodeCache.getById(fieldValues.get("fromNodeId"));
            toNode = NetNodeCache.getById(fieldValues.get("toNodeId"));
        }
    }

    // creates a new link or reads it from the database
    public Link(NetNode from, NetNode to, String fragment) {
        this();           // Set values to default
        setLinkId(LinkCache.createKey(from.getNodeId(), to.getNodeId(), fragment)); // Generate unique link key.
        dbTable.dbCheck(this);              // We have to check if it is in DB!
        if (hasRecordInDB && dbTable.useDb)          // Found in Database?
        {  // Yes:
            fromNode = NetNodeCache.getById(fieldValues.get("fromNodeId"));
            toNode = NetNodeCache.getById(fieldValues.get("toNodeId"));
        } else {
            // No: Try to fill myself...
            fromNode = from;
            toNode = to;
            fieldValues.put("fromNodeId", from.getNodeId());
            fieldValues.put("toNodeId", to.getNodeId());
            fieldValues.put("fragment", fragment);
            computeType();
            // System.out.println("Link ("+getLinkId()+"): "+Integer.toBinaryString(getType()));
        }
        // System.out.println("New Link: "+getLinkId());
    }

    /**
     * returns the id of this link
     * @return the id
     */
    public String getLinkId() {
        return fieldValues.get("linkId");
    }

    /**
     * Set the id of this link
     */
    private void setLinkId(String id) {
        fieldValues.put("linkId", id);
    }

    /**
     * returns the text between the opening and the closing a-tag
     * @return the linktext
     */
    public String getLinkText() {
        return fieldValues.get("linkText");
    }

    /**
     * sets the text between the opening and the closing a-tag
     * @param v the linktext
     */
    public void setLinkText(String v) {
        fieldValues.put("linkText", v);
    }

    /**
     * returns the target window/frame
     * @return the target
     */
    public String getTarget() {
        return fieldValues.get("target");
    }

    /**
     * sets the target window/frame
     * @param v the target
     */
    public void setTarget(String v) {
        fieldValues.put("target", v);
    }

    /**
     * returns the title parameter
     * @return the title
     */
    public String getTitle() {
        return fieldValues.get("title");
    }

    /**
     * sets the title parameter
     * @param v the title
     */
    public void setTitle(String v) {
        fieldValues.put("title", v);
    }

    /**
     * returns the ALT parameter of a pictures inside that link
     * @return the IMaGes ALT parameter
     */
    public String getAlt() {
        return fieldValues.get("alt");
    }

    /**
     * sets the ALT parameter of a pictue inside this link
     * @param v the IMaGes ALT parameter
     */
    public void setAlt(String v) {
        fieldValues.put("alt", v);
    }

    /**
     * returns the rev parameter
     * @return the rev parameter
     */
    public String getRev() {
        return fieldValues.get("rev");
    }

    /**
     * sets the rev parameter
     * @param v the rev parameter
     */
    public void setRev(String v) {
        fieldValues.put("rev", v);
    }

    /**
     * returns the rel parameter
     * @return the rel parameter
     */
    public String getRel() {
        return fieldValues.get("rel");
    }

    /**
     * sets the rel parameter
     * @param v the rel parameter
     */
    public void setRel(String v) {
        fieldValues.put("rel", v);
    }

    /**
     * returns the link type bitfield parameter
     * @return the typ parameter
     */
    public int getType() {
        return Integer.parseInt(fieldValues.get("type"));
    }

    /**
     * sets the type parameter
     * @param t the type parameter
     */
    public void setType(int t) {
        fieldValues.put("type", String.valueOf(t));
    }

    /**
     * returns the node in which the link is defined
     * @return the defining node
     */
    public NetNode getFromNode() {
        return fromNode;
    }

    /**
     * returns the node to which the link is pointing
     * @return the linked node
     */
    public NetNode getToNode() {
        return toNode;
    }

    /**
     * returns the fragment: protocol://server/path#fragment
     * @return the fragment
     */
    public String getFragment() {
        return fieldValues.get("fragment");
    }

    /**
     * sets the attributes to the values from a tag
     * @param tag the tag
     */

    public void fill(HtmlTag tag) {
        String dummy = tag.getParam("target");

        if (dummy != null) {
            fieldValues.put("target", dummy);
        }
        dummy = tag.getParam("title");
        if (dummy != null) {
            fieldValues.put("title", HtmlStreamTokenizer.unescape(dummy));
        }  // better unescape
        dummy = tag.getParam("alt");
        if (dummy != null) {
            fieldValues.put("alt", HtmlStreamTokenizer.unescape(dummy));
        }
        dummy = tag.getParam("rev");
        if (dummy != null) {
            fieldValues.put("rev", dummy);
        }
        dummy = tag.getParam("rel");
        if (dummy != null) {
            fieldValues.put("rel", dummy);
        }
    }

    /**
     * Store Link to DB<BR>
     * For performance reasons, links objects can be created without corresponding DB entry!
     * Therefore store can also create new DB entries.
     */
    // save link to DB
    public void store() {
        if (hasRecordInDB) {    // Entry exists in DB: Update
            dbTable.updateDB(this);
        } else {
            dbTable.createInDB(this);
        }     // For perfomance reasons, link objects are first only created in cache!
    }

    public Object getKey(int KeyNo) {
        switch (KeyNo) {
        case 0:
            return getLinkId();    // Die LinkID wird so wie 

        default:
            return "";
        }
    }

    /**
     * Compute link type. Type information is stored in bits of an integer value. This data is 
     * provided to make access to this kind of topological information easier and faster.<P>
     * bit  0: Roundtrip: Pointing to the same document.<BR>
     * bit  1: Fragment: Fragment defined<BR>
     * bit  2: Query: Query GET-parameters defined<BR>
     *<BR>
     * bit  6: External Link: Pointing from one to an other site<BR>
     * bit  7: Sub-External: Pointign to an other site, but same top level domains.<BR>
     *<BR>
     * bit 10: From Home: Coming from Homepage of server<BR>
     * bit 11: Home: Pointing to homepage of server<BR>
     *<BR>
     * bit 14: Down: Pointing down the hierarchy of the URL - longer path name, same branch<BR>
     * bit 15: Up: Pointing up - shorter path name, same branch.<BR>
     * bit 16: Associative up - <BR>
     * bit 17: Assiciative down - <BR>
     *<BR>
     * bit 20: Cluster: Link points to document in same path.<BR>
     * bit 21: From Cluster root: from collection root, page name is index.html, default.htm etc.<BR>
     * bit 22: To Cluster root: to collection root<BR>
     *<BR>
     * bit 24: Other protocol: Link destination is not http but mail, ftp etc.<BR>
     * bit 25: Other Mime-Type: Destination of link is not HTML-document.<BR>
     */
    public void computeType() {
        int type = 0;

        if (fromNode.toDocString().equals(toNode.toDocString())) {     // Same document
            type |= SAMEDOCUMENT;
        } 
      	
        if (getFragment().length() > 0) {                         // Fragment defined 
            type |= FRAGMENT;
        }  // Integer.parseInt("10",1); }
      	
        if (toNode.getQuery().length() > 0) {                            // Query defined 
            type |= QUERY;
        } 

        // External Link
        if ((toNode.getProtocol().equals("http")
                        || toNode.getProtocol().equals("ftp"))
                &&  // Only for http and ftp!
                        !fromNode.toHostString().equals(toNode.toHostString())) {  // Other Server?
            type |= EXTERNAL;
        } 

        // Internal Link
        if ((toNode.getProtocol().equals("http")
                        || toNode.getProtocol().equals("ftp"))
                &&  // Only for http and ftp!
                        fromNode.toHostString().equals(toNode.toHostString())) {  // Same Server?
            type |= INTERNAL;
        } 

        // External Link, but only to same top-level-domain, i.e. www.sun.com -> java.sun.com
        // Attention: Problems with some ISO-country Domains: co.uk, ac.uk, edu.tw, org.tw etc.
        // if((toNode.getProtocol().equals("http") || toNode.getProtocol().equals("ftp")) &&  // Only for http and ftp!
        // !fromNode.toHostString().equals(toNode.toHostString()))  // Other Server?
        // type|= SAMEDOMAIN; 
		
        // bit 10: From Home: Coming from homepage of server
        if (fromNode.getPath().equals("/") && fromNode.getFile().equals("")) {
            type |= FROMHOME;
        } 
        // bit 11: From Home: Pointing to Homepage of server
        if (toNode.getPath().equals("/") && toNode.getFile().equals("")) {
            type |= TOHOME;
        } 

        if ((type & EXTERNAL) == 0) {
        	// Down: Pointing down the hierarchy of the URL - longer path name, same branch
        	if (toNode.getPath().startsWith(fromNode.getPath()) && !fromNode.getPath().equals(toNode.getPath()) && fromNode.toHostString().equals(toNode.toHostString())) {
        		type |= DOWN;
        	} 
      	
        	// Up: Pointing up - shorter path name, same branch.
        	if (fromNode.getPath().startsWith(toNode.getPath()) && !fromNode.getPath().equals(toNode.getPath()) && fromNode.toHostString().equals(toNode.toHostString())) {
        		type |= UP;
        	} 

        	// bit 16: Associative up - 
        	// if (fromNode.getPath().startsWith(toNode.getPath()) 
        	// type|= 1 << 16; 

        	// bit 17: Assiciative down - 
        	// if (fromNode.getPath().startsWith(toNode.getPath()) 
        	// type|= 1 << 17; 

        	// bit 20: Cluster: Link points to document with same path info.
        	if (fromNode.getPath().equals(toNode.getPath())) {  
        		type |= SAMECLUSTER;
        	} 
         
        	// bit 21: From Cluster root: from collection root, page name is index.html, default.htm etc.
        	if (fromNode.getPath().equals(toNode.getPath())
        			&& fromNode.getFile().equals("")
                	&& !fromNode.toDocString().equals(toNode.toDocString())) {
        		type |= FROMCLUSTERINDEX;
        	} 

	        // bit 22: To Cluster root: to collection root
	        if (fromNode.getPath().equals(toNode.getPath())
	                && toNode.getFile().equals("")
	                && !fromNode.toDocString().equals(toNode.toDocString())) {  
	            type |= TOCLUSTERINDEX;
	        } 
        }

	    // bit 24: Other protocol: Link destination is not http but mail, ftp etc.
        if (!toNode.getProtocol().equals("http")) {  
            type |= NOHTTP;
        } 

        // bit 25: Other Mime-Type: Destination of link is not HTML-document.
        if (!toNode.getMimeType().equals("text/html") && !toNode.getMimeType().equals("")  ) {  
            type |= NOHTML;
        } 

        setType(type);
    }
   
}
