/*
 * Copyright (C) 2007-2009 KenD00
 * 
 * This file is part of DumpHD.
 * 
 * DumpHD is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package dumphd.gui;

import java.awt.Dimension;
import java.net.URL;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;

import dumphd.core.DumpHD;
import dumphd.util.MessagePrinter;
import dumphd.util.Utils;

/**
 * Main class of the GUI.
 * It contains some service methods used throughout the GUI and is used to create and destroy the GUI.
 * 
 * @author KenD00
 */
public class Manager {

   public final static int BIG_SPACING = 10;
   public final static int SMALL_SPACING = 5;
   public final static int BORDER_SPACING = 5;
   public final static int LABEL_SPACING = 5;
   public final static Dimension BOX_HORIZONTAL_BIG_SPACING = new Dimension(BIG_SPACING, 0);
   public final static Dimension BOX_HORIZONTAL_SMALL_SPACING = new Dimension(SMALL_SPACING, 0);
   public final static Dimension BOX_VERTICAL_BIG_SPACING = new Dimension(0, BIG_SPACING);
   public final static Dimension BOX_VERTICAL_SMALL_SPACING = new Dimension(0, SMALL_SPACING);

   private DumpHD dumpHD = null;
   private MainFrame mainFrame = null;
   private boolean working = false;



   public static URL getResource(String resource) {
      return Manager.class.getResource("/resources/" + resource);
   }

   /**
    * Creates an empty border.
    * 
    * @return The created border
    */
   public static Border createEmptyBorder() {
      return BorderFactory.createEmptyBorder(BORDER_SPACING, BORDER_SPACING, BORDER_SPACING, BORDER_SPACING);
   }

   /**
    * Creates a titled border with an empty inner border.
    *  
    * @param title The title
    * @return The created border
    */
   public static Border createTitledBorder(String title) {
      return BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(title), BorderFactory.createEmptyBorder(0, BORDER_SPACING, BORDER_SPACING, BORDER_SPACING));
   }

   /**
    * Creates a border with LABEL_SPACING borders left and right.
    * 
    * @return The created Border
    */
   public static Border createLabelBorder() {
      return BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(0, LABEL_SPACING, 0, LABEL_SPACING));
   }

   /**
    * Makes the label and the button the same height and sets the label maximum width to infinite and its minimum and preferred width to 32.
    * Uses the preferredSize of each component to calculate a height that is not smaller than the preferred height of any of the both.
    * 
    * @param label The label
    * @param button The button
    */
   public static void equalizeLabelButtonHeight(JLabel label, JButton button) {
      Dimension buttonPreferred = button.getPreferredSize();
      Dimension labelPreferred = label.getPreferredSize();

      if (buttonPreferred.height > labelPreferred.height) {
         label.setMinimumSize(new Dimension(32, buttonPreferred.height));
         label.setMaximumSize(new Dimension(Integer.MAX_VALUE, buttonPreferred.height));
         label.setPreferredSize(label.getMinimumSize());
         Dimension buttonDimension = new Dimension(buttonPreferred.width, buttonPreferred.height);
         button.setMinimumSize(buttonDimension);
         button.setMaximumSize(buttonDimension);
         button.setPreferredSize(buttonDimension);

      }
      else {
         label.setMinimumSize(new Dimension(32, labelPreferred.height));
         label.setMaximumSize(new Dimension(Integer.MAX_VALUE, labelPreferred.height));
         label.setPreferredSize(label.getMinimumSize());
         Dimension buttonDimension = new Dimension(buttonPreferred.width, labelPreferred.height);
         button.setMinimumSize(buttonDimension);
         button.setMaximumSize(buttonDimension);
         button.setPreferredSize(buttonDimension);
      }
      //System.out.println("Button minimum: " + button.getMinimumSize());
      //System.out.println("Button preferred: " + button.getPreferredSize());
      //System.out.println("Button maximum: " + button.getMaximumSize());
   }

   /**
    * Makes the given components the same fixed size.
    * Uses the preferredSize from each component to calculate a new size that not smaller than the preferred size of any of the both components.
    * Also sets the minium and maximum size of each component to this size.
    * 
    * @param component1 The first component
    * @param component2 The second component
    */
   public static void equalizeComponentSize(JComponent component1, JComponent component2) {
      Dimension component1Preferred = component1.getPreferredSize();
      Dimension component2Preferred = component2.getPreferredSize();
      int width = 0;
      int height = 0;
      if (component1Preferred.width > component2Preferred.width) {
         width = component1Preferred.width;
      }
      else {
         width = component2Preferred.width;
      }
      if (component1Preferred.height > component2Preferred.height) {
         height = component1Preferred.height;
      }
      else {
         height = component2Preferred.height;
      }
      Dimension newSize = new Dimension(width, height);
      component1.setMinimumSize(newSize);
      component1.setMaximumSize(newSize);
      component1.setPreferredSize(newSize);
      component2.setMinimumSize(newSize);
      component2.setMaximumSize(newSize);
      component2.setPreferredSize(newSize);
   }


   public Manager() {
      try {
         javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
               System.out.print("Creating GUI... ");
               mainFrame = new MainFrame(Manager.this);
               System.out.println("DONE");
               mainFrame.lock();
               mainFrame.show();
            }
         });
         MessagePrinter mainFramePrinter = mainFrame.getMessagePrinter();
         Utils.setMessagePrinter(mainFrame.getMessagePrinter());
         try {
            dumpHD = new DumpHD(mainFramePrinter, false);
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                  mainFrame.unlock();
               }
            });
         }
         catch (Exception e) {
            mainFramePrinter.println(e.getMessage());
            mainFramePrinter.println("Critical error: DumpHD could not be created");
         }
      }
      catch (Exception e) {
         System.out.println("\nCritical error occured while creating the GUI: " + e.getMessage());
         System.exit(1);
      }
   }

   public MainFrame getMainFrame() {
      return mainFrame;
   }

   public DumpHD getDumpHD() {
      return dumpHD;
   }

   //TODO: When should working be set to false? If set in the EventDispatching thread we cannot wait in the GUI for that event
   //TODO: Should working be an int? As boolean it is not allowed to execute multiple jobs. Hmm, this is in general not allowed! Wait for finish of a running job?
   public void execute(final ManagedJob workerJob, final ManagedJob eventDispatcherJob) {
      SwingWorker worker = new SwingWorker() {
         public Object construct() {
            return workerJob.run(null);
         }

         public void finished() {
            working = false;
            eventDispatcherJob.run(get());
         }
      };
      working = true;
      worker.start();
   }

   /**
    * Returns true if a call has been passed to the DumpHD object and the process has not been finished yet.
    * 
    * @return True, if the DumpHD object is currently working, false otherwise
    */
   public boolean isWorking() {
      return working;
   }

   /**
    * Exits the program.
    * 
    * TODO: Implement a way to stop DumpHD if its working
    */
   public void exit() {
      if (dumpHD != null) {
         dumpHD.close();
      }
      mainFrame.dispose();
      System.exit(0);
   }

}
