package hirondelle.movies;

import hirondelle.movies.exception.ExceptionHandler;
import hirondelle.movies.login.LoginController;
import hirondelle.movies.util.Util;

import java.awt.Font;
import java.io.IOException;
import java.util.Enumeration;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import javax.swing.UIManager;
import javax.swing.plaf.FontUIResource;

/**
 <b>Launch the application.</b>
 
 <P> Perform any needed one-time startup operations.
  Ask the user for their login credentials, and then display the main window.
*/
public final class LaunchApplication {

  /**
   Run the application.
   
   <P>Performs the following :
   <ul>
    <li>configure JDK logging : log at <tt>FINEST</tt> level to a file named <tt>log.txt</tt> in the application's home 
    directory. Overwrite the log file each time the application is launched. (Many apps would prefer to make the log level 
    sensitive to an environment property, or a user preference.)
    <li>configure a custom {@link ExceptionHandler} for uncaught exceptions
    <li>use the native look and feel, natural to the runtime operating system
    <li>sets the font for the application (12-point Verdana)
    <li>show the login screen (no real authentication is actually performed)
   </ul>
   
   <P>Some apps might also do these tasks upon startup :
   <ul>
    <li>display a temporary 'splash' screen
    <li>confirm a database connection
   </ul>
   
   <P>The Swing tutorial recommends the following style for application launch :
   <PRE> 
javax.swing.SwingUtilities.invokeLater(new Runnable() {
  public void run() {
    createAndShowGUI();
  }
});
  </PRE>
   However, that style is not used here, since it doesn't seem necessary. During launch, 
   no GUI has yet been realized, so it seems practically impossible for a thread to interact 
   with it. 
  */
  public static void main(String... aArgs){  
    configureJDKLogging();
    
    fLogger.config("Launching application...");
    fLogger.config("Operating System : " + System.getProperty("os.name")  + " " +  System.getProperty("os.version"));
    fLogger.config("Java Version: " + System.getProperty("java.version"));
    fLogger.config("Java Home: " + System.getProperty("java.home"));

    useCustomExceptionHandler();
    useNativeLookAndFeel();
    setApplicationFont();
    
    fLogger.config("Showing user login screen.");
    userLogin();
    
    fLogger.config("Launch thread now ending.");
  }
  
  /** 
  Defines the name of the app.
  Could be used in an About box, trouble ticket emails, and so on.
  */
  public static final String APP_NAME = "My Movies";
  
  /** 
   The version of this application (an arbitrary string). 
   Here, the version string simply matches the version of the JDK. 
  */
  public static final String APP_VERSION = "1.7.0";
  
  // PRIVATE 
 
  /** It makes no sense to call this class's constructor, so it's made private. */
  private LaunchApplication(){ }
  
  private static final Logger fLogger = Util.getLogger(LaunchApplication.class);

  /**
   Set up JDK logging to emit logging entries to a file in the application's
   installation directory. The file name is log.txt.
  */
  private static void configureJDKLogging() {
    fLogger.setLevel(Level.FINE);
    boolean OVERWRITE = false;
    int SINGLE_FILE = 1;
    int UNLIMITED_SIZE = 0;
    try {
      FileHandler fileHandler = new FileHandler("log.txt",  UNLIMITED_SIZE, SINGLE_FILE, OVERWRITE);
      fileHandler.setLevel(Level.FINEST);
      fileHandler.setFormatter(new SimpleFormatter());
      fLogger.addHandler(fileHandler);
    }
    catch (IOException ex){
      fLogger.severe("Cannot set up log file.");
    }
  }
  
  /**   See {@link ExceptionHandler}.    */
  private static void useCustomExceptionHandler(){
    fLogger.config("Setting up custom exception handler.");
    Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());
  }
  
   /**
    Use the look which is usually used on the current operating system.
    
    <P>For example, on a Windows machine, use a Windows look and feel.
    If you are using a non-default look and feel, then you should set it first thing.
    For more info, see the 
    <a href='http://java.sun.com/docs/books/tutorial/uiswing/lookandfeel/plaf.html#available'>Swing Tutorial</a>
    
    <P>Warning: on Windows, the native look & feel displays accelerator keys only when ALT key is held down. 
    Otherwise, they are not displayed!
  */
  private static void useNativeLookAndFeel() {
    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    }
    catch(Throwable ex){
      fLogger.severe("Cannot set the look and feel.");
    }
  }

  private static void setApplicationFont() {
    FontUIResource fontResource = new FontUIResource("Verdana",Font.PLAIN,12);
    Enumeration keys = UIManager.getDefaults().keys();
    while (keys.hasMoreElements()) {
      Object key = keys.nextElement();
      Object value = UIManager.get (key);
      if (value instanceof FontUIResource) {
        UIManager.put (key, fontResource);
      }
    }
  }
  
  /**
    Show the login screen. 
    
    <P>This is the first GUI element displayed to the end user.
    When the GUI is realized, then all interaction with the GUI must be through the 
    Event Dispatch thread.
    
    <P>For more info, see
    <a href='http://www.javapractices.com/topic/TopicAction.do?Id=153'>this article</a>.
   */
   private static void userLogin(){
     fLogger.config("Showing the login screen.");
     LoginController login = new LoginController();
     login.askUserForCredentials();
   }
}