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

import net.sourceforge.rssowl.controller.GUI;
import net.sourceforge.rssowl.controller.MessageBoxFactory;
import net.sourceforge.rssowl.controller.dialog.PreferencesDialog;
import net.sourceforge.rssowl.controller.thread.StreamGobbler;
import net.sourceforge.rssowl.controller.thread.UpdateManager;
import net.sourceforge.rssowl.util.GlobalSettings;
import net.sourceforge.rssowl.util.i18n.Dictionary;

import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.program.Program;

import java.io.IOException;

/**
 * Factory class for browser concerns in RSSOwl
 * 
 * @author <a href="mailto:bpasero@rssowl.org">Benjamin Pasero </a>
 * @version 1.2.3
 */
public class BrowserShop {
  
  /** Windows only: Mark of the Web */
  public static final String IE_MOTW = "<!-- saved from url=(0014)about:internet -->";

  /** Custom browser executable and arguments */
  static String executable;

  /** Local link to launch */
  static String localHref;

  /** Either netscape or mozilla for Linux / Solaris */
  static String webBrowser;

  /** Flag to indicate a successfull launch on Linux / Solaris */
  static boolean webBrowserSuccessfullyOpened;

  /** This utility class constructor is hidden */
  private BrowserShop() {
  // Protect default constructor
  }

  /**
   * Create a human readable Error message from the given SWTError. Point the
   * user to the FAQ as it includes the required help.
   * 
   * @param e The SWTError that occured while creating the Browser.
   * @return String Human readable Error Message pointing to the FAQ.
   */
  public static String createBrowserError(SWTError e) {
    StringBuffer error = new StringBuffer();
    error.append(GUI.i18n.getTranslation("DIALOG_ERROR_INTERNALBROWSER")).append("\n");
    error.append(e.getLocalizedMessage()).append("\n").append("\n");
    error.append("Please refer to the FAQ on how to setup your").append("\n");
    error.append("system to use the Internal Browser in RSSOwl.");
    return error.toString();
  }

  /**
   * Get the owl agent for the platform
   * 
   * @return String The owl agent
   */
  public static String getOwlAgent() {
    if (GlobalSettings.isWindows())
      return "RSSOwl/" + UpdateManager.getPublicVersion() + " (Windows; U; " + Dictionary.selectedLanguage + ")";
    else if (GlobalSettings.isLinux() || GlobalSettings.isSolaris())
      return "RSSOwl/" + UpdateManager.getPublicVersion() + " (X11; U; " + Dictionary.selectedLanguage + ")";
    else if (GlobalSettings.isMac())
      return "RSSOwl/" + UpdateManager.getPublicVersion() + " (Macintosh; U; " + Dictionary.selectedLanguage + ")";
    return "RSSOwl/" + UpdateManager.getPublicVersion();
  }

  /**
   * Get the user agent for the platform
   * 
   * @return String The user agent
   */
  public static String getUserAgent() {
    if (GlobalSettings.isWindows())
      return "Mozilla/5.0 (compatible; MSIE 6.0; Windows XP)";
    else if (GlobalSettings.isLinux())
      return "Mozilla/5.0 (X11; U; Linux i686)";
    else if (GlobalSettings.isSolaris())
      return "Mozilla/5.0 (X11; U; Solaris i686)";
    else if (GlobalSettings.isMac())
      return "Mozilla/5.0 (Macintosh; U)";
    return "Mozilla/5.0";
  }

  /**
   * Open a link in the external browser
   * 
   * @param href Any URL
   */
  public static void openLink(String href) {

    /** If href points to local file */
    if (href.startsWith("file:")) {
      href = href.substring(5);
      while (href.startsWith("/")) {
        href = href.substring(1);
      }
      href = "file:///" + href;
    }

    localHref = href;

    /** Surround href with double quotes if it containes spaces */
    if (localHref.indexOf(" ") >= 0)
      localHref = "\"" + localHref + "\"";

    /** Open custom browser if set */
    if (!GlobalSettings.customBrowser.equals(""))
      useCustomBrowser();

    /** Open default browser */
    else
      useDefaultBrowser();
  }

  /**
   * Open the given URL in a tab with the given title
   * 
   * @param url The URL of the link to open
   */
  public static void openLinkInTab(String url) {

    /** Open URL externaly if user has chosen it */
    if (GlobalSettings.openBrowserExtern || !GlobalSettings.useInternalBrowser())
      BrowserShop.openLink(url);

    /** Open URL in Tab */
    else {
      try {
        GUI.rssOwlGui.getRSSOwlNewsTabFolder().loadURLInTab(url);
      }

      /**
       * The internal browser is not yet supported on all operating systems. If
       * it fails, try the def. browser.
       */
      catch (SWTError e) {
        GUI.logger.log("openLinkInTab", e);

        /** Set flag to use external browser */
        GlobalSettings.openBrowserExtern = true;

        /** Inform user about Error */
        MessageBoxFactory.showError(GUI.shell, createBrowserError(e));
        GUI.rssOwlGui.getEventManager().actionOpenFAQOnBrowser();

        /** Open URL externaly */
        BrowserShop.openLink(url);
      }
    }
  }

