/*   **********************************************************************  **
 **   Copyright notice                                                       **
 **                                                                          **
 **   (c) 2003-2006 RSSOwl Development Team                                  **
 **   http://www.rssowl.org/                                                 **
 **                                                                          **
 **   All rights reserved                                                    **
 **                                                                          **
 **   This program and the accompanying materials are made available under   **
 **   the terms of the Eclipse Public License 1.0 which accompanies this     **
 **   distribution, and is available at:                                     **
 **   http://www.rssowl.org/legal/epl-v10.html                               **
 **                                                                          **
 **   A copy is found in the file epl-v10.html and important notices to the  **
 **   license from the team is found in the textfile LICENSE.txt distributed **
 **   in this package.                                                       **
 **                                                                          **
 **   This copyright notice MUST APPEAR in all copies of the file!           **
 **                                                                          **
 **   Contributors:                                                          **
 **     RSSOwl - initial API and implementation (bpasero@rssowl.org)         **
 **                                                                          **
 **  **********************************************************************  */

package net.sourceforge.rssowl.dao.feedparser;

import net.sourceforge.rssowl.controller.GUI;
import net.sourceforge.rssowl.dao.NewsfeedFactoryException;
import net.sourceforge.rssowl.model.Channel;
import net.sourceforge.rssowl.model.NewsItem;
import net.sourceforge.rssowl.util.shop.StringShop;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.Text;
import org.jdom.output.XMLOutputter;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.List;

/**
 * Parser for the Atom 0.3 Format <br />
 * Specification: http://bitworking.org/projects/atom/draft-gregorio-03.html
 * 
 * @author <a href="mailto:bpasero@rssowl.org">Benjamin Pasero </a>
 * @version 1.2.3
 */
public class Atom_0_3_Parser extends AbstractFeedParser {

  /** Namespace: XHTML */
  private static final Namespace xhtml = Namespace.getNamespace("http://www.w3.org/1999/xhtml");

  /**
   * Instantiate a new Parser for this format.
   * 
   * @param document The document containing the data to parse
   * @param rssChannel The Channel to fill with data from the document
   * @param url The URL of the Newsfeed that is parsed
   * @param nameSpaces Possible NameSpaces of the XML document
   * @throws NewsfeedFactoryException If any error occurs
   */
  public Atom_0_3_Parser(Document document, Channel rssChannel, String url, Namespace nameSpaces[]) throws NewsfeedFactoryException {
    super(document, rssChannel, url, nameSpaces);
  }

