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

import net.sourceforge.rssowl.controller.GUI;
import net.sourceforge.rssowl.controller.NewsTabFolder;
import net.sourceforge.rssowl.controller.statusline.LoadJob;
import net.sourceforge.rssowl.controller.statusline.StatusLine;
import net.sourceforge.rssowl.dao.NewsfeedFactory;
import net.sourceforge.rssowl.dao.NewsfeedFactoryException;
import net.sourceforge.rssowl.model.Category;
import net.sourceforge.rssowl.model.Channel;
import net.sourceforge.rssowl.model.Favorite;
import net.sourceforge.rssowl.model.TabItemData;
import net.sourceforge.rssowl.util.GlobalSettings;
import net.sourceforge.rssowl.util.archive.FeedCacheManager;
import net.sourceforge.rssowl.util.search.SearchDefinition;
import net.sourceforge.rssowl.util.shop.FileShop;
import net.sourceforge.rssowl.util.shop.PaintShop;

import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.widgets.Display;

/**
 * This Thread loads a newsfeed from its source and displays it calling the
 * appropiate method in the main controller. *
 * 
 * @author <a href="mailto:bpasero@rssowl.org">Benjamin Pasero </a>
 * @version 1.2.3
 */
public class FeedLoader extends ExtendedThread {
  Display display;
  int displayMode;
  boolean displayNewsfeed;
  boolean reselectNews;
  Channel rssChannel;
  FeedCacheManager rssOwlFeedCacheManager;
  GUI rssOwlGui;
  LoadJob rssOwlLoadJob;
  NewsTabFolder rssOwlNewsTabFolder;
  StatusLine rssOwlStatusLine;
  SearchDefinition searchDefinition;
  String url;

  /**
   * Instantiate a new FeedLoader Thread
   * 
   * @param url The URL of the newsfeed to load
   * @param searchDefinition Pattern and Scope of the Search.
   * @param displayNewsfeed If TRUE display the newsfeed in the tabfolder
   * @param reselectNews Wether to reselect a selected news
   * @param displayMode One of the supported display modes
   */
  public FeedLoader(String url, SearchDefinition searchDefinition, boolean displayNewsfeed, boolean reselectNews, int displayMode) {
    this.url = url;
    this.searchDefinition = searchDefinition;
    this.displayNewsfeed = displayNewsfeed;
    this.displayMode = displayMode;
    this.reselectNews = reselectNews;
    rssOwlGui = GUI.rssOwlGui;
    display = GUI.display;
    rssOwlFeedCacheManager = rssOwlGui.getFeedCacheManager();
    rssOwlNewsTabFolder = rssOwlGui.getRSSOwlNewsTabFolder();
    rssOwlStatusLine = rssOwlGui.getRSSOwlStatusLine();

    /** Name identifier */
    setName("Load Feed Thread");

    /** JVM should exit if this thread is still waiting for the URL connection */
    setDaemon(true);
  }