  /** Use custom browser */
  private static void useCustomBrowser() {

    /** Get browser data */
    String executable = GlobalSettings.customBrowser;
    String args = GlobalSettings.customBrowserArguments;

    /** User has not set arguments */
    if (args.equals(""))
      BrowserShop.executable = executable + " " + localHref;

    /** User has set argument and placed the wildcard for the URL */
    else if (args.indexOf("[URL]") >= 0)
      BrowserShop.executable = executable + " " + StringShop.replaceAll(args, "[URL]", localHref);

    /** User has set argument but not placed the wildcard for the URL */
    else
      BrowserShop.executable = executable + " " + args + " " + localHref;

    /** Launch custom browser in seperate thread */
    Thread launcher = new Thread("Browser Launcher") {

      /**
       * @see java.lang.Runnable#run()
       */
      public void run() {

        /** Execute custom browser */
        try {
          Process proc = Runtime.getRuntime().exec(BrowserShop.executable);

          /** Let StreamGobbler handle error message */
          StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream());

          /** Let StreamGobbler handle output */
          StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream());

          /** Flush both error and output streams */
          errorGobbler.start();
          outputGobbler.start();

          /** Wait for the process to terminate */
          proc.waitFor();
        } catch (IOException e) {
          GUI.logger.log("useCustomBrowser()", e);

          /** Use default browser if custom browser is not working */
          useDefaultBrowser();

        } catch (InterruptedException e) {
          GUI.logger.log("useCustomBrowser()", e);
        }
      }
    };
    launcher.setDaemon(true);
    launcher.start();
  }

  /**
   * Display an error dialog if browser execution fails
   */
  static void errorMessage() {

    /** Only perform this Runnable if RSSOwl was not closed */
    if (GUI.isAlive()) {
      GUI.display.syncExec(new Runnable() {
        public void run() {
          MessageBoxFactory.showMessage(GUI.shell, SWT.ICON_ERROR, GUI.i18n.getTranslation("MESSAGE_BOX_TITLE_ERROR"), GUI.i18n.getTranslation("DIALOG_BROWSERSTART_FAILED"));

          /** Open the browser preferences for the user */
          PreferencesDialog.lastOpenedPropertyPage = 9;
          new PreferencesDialog(GUI.shell, GUI.i18n.getTranslation("MENU_PREFERENCES"), GUI.rssOwlGui).open();
        }
      });
    }
  }

  /**
   * Open the webbrowser on Linux or Solaris
   * 
   * @param href An URL
   * @return Process The process that was executed
   */
  static Process openWebBrowser(String href) {
    Process p = null;

    /** Try Netscape as default browser */
    if (webBrowser == null) {
      try {
        webBrowser = "netscape";
        p = Runtime.getRuntime().exec(webBrowser + "  " + href);
      } catch (IOException e) {
        webBrowser = "mozilla";
      }
    }

    /** Try Mozilla as default browser */
    if (p == null) {
      try {
        p = Runtime.getRuntime().exec(webBrowser + " " + href);
      } catch (IOException e) {
        GUI.logger.log("openWebBrowser", e);
        errorMessage();
      }
    }
    return p;
  }

  /**
   * Use default browser to display the URL
   */
  static void useDefaultBrowser() {

    /** Launch default browser on Windows */
    if (GlobalSettings.isWindows()) {
      Program.launch(localHref);
    }

    /** Launch default browser on Mac */
    else if (GlobalSettings.isMac()) {
      try {
        Process proc = Runtime.getRuntime().exec("/usr/bin/open " + localHref);

        /** Let StreamGobbler handle error message */
        StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream());

        /** Let StreamGobbler handle output */
        StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream());

        /** Flush both error and output streams */
        errorGobbler.start();
        outputGobbler.start();
      }

      /** Show error message, default browser could not be launched */
      catch (IOException e) {
        GUI.logger.log("useDefaultBrowser", e);
        errorMessage();
      }
    }

    /** Launch default browser on Linux & Solaris */
    else {

      /** Run browser in a seperate thread */
      Thread launcher = new Thread("Browser Launcher") {
        public void run() {
          try {

            /** The default browser was successfully launched once, use again */
            if (webBrowserSuccessfullyOpened) {
              Process proc = Runtime.getRuntime().exec(webBrowser + " -remote openURL(" + localHref + ")");

              /** Let StreamGobbler handle error message */
              StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream());

              /** Let StreamGobbler handle output */
              StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream());

              /** Flush both error and output streams */
              errorGobbler.start();
              outputGobbler.start();
            }

            /** The default browser was not yet launched, try NS and Mozilla */
            else {
              Process proc = openWebBrowser(localHref);
              webBrowserSuccessfullyOpened = true;

              if (proc != null) {

                /** Let StreamGobbler handle error message */
                StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream());

                /** Let StreamGobbler handle output */
                StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream());

                /** Flush both error and output streams */
                errorGobbler.start();
                outputGobbler.start();
              }

              /** Wait for this process */
              try {
                if (proc != null)
                  proc.waitFor();
              } catch (InterruptedException e) {
                GUI.logger.log("useDefaultBrowser", e);
              } finally {
                webBrowserSuccessfullyOpened = false;
              }
            }
          }

          /** Show error, default browser could not be launched */
          catch (IOException e) {
            GUI.logger.log("useDefaultBrowser", e);
            errorMessage();
          }
        }
      };
      launcher.setDaemon(true);
      launcher.start();
    }
  }
}