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

import net.sourceforge.rssowl.controller.GUI;
import net.sourceforge.rssowl.model.Category;
import net.sourceforge.rssowl.model.Favorite;
import net.sourceforge.rssowl.util.DateParser;
import net.sourceforge.rssowl.util.GlobalSettings;
import net.sourceforge.rssowl.util.shop.StringShop;
import net.sourceforge.rssowl.util.shop.XMLShop;

import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * This class is used to export export categories and favorites to external
 * ressources (for example OPML files).
 * 
 * @author <a href="mailto:bpasero@rssowl.org">Benjamin Pasero </a>
 * @version 1.2.3
 */
public class Exporter {
  private Document document;

  /**
   * Instantiate a new Exporter for OPML files
   */
  public Exporter() {
    document = initExportToFile("opml version=\"1.1\"", "opml");
  }

  /**
   * Instantiate a new Exporter for the given Format
   * 
   * @param root Name of the root Element
   * @param filePath The path to write the Document to
   */
  public Exporter(String root, String filePath) {
    document = initExportToFile(root, root, filePath);
  }

  /**
   * Export the given Category to a XML OPML file. The XML file contains all
   * favorits and sub- categorys.
   * 
   * @param rssOwlCategory Selected Category
   */
  public void exportCategoryToOPML(Category rssOwlCategory) {

    /** Could not init the document, return */
    if (document == null)
      return;

    /** Setup default template for XML Document */
    Element body = prepareOPMLDocument(document);

    /** Category is Root */
    if (rssOwlCategory == Category.getRootCategory()) {

      /** Directly export all Sub-Caqtegories */
      exportCategoryToOPML(rssOwlCategory, body);
    }

    /** Category is not Root */
    else {

      /** Root OPML element */
      Element rootOutline = new Element("outline");
      rootOutline.setAttribute("text", rssOwlCategory.getName());
      body.addContent(rootOutline);

      exportCategoryToOPML(rssOwlCategory, rootOutline);
    }

    /** Write the new DOM into temp File */
    XMLShop.writeXML(document, GlobalSettings.TEMP_EXPORT_FILE, true);
  }

  /**
   * Export the given Favorite to a XML OPML file.
   * 
   * @param rssOwlCategory Selected Category
   * @param favorite Selected Favorite
   */
  public void exportFavoriteToOPML(Favorite favorite, Category rssOwlCategory) {

    /** Could not init the document, return */
    if (document == null)
      return;

    /** Setup default template for XML Document */
    Element body = prepareOPMLDocument(document);

    /** Save category */
    Element outlineCat = new Element("outline");
    outlineCat.setAttribute("text", rssOwlCategory.getName());
    body.addContent(outlineCat);

    /** Save favorite */
    Element outline = new Element("outline");
    outline.setAttribute("text", favorite.getTitle());
    outline.setAttribute("title", favorite.getTitle());
    outline.setAttribute("type", "rss");
    outline.setAttribute("xmlUrl", favorite.getUrl());

    /** Extended information: Homepage */
    if (favorite.getHomepage() != null)
      outline.setAttribute("htmlUrl", favorite.getHomepage());

    /** Extended information: Language */
    if (StringShop.isset(favorite.getLanguage()))
      outline.setAttribute("language", favorite.getLanguage());

    /** Extended information: Description */
    if (favorite.getDescription() != null)
      outline.setAttribute("description", favorite.getDescription());

    /** Extended information: Open on startup */
    if (favorite.isOpenOnStartup())
      outline.setAttribute("openOnStartup", String.valueOf(true));

    /** Extended information: Re-Load on startup */
    if (favorite.isLoadOnStartup())
      outline.setAttribute("loadOnStartup", String.valueOf(true));

    /** Extended information: Use Proxy */
    if (favorite.isUseProxy())
      outline.setAttribute("useproxy", String.valueOf(true));

    /** Extended information: Update Interval */
    if (favorite.getUpdateInterval() != 0)
      outline.setAttribute("rssOwlUpdateInterval", String.valueOf(favorite.getUpdateInterval()));

    /** Add Element into Outline */
    outlineCat.addContent(outline);

    /** Write the new DOM into temp File */
    XMLShop.writeXML(document, GlobalSettings.TEMP_EXPORT_FILE, true);
  }