  /**
   * @see net.sourceforge.rssowl.dao.feedparser.AbstractFeedParser#parse()
   */
  public void parse() {

    /** Temp String */
    String str;

    /** Language of feed */
    str = getAttributeValue(root, "lang", Namespace.XML_NAMESPACE);
    if (StringShop.isset(str))
      rssChannel.setLanguage(str);

    /** Title of feed */
    str = getChildValue("title", root);
    if (StringShop.isset(str))
      rssChannel.setTitle(Text.normalizeString(str));

    /** Link(s) of feed */
    List links = getChildren(root, "link");
    for (int a = 0; links != null && a < links.size(); a++) {
      Element link = (Element) links.get(a);

      /** Check the relation attribute */
      String rel = getAttributeValue(link, "rel");

      /** Set as homepage if rel equals "alternate" or link is the last one */
      if ((rel != null && rel.equals("alternate")) || a == (links.size() - 1)) {
        rssChannel.setHomepage(getAttributeValue(link, "href"));
        break;
      }
    }

    /** Description of feed */
    str = getChildValue("tagline", root);
    if (StringShop.isset(str))
      rssChannel.setDescription(Text.normalizeString(str));

    /** Modified Date */
    str = getChildValue("modified", root);
    if (StringShop.isset(str))
      rssChannel.setLastBuildDate(str);

    /** Author of feed */
    Element author = getChildElement(root, "author");
    if (author != null) {
      str = getChildValue("name", author);
      if (StringShop.isset(str))
        rssChannel.setCreator(str);
    }

    /** Generator of feed */
    str = getChildValue("generator", root);
    if (StringShop.isset(str))
      rssChannel.setGenerator(str);

    /** Copyright of feed */
    str = getChildValue("copyright", root);
    if (StringShop.isset(str))
      rssChannel.setCopyright(str);

    /** Entries of the feed */
    List entries = getChildren(root, "entry");
    Iterator entriesIt = entries.iterator();
    while (entriesIt.hasNext()) {
      Element entry = (Element) entriesIt.next();
      NewsItem rssNewsItem = new NewsItem();

      /** Title */
      str = getChildValue("title", entry);
      if (StringShop.isset(str))
        rssNewsItem.setTitle(str);

      /** Link(s) of feed */
      List newsLinks = getChildren(entry, "link");
      for (int a = 0; newsLinks != null && a < newsLinks.size(); a++) {
        Element newsLink = (Element) newsLinks.get(a);

        /** Check the relation attribute */
        String rel = getAttributeValue(newsLink, "rel");

        /** Set as homepage if rel equals "alternate" or link is the last one */
        if ((rel != null && rel.equals("alternate")) || a == (newsLinks.size() - 1)) {
          rssNewsItem.setLink(getAttributeValue(newsLink, "href"));
          break;
        }
      }

      /** Content */
      str = getChildValue("content", entry);
      Element content = getChildElement(entry, "content");
      String type = getAttributeValue(content, "type");
      String mode = getAttributeValue(content, "mode");
      boolean isApplicationXhtml = (type != null) ? type.equals("application/xhtml+xml") : false;
      boolean isXmlMode = (mode != null) ? mode.equals("xml") : false;

      /** Content mode is "escaped" */
      if (StringShop.isset(str))
        rssNewsItem.setDescription(str);

      /** Content type is "application/xhtml+xml" or content mode is "xml" */
      else if (content != null && (isApplicationXhtml || isXmlMode)) {

        /** Get the div containing the XML */
        Element xmlDiv = getChildElement(content, "div", xhtml);

        /** Feed may use the BODY tag instead of DIV */
        if (xmlDiv == null)
          xmlDiv = getChildElement(content, "body", xhtml);

        /** Get content into a String if provided */
        if (xmlDiv != null) {
          XMLOutputter out = new XMLOutputter();
          StringWriter writer = new StringWriter();
          try {
            out.output(xmlDiv, writer);
            writer.close();
          } catch (IOException e) {
            GUI.logger.log("parseAtomVersion_0_3()", e);
          }

          /** Get Text */
          String contentStr = writer.toString();

          /**
           * Problem: This Method of writing the content of the xmlDiv into the
           * StringWriter is not taking care of any CDATA-Constructs inside, as
           * no XML-parsing is done. The workaround is to manually remove the
           * CDATA-Tags. This has the same effect as parsing, since CDATA is to
           * be taken as is, with no element or entitiy processing.
           */
          if (contentStr.indexOf("<![CDATA[") >= 0) {
            contentStr = StringShop.replaceAll(contentStr, "<![CDATA[", "");
            contentStr = StringShop.replaceAll(contentStr, "]]>", "");
          }

          /** Set content as description */
          rssNewsItem.setDescription(contentStr);
        }
      }

      /** Content was not available, use Summary then */
      if (rssNewsItem.getDescription() == null) {
        str = getChildValue("summary", entry);
        Element summary = getChildElement(entry, "summary");
        type = getAttributeValue(summary, "type");
        mode = getAttributeValue(summary, "mode");
        isApplicationXhtml = (type != null) ? type.equals("application/xhtml+xml") : false;
        isXmlMode = (mode != null) ? mode.equals("xml") : false;

        /** Summary mode is "escaped" */
        if (StringShop.isset(str))
          rssNewsItem.setDescription(str);

        /** Content type is "application/xhtml+xml" or content mode is "xml" */
        else if (summary != null && (isApplicationXhtml || isXmlMode)) {

          /** Get the div containing the XML */
          Element xmlDiv = getChildElement(summary, "div", xhtml);

          /** Feed may use the BODY tag instead of DIV */
          if (xmlDiv == null)
            xmlDiv = getChildElement(summary, "body", xhtml);

          /** Get content into a String if provided */
          if (xmlDiv != null) {
            XMLOutputter out = new XMLOutputter();
            StringWriter writer = new StringWriter();
            try {
              out.output(xmlDiv, writer);
              writer.close();
            } catch (IOException e) {
              GUI.logger.log("parseAtomVersion_0_3()", e);
            }

            /** Set content as description */
            rssNewsItem.setDescription(writer.toString());
          }
        }
      }

      /** Set part of description as title if title is not available */
      if (!StringShop.isset(rssNewsItem.getTitle()) && rssNewsItem.getDescription() != null)
        rssNewsItem.setTitle(rssNewsItem.getDescription(), true);

      /** PudDate */
      str = getChildValue("issued", entry);
      if (StringShop.isset(str)) {
        rssNewsItem.setPubDate(str, true);
        rssChannel.addAvailableNewsItemInfo("TABLE_HEADER_PUBDATE");
      }

      /** Author */
      Element newsauthor = getChildElement(entry, "author");
      if (newsauthor != null) {
        str = getChildValue("name", newsauthor);
        if (StringShop.isset(str)) {
          rssNewsItem.setAuthor(str);
          rssChannel.addAvailableNewsItemInfo("TABLE_HEADER_AUTHOR");
        }
      }

      /** Use channel creator as author then */
      else if (rssChannel.getCreator() != null) {
        rssNewsItem.setAuthor(rssChannel.getCreator());
        rssChannel.addAvailableNewsItemInfo("TABLE_HEADER_AUTHOR");
      }

      rssChannel.insertItem(rssNewsItem);
    }
  }
}