/*
 * Created on May 11, 2005
 *
 */
package com.kellegous.mas864.clusterss;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * @author knorton
 * Copyright (C) 2005 MIT Media Lab, All Right Reserved.
 */
public abstract class Feed {
    protected URL			url;
    
    protected HashMap			dic;
    protected Article[]		articles;
    protected String			description;
    
    public Article[] getArticles() {
        return this.articles;
    }
    
    public String getDescription() {
        return this.description;
    }
    
    public URL getURL() {
        return this.url;
    }
    
    private void flattenNodeValue(Node root, StringBuffer sb) {
        NodeList cn = root.getChildNodes();
        for (int i=0;i<cn.getLength();i++) {
            Node ni = cn.item(i);
            if (ni.getNodeType() == Node.TEXT_NODE || ni.getNodeType() == Node.CDATA_SECTION_NODE)
                sb.append(ni.getNodeValue());
            else
                flattenNodeValue(ni,sb);
        }
    }
    
    protected String getFlattenedNodeValue(Node root) {
        StringBuffer	sb = new StringBuffer();
        flattenNodeValue(root,sb);
        return sb.toString();
    }
    
    protected String getChildNodeContent(Node parent, String tagName) {
        NodeList cn = parent.getChildNodes();
        for (int i=0;i<cn.getLength();i++) {
            Node ni = cn.item(i);
            if (ni.getNodeType() == Node.ELEMENT_NODE
                    && ni.getNodeName().equalsIgnoreCase(tagName)) {
                return this.getFlattenedNodeValue(ni);
            }
        }
        return null;
    }
    
    protected String getChildNodeValue(Node parent, String tagName) {
        NodeList cn = parent.getChildNodes();
        for (int i=0;i<cn.getLength();i++) {
            Node ni = cn.item(i);
            if (ni.getNodeType() == Node.ELEMENT_NODE
                    && ni.getNodeName().equalsIgnoreCase(tagName)) {
                Node nj = ni.getFirstChild();
                if (nj==null || nj.getNodeType() != Node.TEXT_NODE)
                    return null;
                return nj.getNodeValue();
            }
        }
        return null;        
    }
    
    public static Feed create(URL url) throws InvalidFeedException {
        URLConnection c = null;       
        try {
            c = url.openConnection();
            Document d
            		= DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(c.getInputStream());
            
            Element	de = d.getDocumentElement();
            if ((de == null)||(de.getNodeName()==null))
                throw new InvalidFeedException();
            if (de.getNodeName().equals("rss") || de.getNodeName().equals("RDF") || de.getNodeName().equals("rdf:RDF"))
                return new RSSFeed(url,d);
            else if (de.getNodeName().equals("feed"))
                return new AtomFeed(url,d);
            else
                throw new InvalidFeedException();
        }
        catch (IOException e) {
            throw new InvalidFeedException(e);
        }
        catch (SAXException e) {
            throw new InvalidFeedException(e);
        }
        catch (ParserConfigurationException e) {
            throw new InvalidFeedException(e);
        }
        finally {
            try {
                c.getInputStream().close();
            }
            catch (IOException e) {
                throw new InvalidFeedException(e);
            }
        }
    }
    
    public String toString() {
        return this.url.toExternalForm();
    }
    
    /**
     * @author knorton
     * Copyright (C) 2005 MIT Media Lab, All Right Reserved.
     */
    static class RSSFeed extends Feed {
        public RSSFeed(URL url,Document dom) {
            this.url = url;
            this.description = this.getChildNodeValue(
                    dom.getDocumentElement(),
                    "description");
            this.articles = this.extractFeedArticles(
                    dom.getDocumentElement());
        }
        
        private Article[] extractFeedArticles(Element el) {
            NodeList in = el.getElementsByTagName("item");
            ArrayList al = new ArrayList(); 
            for (int i=0;i<in.getLength();i++) {
                al.add(new Article(
                        this,
                        this.extractArticleID(in.item(i)),
                        this.getChildNodeValue(in.item(i),"title"),
                        this.getChildNodeContent(in.item(i),"description")));
            }
            
            Article[] aa = new Article[al.size()];
            al.toArray(aa);
            return aa;
        }
        
        private String extractArticleID(Node n) {
            String id = this.getChildNodeValue(n,"id");
            if (id!=null)
                return id;
            
            id = this.getChildNodeValue(n,"guid");
            if (id!=null)
                return id;
            
            id = this.getChildNodeValue(n,"link");
            if (id!=null)
                return id;
            
            NamedNodeMap at = n.getAttributes();
            n = at.getNamedItemNS("rdf","about");
            if (n!=null)
                return n.getNodeValue();
            
            return null;
        }
    }
    
    /**
     * @author knorton
     * Copyright (C) 2005 MIT Media Lab, All Right Reserved.
     */
    static class AtomFeed extends Feed {
        public AtomFeed(URL url,Document dom) {
            this.url = url;
            this.description = this.getChildNodeValue(
                    dom.getDocumentElement(),
                    "tagline");
            this.articles = this.extractFeedArticles(
                    dom.getDocumentElement());
        }
                
        private Article[] extractFeedArticles(Element el) {
            NodeList in = el.getElementsByTagName("entry");
            ArrayList al = new ArrayList(); 
            for (int i=0;i<in.getLength();i++) {
                String c = this.getChildNodeContent(in.item(i),"content");
                String id = this.getChildNodeValue(in.item(i),"id");
                al.add(new Article(
                        this,
                        (id!=null)?id:this.getChildNodeValue(in.item(i),"guid"),
                        this.getChildNodeValue(in.item(i),"title"),
                        (c!=null)?c:this.getChildNodeContent(in.item(i),"summary")));
            }
            
            Article[] aa = new Article[al.size()];
            al.toArray(aa);
            return aa;
        }
    }
}