  /**
   * Load the newsfeed located at the URL
   * 
   * @see java.lang.Thread#run()
   */
  public void run() {

    /** Load the Channel from the live cache */
    if (rssOwlFeedCacheManager.isNewsfeedCached(url, false) && !FileShop.exists(url)) {
      rssChannel = rssOwlFeedCacheManager.getCachedNewsfeed(url);

      /** Set unread / read state in newsitems */
      if (!isStopped() && rssChannel != null)
        rssChannel.updateReadStatusOnNews();
    }

    /** Load the Channel from the local cache in case the user is working offline */
    else if (GlobalSettings.workOffline && rssOwlFeedCacheManager.isNewsfeedCached(url, true) && !FileShop.exists(url)) {
      rssChannel = rssOwlFeedCacheManager.getCachedNewsfeed(url);

      /** Set unread / read state in newsitems */
      if (!isStopped() && rssChannel != null)
        rssChannel.updateReadStatusOnNews();

      /** Store local cache into live cache */
      rssOwlFeedCacheManager.cacheNewsfeed(url, rssChannel);
    }

    /** The user is working offline but the feed not cached */
    else if (GlobalSettings.workOffline && !FileShop.exists(url)) {
      handleErrorLoading(new NewsfeedFactoryException(url, null, null, NewsfeedFactoryException.ERROR_WORKING_OFFLINE));

      /** Stop this thread */
      stopThread();
    }

    /** Only load this channel from URL if it wasnt opened yet */
    else if (rssChannel == null) {

      /** Try to load the channel from the URL */
      try {

        /** Retrieve and parse the newsfeed */
        if (!isStopped()) {
          NewsfeedFactory rssFactory = new NewsfeedFactory(url);
          rssChannel = rssFactory.getRSSChannel();

          /** As the feed was loaded from the URL, update cache */
          rssOwlFeedCacheManager.cacheNewsfeed(url, rssChannel);

          /** Update the Favorite's TreeItem (if it is a favorite) */
          if (!isStopped() && Category.getFavPool().containsKey(url)) {
            Favorite rssOwlFavorite = (Favorite) Category.getFavPool().get(url);
            rssOwlFavorite.updateReadStatus(rssChannel.getUnreadNewsCount());
            rssOwlFavorite.syncMetaData(rssChannel);
          }
        }
      }

      /** There was an error. Display it in a tab */
      catch (NewsfeedFactoryException e) {
        if (!isStopped() && GUI.isAlive()) {

          /** Handle this exception */
          handleErrorLoading(e);

          /** Stop this thread */
          stopThread();
        }
      }
    }

    /** Update error state if this is a favorite */
    if (!isStopped() && rssChannel != null && Category.getFavPool().containsKey(url)) {
      Favorite rssOwlFavorite = (Favorite) Category.getFavPool().get(url);
      rssOwlFavorite.updateErrorState(false);
    }

    /** Feed XML was found, thread is still running and display not disposed */
    if (!isStopped() && GUI.isAlive()) {

      /** Build a tab with the RSS Channel Informations */
      display.syncExec(new Runnable() {
        public void run() {

          /** Display the newsfeed in a tab */
          if (displayNewsfeed) {

            /** Display feed */
            if (!isStopped())
              rssOwlGui.displayNewsfeed(rssChannel, url, searchDefinition, reselectNews, displayMode);

            /** Add feed to last opened feeds is user has set so */
            if (GlobalSettings.reopenFeeds && !isStopped())
              rssOwlNewsTabFolder.addFeedToLastOpened(url);
          }

          /** Operation finished */
          if (!isStopped())
            rssOwlStatusLine.finishJob(rssOwlLoadJob);
        }
      });
    }
  }

  /**
   * Set the connected loading job which is operated by this Thread
   * 
   * @param rssOwlLoadJob The loading job which this Thread is performing
   */
  public void setRSSOwlLoadJob(LoadJob rssOwlLoadJob) {
    this.rssOwlLoadJob = rssOwlLoadJob;
  }

  /**
   * Handle the exception that was thrown while loading a newsfeed
   * 
   * @param xmlLoadException The exception that occured
   */
  private void handleErrorLoading(final NewsfeedFactoryException xmlLoadException) {

    /** Build a tab with the error text */
    display.syncExec(new Runnable() {
      public void run() {

        /** Check if the warning is already opened in the TabFolder */
        CTabItem existingTabItem = rssOwlNewsTabFolder.getFeedTabItem(url);

        /** Update error in existing tabitem */
        if (existingTabItem != null) {

          /** Prepare existing tabitem */
          existingTabItem.getControl().dispose();
          existingTabItem.setData(TabItemData.createErrorData(url));
          existingTabItem.setImage(PaintShop.iconError);

          /** Create error tab */
          rssOwlNewsTabFolder.showErrorTab(existingTabItem, xmlLoadException);

          /** Set selection to this item */
          if (rssOwlNewsTabFolder.focusTab()) {
            rssOwlNewsTabFolder.getNewsHeaderTabFolder().setSelection(existingTabItem);
            rssOwlNewsTabFolder.updateTabFolderState();
          }
        }

        /**
         * Create new tab to display error if user has set to show errors and
         * only if this loading Newsfeed is to be displayed in the Tabfolder.
         */
        else if (GlobalSettings.showErrors && displayNewsfeed)
          rssOwlNewsTabFolder.showErrorTab(url, xmlLoadException);

        /** Update the Favorite state of warning loading */
        Favorite rssOwlFavorite = (Favorite) Category.getFavPool().get(url);
        if (rssOwlFavorite != null)
          rssOwlFavorite.updateErrorState(true);

        /** Operation finished */
        rssOwlStatusLine.finishJob(rssOwlLoadJob);
      }
    });
  }
}