/*
 * 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.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.util.*;

import javax.swing.*;
import javax.swing.text.*;

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

/**
 * Main window of DumpHD.
 * 
 * FIXME: Don't use DiscSet.contentType as Index!!!!! A BDMV DiscSet will be overwritten by a BDRMV DiscSet!
 * 
 * @author KenD00
 */
public class MainFrame {

   private final static String NO_SOURCE_SET = "No source selected";
   private final static String NO_DESTINATION_SET = "No destination selected";
   private final static String BROWSE = "Browse";
   private final static String DUMP = "Dump";

   private final static UIManager.LookAndFeelInfo laf[] = UIManager.getInstalledLookAndFeels();


   private Manager manager = null;


   private MessagePrinter out = null;


   private boolean locked = false;


   private JFrame mainFrame = null;


   private JSplitPane horizontalSplitPane = null;
   private JSplitPane verticalSplitPane = null;

   private JLabel sourceLabel = null;
   private JButton sourceButton = null;
   private JPanel gridPanel = null;
   private JLabel[] sourceTypeImage = null;
   private JCheckBox[] sourceTypeCheckBox = null;
   private boolean[] sourceTypeSelectable = null;
   private int sourceTypesSelected = 0;
   private DiscSet[] discSets = null;
   private JFileChooser sourceChooser = null;

   private JLabel destinationLabel = null;
   private JButton destinationButton = null;
   private JFileChooser destinationChooser = null;
   private File destinationDirectory = null;

   private JButton dumpButton = null;

   private JTabbedPane tabPane = null;
   private LinkedList<SourceTab> sourceTabs = new LinkedList<SourceTab>();

   private JTextArea logArea = null;