  /**
   * Export the given results to a XML OPML file.
   * 
   * @param links Some links with title to export
   * @param searchTopic The search topic that was used
   */
  public void exportResultsToOPML(Hashtable links, String searchTopic) {

    /** Could not init the document, return */
    if (document == null)
      return;

    /** Setup default template for XML Document */
    Element body = prepareOPMLDocument(document);

    /** Save category */
    Element outlineCat = new Element("outline");
    outlineCat.setAttribute("text", searchTopic);
    body.addContent(outlineCat);

    /** Save links */
    Set entrySet = links.entrySet();
    for (Iterator iterator = entrySet.iterator(); iterator.hasNext();) {
      Map.Entry entry = (Map.Entry) iterator.next();
      String url = (String) entry.getKey();
      String linkTitle = (String) entry.getValue();

      /** New Element outline */
      Element outlineLink = new Element("outline");
      outlineLink.setAttribute("text", linkTitle);
      outlineLink.setAttribute("title", linkTitle);
      outlineLink.setAttribute("type", "rss");
      outlineLink.setAttribute("xmlUrl", url);
      outlineCat.addContent(outlineLink);
    }

    /** Write the new DOM into temp File */
    XMLShop.writeXML(document, GlobalSettings.TEMP_EXPORT_FILE, true);
  }

  /**
   * Get the Document
   * 
   * @return Document The XML document
   */
  public Document getDocument() {
    return document;
  }

  /**
   * Recursivly export categories / favorits to the OPML file
   * 
   * @param rssOwlCategory Current rssOwlCategory
   * @param element Current outline element
   */
  private void exportCategoryToOPML(Category rssOwlCategory, Element element) {

    /** Save Sub - Categorys */
    TreeSet subCats = rssOwlCategory.getSortedSubCatTitles();
    Iterator subCatIt = subCats.iterator();
    while (subCatIt.hasNext()) {
      Category subCategory = (Category) rssOwlCategory.getSubCategories().get(subCatIt.next());

      /** New element outline */
      Element outline = new Element("outline");
      outline.setAttribute("text", subCategory.getName());
      element.addContent(outline);

      /** Recursivly export category */
      exportCategoryToOPML(subCategory, outline);
    }

    /** Save Favorites */
    Hashtable favorites = rssOwlCategory.getFavorites();
    Enumeration elements = favorites.elements();

    /** Foreach Favorite */
    while (elements.hasMoreElements()) {
      Favorite favorite = (Favorite) elements.nextElement();

      /** Do not export synchronizer */
      if (favorite.isSynchronizer())
        break;

      /** New element outline */
      Element outline = new Element("outline");
      outline.setAttribute("text", favorite.getTitle());
      outline.setAttribute("title", favorite.getTitle());
      outline.setAttribute("type", "rss");
      outline.setAttribute("xmlUrl", favorite.getUrl());

      /** Extended information: Open on startup */
      if (favorite.isOpenOnStartup())
        outline.setAttribute("openOnStartup", String.valueOf(true));

      /** Extended information: Re-Load on startup */
      if (favorite.isLoadOnStartup())
        outline.setAttribute("loadOnStartup", String.valueOf(true));

      /** Extended information: Use Proxy */
      if (favorite.isUseProxy())
        outline.setAttribute("useproxy", String.valueOf(true));

      /** Extended information: Update Interval */
      if (favorite.getUpdateInterval() != 0)
        outline.setAttribute("rssOwlUpdateInterval", String.valueOf(favorite.getUpdateInterval()));

      /** Extended information: Homepage */
      if (favorite.getHomepage() != null)
        outline.setAttribute("htmlUrl", favorite.getHomepage());

      /** Extended information: Language */
      if (StringShop.isset(favorite.getLanguage()))
        outline.setAttribute("language", favorite.getLanguage());

      /** Extended information: Description */
      if (favorite.getDescription() != null)
        outline.setAttribute("description", favorite.getDescription());

      /** Add Element into Outline */
      element.addContent(outline);
    }
  }