   public MainFrame(Manager manager) {
      this.manager = manager;

      JMenuBar menuBar = null;
      JMenu menu = null;
      JMenuItem menuItem = null;
      ButtonGroup buttonGroup = null;
      JRadioButtonMenuItem radioButtonMenuItem = null;
      JPanel panel = null;

      String systemLafClassName = UIManager.getSystemLookAndFeelClassName();

      // Set the Look & Feel to the System Look & Feel
      try {
         UIManager.setLookAndFeel(systemLafClassName);
      }
      catch (UnsupportedLookAndFeelException e1) {
         // TODO: What to do?
      }
      catch (Exception e2) {
         // TODO: What to do?
      }

      // Set up the main JFrame
      mainFrame = new JFrame(DumpHD.PROGRAM_NAME + " " + DumpHD.PROGRAM_VERSION);
      mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
      mainFrame.addWindowListener(new MainFrameWindowAdapter());

      // ######################
      // ###****************###
      // ###*** Menu bar ***###
      // ###****************###
      // ######################
      menuBar = new JMenuBar();
      // *****************
      // *** File menu ***
      // *****************
      menu = new JMenu("File");
      //menuItem = new JMenuItem("Settings");
      //menu.add(menuItem);
      //menu.addSeparator();
      menuItem = new JMenuItem("Exit");
      menuItem.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            close();
         }
      });
      menu.add(menuItem);
      menuBar.add(menu);
      // ************************
      // *** Look & Feel menu ***
      // ************************
      menu = new JMenu("Look & Feel");
      LafActionListener lal = new LafActionListener();
      buttonGroup = new ButtonGroup();
      for (int i = 0; i < MainFrame.laf.length; i++) {
         String lafClassName = laf[i].getClassName();
         if (lafClassName.equals(systemLafClassName)) {
            radioButtonMenuItem = new JRadioButtonMenuItem(laf[i].getName(), true);
         }
         else {
            radioButtonMenuItem = new JRadioButtonMenuItem((laf[i].getName()), false);
         }
         radioButtonMenuItem.setActionCommand(lafClassName);
         radioButtonMenuItem.addActionListener(lal);
         buttonGroup.add(radioButtonMenuItem);
         menu.add(radioButtonMenuItem);
      }
      menuBar.add(menu);
      // *****************
      // *** Help menu ***
      // *****************
      menu = new JMenu("Help");
      //menuItem = new JMenuItem("Help");
      //menu.add(menuItem);
      //menu.addSeparator();
      menuItem = new JMenuItem("About");
      menuItem.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            JOptionPane.showMessageDialog (mainFrame, DumpHD.PROGRAM_NAME + " " + DumpHD.PROGRAM_VERSION +
                  "\n\nThis is the about box.\n" +
                  "\nWhen the time has come, it will be filled with content.\n\n" +
                  DumpHD.PROGRAM_AUTHOR,
                  "About", JOptionPane.INFORMATION_MESSAGE);
         }
      });
      menu.add(menuItem);
      menuBar.add(menu);
      mainFrame.setJMenuBar(menuBar);


      // ###########################
      // ###*********************###
      // ###*** Control panel ***###
      // ###*********************###
      // ###########################
      JPanel controlPanel = new JPanel();
      controlPanel.setOpaque(true);
      controlPanel.setBorder(Manager.createEmptyBorder());
      controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.PAGE_AXIS));

      // ##########################
      // ###### Source panel ######
      // ##########################
      JPanel sourcePanel = new JPanel();
      sourcePanel.setBorder(Manager.createTitledBorder("Source"));
      sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS));
      // ***************************
      // *** Source browse panel ***
      // ***************************
      panel = new JPanel();
      panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
      sourceLabel = new JLabel(MainFrame.NO_SOURCE_SET);
      sourceLabel.setBorder(Manager.createLabelBorder());
      sourceLabel.setToolTipText(MainFrame.NO_SOURCE_SET);
      sourceButton = new JButton(MainFrame.BROWSE);
      sourceButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            int returnVal = sourceChooser.showOpenDialog(mainFrame);
            if (returnVal == JFileChooser.APPROVE_OPTION) {
               final File selectedFile = sourceChooser.getSelectedFile().getAbsoluteFile();
               String selectedFilePath = selectedFile.getPath();
               sourceLabel.setText(selectedFilePath);
               sourceLabel.setToolTipText(selectedFilePath);
               clear();
               ManagedJob workload = new ManagedJob() {
                  public Object run(Object input) {
                     try {
                        MainFrame.this.manager.getDumpHD().initSource(selectedFile, null, null);
                        Collection<DiscSet> result = MainFrame.this.manager.getDumpHD().identifySource();
                        if (result.size() == 0) {
                           result = null;
                        }
                        return result;
                     }
                     catch (IOException e) {
                        return null;
                     }
                  }
               };
               ManagedJob guiJob = new ManagedJob() {
                  public Object run(Object input) {
                     if (input != null) {
                        MainFrame.this.init((Collection<DiscSet>)input);
                     }
                     MainFrame.this.unlock();
                     return null;
                  }
               };
               MainFrame.this.lock();
               MainFrame.this.manager.execute(workload, guiJob);
            }
         }
      });
      Manager.equalizeLabelButtonHeight(sourceLabel, sourceButton);
      panel.add(sourceLabel);
      panel.add(Box.createRigidArea(Manager.BOX_HORIZONTAL_BIG_SPACING));
      panel.add(sourceButton);
      sourcePanel.add(panel);
      sourcePanel.add(Box.createRigidArea(Manager.BOX_VERTICAL_BIG_SPACING));
      // ********************************
      // *** Source type select panel ***
      // ********************************
      gridPanel = new JPanel(new GridLayout(1, 0, Manager.BIG_SPACING, 0));
      JPanel checkBoxPanel = null;
      SourceTypeCheckBoxActionListener cbal = new SourceTypeCheckBoxActionListener();
      sourceTypeImage = new JLabel[3];
      sourceTypeCheckBox = new JCheckBox[6];
      sourceTypeSelectable = new boolean[6];
      discSets = new DiscSet[6];

      // ### HD-DVD Standard Content ###
      panel = new JPanel();
      panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
      checkBoxPanel = new JPanel();
      checkBoxPanel.setLayout(new BoxLayout(checkBoxPanel, BoxLayout.PAGE_AXIS));
      checkBoxPanel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
      sourceTypeImage[0] = new JLabel(new ImageIcon(Manager.getResource("icons/hdsc_logo.png")));
      sourceTypeImage[0].setDisabledIcon(new ImageIcon(Manager.getResource("icons/hdsc_logo_disabled.png")));
      sourceTypeImage[0].setAlignmentX(JComponent.CENTER_ALIGNMENT);
      sourceTypeCheckBox[0] = new JCheckBox("Video");
      sourceTypeCheckBox[0].setAlignmentX(JComponent.LEFT_ALIGNMENT);
      sourceTypeCheckBox[0].addActionListener(cbal);
      sourceTypeCheckBox[1] = new JCheckBox("Audio");
      sourceTypeCheckBox[1].setAlignmentX(JComponent.LEFT_ALIGNMENT);
      sourceTypeCheckBox[1].addActionListener(cbal);
      panel.add(sourceTypeImage[0]);
      panel.add(Box.createRigidArea(Manager.BOX_VERTICAL_SMALL_SPACING));
      checkBoxPanel.add(sourceTypeCheckBox[0]);
      checkBoxPanel.add(sourceTypeCheckBox[1]);
      panel.add(checkBoxPanel);
      gridPanel.add(panel);

      // ### HD-DVD Advanced Content ###
      panel = new JPanel();
      panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
      checkBoxPanel = new JPanel();
      checkBoxPanel.setLayout(new BoxLayout(checkBoxPanel, BoxLayout.PAGE_AXIS));
      checkBoxPanel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
      sourceTypeImage[1] = new JLabel(new ImageIcon(Manager.getResource("icons/hdac_logo.png")));
      sourceTypeImage[1].setDisabledIcon(new ImageIcon(Manager.getResource("icons/hdac_logo_disabled.png")));
      sourceTypeImage[1].setAlignmentX(JComponent.CENTER_ALIGNMENT);
      sourceTypeCheckBox[2] = new JCheckBox("Video");
      sourceTypeCheckBox[2].setAlignmentX(JComponent.LEFT_ALIGNMENT);
      sourceTypeCheckBox[2].addActionListener(cbal);
      sourceTypeCheckBox[3] = new JCheckBox("Audio");
      sourceTypeCheckBox[3].setAlignmentX(JComponent.LEFT_ALIGNMENT);
      sourceTypeCheckBox[3].addActionListener(cbal);
      panel.add(sourceTypeImage[1]);
      panel.add(Box.createRigidArea(Manager.BOX_VERTICAL_SMALL_SPACING));
      checkBoxPanel.add(sourceTypeCheckBox[2]);
      checkBoxPanel.add(sourceTypeCheckBox[3]);
      panel.add(checkBoxPanel);
      gridPanel.add(panel);

      // ### BluRay ###
      panel = new JPanel();
      panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
      checkBoxPanel = new JPanel();
      checkBoxPanel.setLayout(new BoxLayout(checkBoxPanel, BoxLayout.PAGE_AXIS));
      checkBoxPanel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
      sourceTypeImage[2] = new JLabel(new ImageIcon(Manager.getResource("icons/bd_logo.png")));
      sourceTypeImage[2].setDisabledIcon(new ImageIcon(Manager.getResource("icons/bd_logo_disabled.png")));
      sourceTypeImage[2].setAlignmentX(JComponent.CENTER_ALIGNMENT);
      sourceTypeCheckBox[4] = new JCheckBox("BDMV");
      sourceTypeCheckBox[4].setAlignmentX(JComponent.LEFT_ALIGNMENT);
      sourceTypeCheckBox[4].addActionListener(cbal);
      sourceTypeCheckBox[5] = new JCheckBox("BDAV");
      sourceTypeCheckBox[5].setAlignmentX(JComponent.LEFT_ALIGNMENT);
      sourceTypeCheckBox[5].addActionListener(cbal);
      panel.add(sourceTypeImage[2]);
      panel.add(Box.createRigidArea(Manager.BOX_VERTICAL_SMALL_SPACING));
      checkBoxPanel.add(sourceTypeCheckBox[4]);
      checkBoxPanel.add(sourceTypeCheckBox[5]);
      panel.add(checkBoxPanel);
      gridPanel.add(panel);

      gridPanel.setPreferredSize(gridPanel.getMinimumSize());
      gridPanel.setMaximumSize(new Dimension(gridPanel.getMaximumSize().width, gridPanel.getPreferredSize().height));

      sourcePanel.add(gridPanel);

      // ### Initialize default values ###
      for (int i = 0; i < sourceTypeImage.length; i++) {
         sourceTypeImage[i].setEnabled(false);
      }
      for (int i = 0; i < sourceTypeCheckBox.length; i++) {
         sourceTypeCheckBox[i].setEnabled(false);
         sourceTypeSelectable[i] = false;
         discSets[i] = null;
      }

      controlPanel.add(sourcePanel);

      // ### Setting up the FileChooser ###
      sourceChooser = new JFileChooser();
      sourceChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
      sourceChooser.setMultiSelectionEnabled(false);


      // ###############################
      // ###### Destination panel ######
      // ###############################
      panel = new JPanel();
      panel.setBorder(Manager.createTitledBorder("Destination"));
      panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
      destinationLabel = new JLabel(MainFrame.NO_DESTINATION_SET);
      destinationLabel.setBorder(Manager.createLabelBorder());
      destinationLabel.setToolTipText(MainFrame.NO_DESTINATION_SET);
      destinationButton = new JButton(MainFrame.BROWSE);
      destinationButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            int returnVal = destinationChooser.showSaveDialog(mainFrame);
            if (returnVal == JFileChooser.APPROVE_OPTION) {
               destinationDirectory = destinationChooser.getSelectedFile().getAbsoluteFile();
               String selectedFilePath = destinationDirectory.getPath();
               destinationLabel.setText(selectedFilePath);
               destinationLabel.setToolTipText(selectedFilePath);
               for (int i = 0; i < discSets.length; i++) {
                  if (discSets[i] != null) {
                     discSets[i].dstDir = destinationDirectory;
                  }
               }
               updateDumpButtonEnabledState();
            }
         }
      });
      Manager.equalizeLabelButtonHeight(destinationLabel, destinationButton);
      panel.add(destinationLabel);
      panel.add(Box.createRigidArea(Manager.BOX_HORIZONTAL_BIG_SPACING));
      panel.add(destinationButton);
      controlPanel.add(panel);

      controlPanel.add(Box.createVerticalGlue());
      controlPanel.add(Box.createRigidArea(Manager.BOX_VERTICAL_SMALL_SPACING));

      // ### Setting up the FileChooser ###
      destinationChooser = new JFileChooser();
      destinationChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
      destinationChooser.setMultiSelectionEnabled(false);


      // #######################
      // ### Control buttons ###
      // #######################
      panel = new JPanel();
      panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
      dumpButton = new JButton(MainFrame.DUMP);
      dumpButton.setEnabled(false);
      dumpButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            ManagedJob workload = new ManagedJob() {
               public Object run(Object input) {
                  MainFrame.this.manager.getDumpHD().dumpSource();
                  return null;
               }
            };
            ManagedJob guiJob = new ManagedJob() {
               public Object run(Object input) {
                  MainFrame.this.unlock();
                  return null;
               }
            };
            MainFrame.this.lock();
            MainFrame.this.manager.execute(workload, guiJob);
         }
      });
      panel.add(dumpButton);
      controlPanel.add(panel);


      // #########################
      // ###*******************###
      // ###*** Source view ***###
      // ###*******************###
      // #########################
      JPanel viewPanel = new JPanel(new BorderLayout());
      viewPanel.setOpaque(true);
      viewPanel.setBorder(Manager.createEmptyBorder());
      tabPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
      tabPane.setPreferredSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
      SourceTab tab = new SourceTab(manager);
      sourceTabs.add(tab);
      tabPane.addTab(tab.getTabTitle(), tab.getTabIcon(), tab.getTabComponent());
      viewPanel.add(tabPane, BorderLayout.CENTER);


      // #################
      // ###***********###
      // ###*** Log ***###
      // ###***********###
      // #################
      JPanel logPanel = new JPanel(new BorderLayout());
      logPanel.setOpaque(true);
      // ****************
      // *** Menu bar ***
      // ****************
      menuBar = new JMenuBar();
      menu = new JMenu("Log");
      menuItem = new JMenuItem("Clear");
      menuItem.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            Document logDocument = logArea.getDocument();
            try {
               logDocument.remove(0, logDocument.getLength());
            }
            catch (BadLocationException ex) {
               // TODO: What to do?
            }
         }
      });
      menu.add(menuItem);
      menuBar.add(menu);
      logPanel.add(menuBar, BorderLayout.PAGE_START);
      // *********************
      // *** Log text area ***
      // *********************
      logArea = new JTextArea();
      logArea.setEditable(false);
      JScrollPane scrollPane = new JScrollPane(logArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
      scrollPane.setMinimumSize(new Dimension(64, 64));
      logPanel.add(scrollPane, BorderLayout.CENTER);
      out = new TextAreaMessagePrinter();


      // ##############################
      // ###************************###
      // ###*** Mainframe layout ***###
      // ###************************###
      // ##############################
      // ******************
      // *** Splitpanes ***
      // ******************
      horizontalSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
      horizontalSplitPane.setResizeWeight(0.75);
      verticalSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true);
      verticalSplitPane.setResizeWeight(0.25);
      horizontalSplitPane.setLeftComponent(viewPanel);
      horizontalSplitPane.setRightComponent(controlPanel);
      verticalSplitPane.setTopComponent(horizontalSplitPane);
      verticalSplitPane.setBottomComponent(logPanel);
      // ************************
      // *** Mainframe layout ***
      // ************************
      mainFrame.setContentPane(verticalSplitPane);
      //mainFrame.pack();
      mainFrame.setSize(800, 600);
   }

   public void show() {
      mainFrame.setVisible(true);
   }

   public void dispose() {
      mainFrame.dispose();
   }

   public JFrame getFrame() {
      return mainFrame;
   }

   private void close() {
      if (manager.isWorking()) {
         int returnVal = JOptionPane.showConfirmDialog(mainFrame, "There is currently an operation running, exit anyway?", "Question", JOptionPane.OK_CANCEL_OPTION , JOptionPane.QUESTION_MESSAGE);
         if (returnVal != JOptionPane.OK_OPTION) {
            return;
         }
      }
      manager.exit();
   }

   public MessagePrinter getMessagePrinter() {
      return out;
   }

   // TODO: A clear must have been done before an init!
   public void init(Collection<DiscSet> discSets) {
      for (int i = discSets.size() - sourceTabs.size(); i > 0; i--) {
         sourceTabs.add(new SourceTab(manager));
      }
      boolean firstTab = true;
      Iterator<DiscSet> dsIt = discSets.iterator();
      Iterator<SourceTab> tabIt = sourceTabs.iterator();
      while (dsIt.hasNext()) {
         DiscSet ds = dsIt.next();
         SourceTab tab = tabIt.next();
         // FIXME: Hack! This makes sure the BDR types won't be put outside the array
         if (!ds.isRecordable()) {
            this.discSets[ds.contentType - 1] = ds;
         } else {
            this.discSets[ds.contentType - 2] = ds;
         }
         if (destinationDirectory != null) {
            ds.dstDir = destinationDirectory;
         }
         // FIXME: Hack! This makes sure that for the BDR types nothing outside the arrays will be accessed
         if (!ds.isRecordable()) {
            sourceTypeImage[(ds.contentType - 1) / 2].setEnabled(true);
            sourceTypeCheckBox[ds.contentType - 1].setSelected(ds.selected);
            sourceTypeSelectable[ds.contentType - 1] = ds.selected;
         } else {
            sourceTypeImage[(ds.contentType - 2) / 2].setEnabled(true);
            sourceTypeCheckBox[ds.contentType - 2].setSelected(ds.selected);
            sourceTypeSelectable[ds.contentType - 2] = ds.selected;
         }
         if (ds.selected) {
            sourceTypesSelected++;
         }
         tab.init(ds);
         if (firstTab) {
            firstTab = false;
            tabPane.setTitleAt(0, tab.getTabTitle());
            tabPane.setIconAt(0, tab.getTabIcon());
         }
         else {
            tabPane.addTab(tab.getTabTitle(), tab.getTabIcon(), tab.getTabComponent());
         }
      }
      updateEnabledState(false);
      updateDumpButtonEnabledState();
   }

   public void clear() {
      // Clear the tab panel
      int tabsInPane = tabPane.getTabCount();
      Iterator<SourceTab> tabIt = sourceTabs.iterator();
      for (int i = 0; i < tabsInPane; i++) {
         SourceTab tab = tabIt.next();
         tab.clear();
         if (i != 0) {
            tabIt.remove();
            tabPane.removeTabAt(i);
         }
         else {
            tabPane.setTitleAt(0, tab.getTabTitle());
            tabPane.setIconAt(0, tab.getTabIcon());
         }
      }
      // Clear the select panel
      for (int i = 0; i < sourceTypeCheckBox.length; i++) {
         sourceTypeCheckBox[i].setSelected(false);
         sourceTypeSelectable[i] = false;
         discSets[i] = null;
      }
      sourceTypesSelected = 0;
      for (int i = 0; i < sourceTypeImage.length; i++) {
         sourceTypeImage[i].setEnabled(false);
      }
      updateEnabledState(false);
      updateDumpButtonEnabledState();
   }

   private void updateDumpButtonEnabledState() {
      if (!locked) {
         if (destinationDirectory != null && sourceTypesSelected > 0) {
            if (!dumpButton.isEnabled()) {
               dumpButton.setEnabled(true);
            }
         }
         else {
            if (dumpButton.isEnabled()) {
               dumpButton.setEnabled(false);
            }
         }
      }
   }

   private void updateEnabledState(boolean forced) {
      if (!locked) {
         for (int i = 0; i < sourceTypeCheckBox.length; i++) {
            sourceTypeCheckBox[i].setEnabled(sourceTypeSelectable[i]);
         }
         if (forced) {
            sourceButton.setEnabled(true);
            destinationButton.setEnabled(true);
            updateDumpButtonEnabledState();
         }
      }
      else {
         if (forced) {
            for (int i = 0; i < sourceTypeCheckBox.length; i++) {
               sourceTypeCheckBox[i].setEnabled(false);
            }
            sourceButton.setEnabled(false);
            destinationButton.setEnabled(false);
            dumpButton.setEnabled(false);
         }
      }
   }

   public void lock() {
      locked = true;
      updateEnabledState(true);
      Iterator<SourceTab> it = sourceTabs.iterator();
      while (it.hasNext()) {
         it.next().lock();
      }
   }

   public void unlock() {
      locked = false;
      updateEnabledState(true);
      Iterator<SourceTab> it = sourceTabs.iterator();
      while (it.hasNext()) {
         it.next().unlock();
      }
   }

   public boolean isLocked() {
      return locked;
   }

   public void updateFixedSizedComponentsSize() {
      sourceLabel.setMinimumSize(null);
      sourceLabel.setMaximumSize(null);
      sourceLabel.setPreferredSize(null);
      sourceButton.setMinimumSize(null);
      sourceButton.setMaximumSize(null);
      sourceButton.setPreferredSize(null);
      Manager.equalizeLabelButtonHeight(sourceLabel, sourceButton);
      gridPanel.setMaximumSize(null);
      gridPanel.setPreferredSize(null);
      gridPanel.setPreferredSize(gridPanel.getMinimumSize());
      gridPanel.setMaximumSize(new Dimension(gridPanel.getMaximumSize().width, gridPanel.getPreferredSize().height));
      destinationLabel.setMinimumSize(null);
      destinationLabel.setMaximumSize(null);
      destinationLabel.setPreferredSize(null);
      destinationButton.setMinimumSize(null);
      destinationButton.setMaximumSize(null);
      destinationButton.setPreferredSize(null);
      Manager.equalizeLabelButtonHeight(destinationLabel, destinationButton);
      // Invalidate a element in each panel to force it to relayout
      // FIXME: Somehow the gridPanel does not change its size, the images get sometimes cut
      sourceButton.invalidate();
      destinationButton.invalidate();
      gridPanel.invalidate();
      Iterator<SourceTab> it = sourceTabs.iterator();
      while (it.hasNext()) {
         it.next().updateFixedSizedComponentsSize();
      }
   }



   private class TextAreaMessagePrinter implements MessagePrinter {

      public void print(final String msg) {
         Runnable runMe = new Runnable() {
            public void run() {
               logArea.append(msg);
            }
         };
         if (SwingUtilities.isEventDispatchThread()) {
            runMe.run();
         } else {
            SwingUtilities.invokeLater(runMe);
         }
      }

      public void println() {
         Runnable runMe = new Runnable() {
            public void run() {
               logArea.append("\n");
            }
         };
         if (SwingUtilities.isEventDispatchThread()) {
            runMe.run();
         } else {
            SwingUtilities.invokeLater(runMe);
         }
      }

      public void println(final String msg) {
         Runnable runMe = new Runnable() {
            public void run() {
               logArea.append(msg);
               logArea.append("\n");
            }
         };
         if (SwingUtilities.isEventDispatchThread()) {
            runMe.run();
         } else {
            SwingUtilities.invokeLater(runMe);
         }
      }
   }

   private class MainFrameWindowAdapter extends WindowAdapter {
      public void windowClosing(WindowEvent e) {
         close();
      }
   }

   private class LafActionListener implements ActionListener {
      public void actionPerformed(ActionEvent e) {
         String lafClassName = e.getActionCommand();
         try {
            UIManager.setLookAndFeel(lafClassName);
            SwingUtilities.updateComponentTreeUI(mainFrame);
            SwingUtilities.updateComponentTreeUI(sourceChooser);
            SwingUtilities.updateComponentTreeUI(destinationChooser);
            logArea.setCaretPosition(logArea.getDocument().getLength());
            updateFixedSizedComponentsSize();
            horizontalSplitPane.resetToPreferredSizes();
            verticalSplitPane.resetToPreferredSizes();
         }
         catch (UnsupportedLookAndFeelException e1) {
            // TODO: What to do?
         }
         catch (Exception e2) {
            // TODO: What to do?
         }
      }
   }

   private class SourceTypeCheckBoxActionListener implements ActionListener {
      public void actionPerformed(ActionEvent e) {
         JCheckBox source = (JCheckBox)e.getSource();
         boolean selected = source.isSelected();
         if (selected) {
            sourceTypesSelected++;
         }
         else {
            sourceTypesSelected--;
         }
         for (int i = 0; i < sourceTypeCheckBox.length; i++) {
            if (source == sourceTypeCheckBox[i]) {
               discSets[i].selected = selected;
               break;
            }
         }
         updateDumpButtonEnabledState();
      }
   }

}