  /**
   * Init the document for the export to a file
   * 
   * @param rootOpen Open tag for the root element
   * @param rootClose Close tag for the root element
   * @return Document The initalized document
   */
  private Document initExportToFile(String rootOpen, String rootClose) {
    return initExportToFile(rootOpen, rootClose, GlobalSettings.TEMP_EXPORT_FILE);
  }

  /**
   * Init the document for the export to a file
   * 
   * @param rootOpen Open tag for the root element
   * @param rootClose Close tag for the root element
   * @param file File that should be initialized
   * @return Document The initalized document
   */
  private Document initExportToFile(String rootOpen, String rootClose, File file) {

    /** Init SAXBuilder */
    SAXBuilder builder = new SAXBuilder("org.apache.xerces.parsers.SAXParser");
    XMLShop.setDefaultEntityResolver(builder);

    Document document = null;
    FileWriter fw = null;

    /** Set the encoding to use. If not supported use UTF-8 by default */
    String encoding = XMLShop.isEncodingSupported(GlobalSettings.charEncoding) ? GlobalSettings.charEncoding : "UTF-8";

    /** Default Template */
    try {
      fw = new FileWriter(file);
      fw.write("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>\n");
      fw.write('<' + rootOpen + '>');
      fw.write("</" + rootClose + '>');
      fw.close();
    } catch (IOException e) {
      GUI.logger.log("initExportToFile()", e);
    } finally {

      /** Try to close the stream in any case */
      try {
        if (fw != null)
          fw.close();
      } catch (IOException e) {
        GUI.logger.log("initExportToFile()", e);
      }
    }

    /** Parse document */
    try {
      document = builder.build(file);

      /** Add Comment */
      Comment comment = new Comment("XML generated by RSSOwl (http://www.rssowl.org) on " + DateParser.formatDate(new Date(), true));
      document.getContent().add(0, comment);
    } catch (JDOMException e) {
      GUI.logger.log("initExportToFile()", e);
    } catch (IOException e) {
      GUI.logger.log("initExportToFile()", e);
    } catch (IllegalArgumentException e) {
      GUI.logger.log("initExportToFile()", e);
    }
    return document;
  }

  /**
   * Setup default OPML Template with head tag
   * 
   * @param document The current working OPML XML document
   * @return Element Body element
   */
  private Element prepareOPMLDocument(Document document) {
    Element root = document.getRootElement();

    /** Setup head */
    Element head = new Element("head");
    root.addContent(head);

    /** Title */
    Element title = new Element("title");
    title.setText("OPML generated by RSSOwl (http://www.rssowl.org)");
    head.addContent(title);

    /** Date */
    Element dateCreated = new Element("dateCreated");
    dateCreated.setText(DateParser.formatDate(new Date(), true));
    head.addContent(dateCreated);

    /** Owner */
    Element ownerName = new Element("ownerName");
    String owner = System.getProperty("user.name");
    ownerName.setText((owner != null) ? owner : "RSSOwl");
    head.addContent(ownerName);

    /** Setup body */
    Element body = new Element("body");
    root.addContent(body);
    return body;
  }

  /**
   * Init the document for the export to a file. This method also checks if the
   * medium is writeable.
   * 
   * @param rootOpen Open tag for the root element
   * @param rootClose Close tag for the root element
   * @param filePath Path to the file that should be initialized
   * @return Document The initalized document
   */
  Document initExportToFile(String rootOpen, String rootClose, String filePath) {
    return initExportToFile(rootOpen, rootClose, new File(filePath));
  }
}