package com.swingsane.gui.panel;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JViewport;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.apache.log4j.Logger;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDPixelMap;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
import org.imgscalr.Scalr.Rotation;

import com.google.common.io.Files;
import com.swingsane.business.image.transform.CropTransform;
import com.swingsane.business.image.transform.IImageTransform;
import com.swingsane.business.image.transform.RotateTransform;
import com.swingsane.business.notification.DialogNotificationImpl;
import com.swingsane.business.notification.INotification;
import com.swingsane.business.scanning.ScanEvent;
import com.swingsane.gui.component.ImageLabel;
import com.swingsane.gui.dialog.ImageTransformDialog;
import com.swingsane.i18n.Localizer;
import com.swingsane.preferences.IPreferredDefaults;
import com.swingsane.preferences.ISwingSanePreferences;
import com.swingsane.util.FilenameExtensionFilter;
import com.swingsane.util.Misc;
import com.swingsane.util.RandomGUID;

@SuppressWarnings("serial")
public class PreviewPanel extends JPanel implements MouseMotionListener, MouseListener,
MouseWheelListener {

  private class TempFileListItem {

    private File tempFile;
    private String batchPrefix;
    private int pageNumber;
    private int pagesToScan;
    private String guid = (new RandomGUID()).toString();

    public TempFileListItem(ScanEvent scanEvent) throws IOException {
      tempFile = createTempImageFile(scanEvent.getBufferedImage());
      pagesToScan = scanEvent.getPagesToScan();
      pageNumber = scanEvent.getPageNumber();
      batchPrefix = scanEvent.getBatchPrefix();
    }

    private File createTempImageFile(BufferedImage bufferedImage) throws IOException {
      File tempDir = preferences.getTempDirectory();
      File temporaryFile = File.createTempFile(guid, "", tempDir);
      temporaryFile.deleteOnExit();
      ImageIO.write(bufferedImage, "PNG", temporaryFile);
      return temporaryFile;
    }

    @Override
    public String toString() {
      return parseBatchPrefix(batchPrefix, pageNumber + "", pagesToScan);
    }

  }

  /**
   * Log4J logger.
   */
  private static final Logger LOG = Logger.getLogger(PreviewPanel.class);

  private BufferedImage sourceImage;

  private BufferedImage convertedImage;
  private Float realZoom = 1.0f;

  private Float lastZoom = 1.0f;
  private JSplitPane dialogPane;
  private JPanel contentPanel;
  private JScrollPane imagePreviewScrollPane;
  private ImageLabel imagePreviewLabel;
  private JPanel masterPanel;
  private JPanel zoomPanel;
  private JComboBox<String> zoomBox;
  private JLabel zoomInLabel;

  private JLabel zoomOutLabel;

  private final Cursor defCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);

  private final Cursor moveCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
  private final Point pp = new Point();
  private JPanel tempFileListPanel;
  private JList<TempFileListItem> tempFileList;

  private DefaultListModel<TempFileListItem> tempFileListModel = new DefaultListModel<TempFileListItem>();
  private JPanel scannedImagesActionsPanel;
  private JButton saveScanButton;
  private JPanel imagePreviewPanel;
  private JScrollPane tempFileListScrollPane;
  private JComboBox<String> exportTypeComboBox;
  private JPanel scannedImagesButtonPanel;
  private JButton removeScanButton;

  private JButton transformButton;
  private ISwingSanePreferences preferences;
  private JLabel rotateLeftLabel;
  private JLabel rotateRightLabel;
  private JPanel imageTransformPanel;

  private JLabel cropLabel;

  private IPreferredDefaults preferredDefaults;

  public PreviewPanel() {
    initComponents();
  }

  public final void addImage(ScanEvent scanEvent) throws IOException {
    TempFileListItem tempFileListItem = new TempFileListItem(scanEvent);
    tempFileListModel.addElement(tempFileListItem);
  }

  private void cropActionPerformed() {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        int[] indicies = tempFileList.getSelectedIndices();
        if (indicies.length <= 0) {
          String message = Localizer.localize("RequireImagesToCropSelectionMessage");
          JOptionPane.showMessageDialog(getRootPane().getTopLevelAncestor(), message);
          return;
        }
        CropTransform cropTransform = new CropTransform();
        cropTransform.setSourceImageFile(tempFileList.getSelectedValue().tempFile);
        cropTransform.setOutputImageFile(tempFileList.getSelectedValue().tempFile);
        try {
          cropTransform.configure(preferredDefaults);
          cropTransform.transform();
          tempFileListSelectionChanged(null);
        } catch (Exception ex) {
          LOG.error(ex, ex);
          showCropErrorMessage(ex);
        }
      }
    });
  }

  private File getSaveDirectory() {
    JFileChooser fd = new JFileChooser();
    fd.setCurrentDirectory(new File("."));
    fd.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
    fd.setDialogTitle(Localizer.localize("SaveImagesToDirectoryTittle"));
    fd.setAcceptAllFileFilterUsed(false);
    if (fd.showOpenDialog(getRootPane().getTopLevelAncestor()) == JFileChooser.APPROVE_OPTION) {
      File directory = fd.getCurrentDirectory();
      File selectedFile = fd.getSelectedFile();
      if ((selectedFile != null) && selectedFile.isDirectory()) {
        return selectedFile;
      } else if (directory != null) {
        return directory;
      }
    }
    return null;
  }

  private File getSaveFile(String filename, String extension) {
    FileDialog fd = new FileDialog((Frame) getRootPane().getTopLevelAncestor(),
        Localizer.localize("SaveImagesToFileTittle"), FileDialog.SAVE);
    fd.setDirectory(".");
    FilenameExtensionFilter filter = new FilenameExtensionFilter();
    filter.addExtension(extension);
    fd.setFilenameFilter(filter);
    fd.setFile(filename + "." + extension);
    fd.setModal(true);
    fd.setVisible(true);
    return new File(fd.getDirectory() + File.separator + fd.getFile());
  }

  private Point getViewportMidpoint(JViewport viewport) {
    Point viewPosition = viewport.getViewPosition();
    Rectangle viewportRectangle = new Rectangle(viewPosition, viewport.getSize());
    Point viewportMidpoint = new Point((int) viewportRectangle.getCenterX(),
        (int) viewportRectangle.getCenterY());
    return viewportMidpoint;
  }

  private void initComponents() {

    setBorder(new EmptyBorder(5, 5, 5, 5));
    setOpaque(false);

    contentPanel = new JPanel();
    contentPanel.setOpaque(false);
    imagePreviewScrollPane = new JScrollPane();
    imagePreviewScrollPane.setOpaque(false);
    imagePreviewScrollPane.setWheelScrollingEnabled(false);

    imagePreviewLabel = new ImageLabel();
    imagePreviewLabel.setFont(UIManager.getFont("Label.font"));

    setLayout(new BorderLayout());

    contentPanel.setBorder(null);
    contentPanel.setLayout(new GridBagLayout());
    ((GridBagLayout) contentPanel.getLayout()).columnWidths = new int[] { 0, 0 };
    ((GridBagLayout) contentPanel.getLayout()).rowHeights = new int[] { 0, 0, 0, 0 };
    ((GridBagLayout) contentPanel.getLayout()).columnWeights = new double[] { 1.0, 1.0E-4 };
    ((GridBagLayout) contentPanel.getLayout()).rowWeights = new double[] { 1.0, 0.0, 0.0, 1.0E-4 };
    imagePreviewScrollPane.setViewportBorder(null);
    imagePreviewLabel.setHorizontalAlignment(SwingConstants.CENTER);
    imagePreviewLabel.setViewer(this);
    imagePreviewScrollPane.setViewportView(imagePreviewLabel);
    imagePreviewPanel = new JPanel();
    imagePreviewPanel.setOpaque(false);

    imagePreviewPanel.setBorder(new CompoundBorder(new TitledBorder(Localizer
        .localize("ImagePreviewBorderTitle")), new EmptyBorder(5, 5, 5, 5)));
    GridBagConstraints gbc_imagePreviewPanel = new GridBagConstraints();
    gbc_imagePreviewPanel.insets = new Insets(0, 0, 5, 0);
    gbc_imagePreviewPanel.fill = GridBagConstraints.BOTH;
    gbc_imagePreviewPanel.gridx = 0;
    gbc_imagePreviewPanel.gridy = 0;
    contentPanel.add(imagePreviewPanel, gbc_imagePreviewPanel);
    GridBagLayout gbl_imagePreviewPanel = new GridBagLayout();
    gbl_imagePreviewPanel.columnWidths = new int[] { 0 };
    gbl_imagePreviewPanel.rowHeights = new int[] { 0 };
    gbl_imagePreviewPanel.columnWeights = new double[] { Double.MIN_VALUE };
    gbl_imagePreviewPanel.rowWeights = new double[] { Double.MIN_VALUE };
    imagePreviewPanel.setLayout(gbl_imagePreviewPanel);
    imagePreviewPanel.add(imagePreviewScrollPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
        GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 5, 0), 0, 0));

    tempFileListPanel = new JPanel();
    GridBagConstraints gbc_tempFileListPanel = new GridBagConstraints();
    gbc_tempFileListPanel.fill = GridBagConstraints.VERTICAL;
    gbc_tempFileListPanel.gridx = 1;
    gbc_tempFileListPanel.gridy = 0;

    tempFileListPanel.setBorder(new CompoundBorder(new TitledBorder(Localizer
        .localize("ScannedImagesBorderTitle")), new EmptyBorder(5, 5, 5, 5)));
    tempFileListPanel.setOpaque(false);
    GridBagLayout gbl_tempFileListPanel = new GridBagLayout();
    gbl_tempFileListPanel.columnWidths = new int[] { 0, 0 };
    gbl_tempFileListPanel.rowHeights = new int[] { 0, 100, 0, 0 };
    gbl_tempFileListPanel.columnWeights = new double[] { 1.0, Double.MIN_VALUE };
    gbl_tempFileListPanel.rowWeights = new double[] { 0.0, 1.0, 0.0, Double.MIN_VALUE };
    tempFileListPanel.setLayout(gbl_tempFileListPanel);

    scannedImagesButtonPanel = new JPanel();
    GridBagConstraints gbc_scannedImagesButtonPanel = new GridBagConstraints();
    gbc_scannedImagesButtonPanel.fill = GridBagConstraints.HORIZONTAL;
    gbc_scannedImagesButtonPanel.insets = new Insets(0, 0, 5, 0);
    gbc_scannedImagesButtonPanel.gridx = 0;
    gbc_scannedImagesButtonPanel.gridy = 0;
    tempFileListPanel.add(scannedImagesButtonPanel, gbc_scannedImagesButtonPanel);
    scannedImagesButtonPanel.setOpaque(false);
    GridBagLayout gbl_scannedImagesButtonPanel = new GridBagLayout();
    gbl_scannedImagesButtonPanel.columnWidths = new int[] { 164, 0, 109, 0 };
    gbl_scannedImagesButtonPanel.rowHeights = new int[] { 26, 0 };
    gbl_scannedImagesButtonPanel.columnWeights = new double[] { 0.0, 1.0, 0.0, Double.MIN_VALUE };
    gbl_scannedImagesButtonPanel.rowWeights = new double[] { 0.0, Double.MIN_VALUE };
    scannedImagesButtonPanel.setLayout(gbl_scannedImagesButtonPanel);

    removeScanButton = new JButton(Localizer.localize("Remove"));
    removeScanButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        removeActionPerformed(e);
      }
    });

    transformButton = new JButton(Localizer.localize("TransformButtonText"));
    transformButton.setEnabled(true);
    transformButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        transformActionPerformed(e);
      }
    });
    transformButton.setIcon(new ImageIcon(PreviewPanel.class
        .getResource("/com/famfamfam/silk/wand.png")));
    transformButton.setFont(UIManager.getFont("Button.font"));
    transformButton.setMargin(new Insets(1, 5, 1, 5));
    GridBagConstraints gbc_transformButton = new GridBagConstraints();
    gbc_transformButton.anchor = GridBagConstraints.WEST;
    gbc_transformButton.insets = new Insets(0, 0, 0, 5);
    gbc_transformButton.gridx = 0;
    gbc_transformButton.gridy = 0;
    scannedImagesButtonPanel.add(transformButton, gbc_transformButton);
    removeScanButton.setIcon(new ImageIcon(PreviewPanel.class
        .getResource("/com/famfamfam/silk/delete.png")));
    removeScanButton.setFont(UIManager.getFont("Button.font"));
    removeScanButton.setMargin(new Insets(1, 5, 1, 5));
    GridBagConstraints gbc_removeScanButton = new GridBagConstraints();
    gbc_removeScanButton.anchor = GridBagConstraints.NORTHEAST;
    gbc_removeScanButton.gridx = 2;
    gbc_removeScanButton.gridy = 0;
    scannedImagesButtonPanel.add(removeScanButton, gbc_removeScanButton);

    tempFileListScrollPane = new JScrollPane();
    tempFileListScrollPane.setMinimumSize(new Dimension(150, 22));
    GridBagConstraints gbc_tempFileListScrollPane = new GridBagConstraints();
    gbc_tempFileListScrollPane.insets = new Insets(0, 0, 5, 0);
    gbc_tempFileListScrollPane.fill = GridBagConstraints.BOTH;
    gbc_tempFileListScrollPane.gridx = 0;
    gbc_tempFileListScrollPane.gridy = 1;
    tempFileListPanel.add(tempFileListScrollPane, gbc_tempFileListScrollPane);

    tempFileList = new JList<TempFileListItem>();
    tempFileList.setFont(UIManager.getFont("List.font"));
    tempFileList.addListSelectionListener(new ListSelectionListener() {
      @Override
      public void valueChanged(ListSelectionEvent e) {
        tempFileListSelectionChanged(e);
      }
    });
    tempFileListScrollPane.setViewportView(tempFileList);

    dialogPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, contentPanel, tempFileListPanel);
    masterPanel = new JPanel();
    masterPanel.setBorder(new EmptyBorder(0, 12, 0, 12));
    zoomPanel = new JPanel();
    zoomBox = new JComboBox<String>();
    zoomInLabel = new JLabel();
    zoomOutLabel = new JLabel();
    masterPanel.setOpaque(false);
    masterPanel.setLayout(new GridBagLayout());
    ((GridBagLayout) masterPanel.getLayout()).columnWidths = new int[] { 0, 0, 0, 0 };
    ((GridBagLayout) masterPanel.getLayout()).rowHeights = new int[] { 0, 0 };
    ((GridBagLayout) masterPanel.getLayout()).columnWeights = new double[] { 1.0, 0.0, 1.0, 1.0E-4 };
    ((GridBagLayout) masterPanel.getLayout()).rowWeights = new double[] { 0.0, 1.0E-4 };
    zoomPanel.setOpaque(false);
    zoomPanel.setLayout(new GridBagLayout());
    ((GridBagLayout) zoomPanel.getLayout()).columnWidths = new int[] { 0, 0, 0, 0, 0, 10, 0 };
    ((GridBagLayout) zoomPanel.getLayout()).rowHeights = new int[] { 0, 0 };
    ((GridBagLayout) zoomPanel.getLayout()).columnWeights = new double[] { 1.0, 0.0, 0.0, 0.0, 1.0,
        1.0, 1.0E-4 };
    ((GridBagLayout) zoomPanel.getLayout()).rowWeights = new double[] { 0.0, 1.0E-4 };
    zoomBox.setModel(new DefaultComboBoxModel<String>(new String[] { "5%", "10%", "25%", "50%",
        "100%", "200%", "500%", "1000%" }));
    zoomBox.setSelectedIndex(4);
    zoomBox.setFont(UIManager.getFont("ComboBox.font"));
    zoomBox.addItemListener(new ItemListener() {
      @Override
      public void itemStateChanged(ItemEvent e) {
        zoomBoxItemStateChanged(e);
      }
    });
    zoomPanel.add(zoomBox, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
        GridBagConstraints.BOTH, new Insets(0, 0, 0, 5), 0, 0));
    zoomInLabel.setIcon(new ImageIcon(PreviewPanel.class
        .getResource("/com/famfamfam/silk/zoom_in.png")));
    zoomInLabel.setFont(UIManager.getFont("Label.font"));
    zoomInLabel.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        zoomInLabelMouseClicked(e);
      }

      @Override
      public void mouseEntered(MouseEvent e) {
        zoomInLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
      }

      @Override
      public void mouseExited(MouseEvent e) {
        zoomInLabel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
    });
    zoomPanel.add(zoomInLabel, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
        GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 5), 0, 0));
    zoomOutLabel.setIcon(new ImageIcon(PreviewPanel.class
        .getResource("/com/famfamfam/silk/zoom_out.png")));
    zoomOutLabel.setFont(UIManager.getFont("Label.font"));
    zoomOutLabel.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        zoomOutLabelMouseClicked(e);
      }

      @Override
      public void mouseEntered(MouseEvent e) {
        zoomOutLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
      }

      @Override
      public void mouseExited(MouseEvent e) {
        zoomOutLabel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
    });
    zoomPanel.add(zoomOutLabel, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0,
        GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 5), 0, 0));
    masterPanel.add(zoomPanel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
        GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 5), 0, 0));
    contentPanel.add(masterPanel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
        GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 5, 0), 0, 0));

    imageTransformPanel = new JPanel();
    imageTransformPanel.setOpaque(false);
    GridBagConstraints gbc_imageTransformPanel = new GridBagConstraints();
    gbc_imageTransformPanel.fill = GridBagConstraints.BOTH;
    gbc_imageTransformPanel.gridx = 0;
    gbc_imageTransformPanel.gridy = 2;
    contentPanel.add(imageTransformPanel, gbc_imageTransformPanel);
    GridBagLayout gbl_imageTransformPanel = new GridBagLayout();
    gbl_imageTransformPanel.columnWidths = new int[] { 0, 0, 100, 0, 0 };
    gbl_imageTransformPanel.rowHeights = new int[] { 16, 0 };
    gbl_imageTransformPanel.columnWeights = new double[] { 0.0, 1.0, 0.0, 0.0, Double.MIN_VALUE };
    gbl_imageTransformPanel.rowWeights = new double[] { 0.0, Double.MIN_VALUE };
    imageTransformPanel.setLayout(gbl_imageTransformPanel);

    cropLabel = new JLabel(Localizer.localize("CropImageLabelText"));
    cropLabel.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        if (e.isConsumed()) {
          return;
        }
        cropActionPerformed();
        e.consume();
      }

      @Override
      public void mouseEntered(MouseEvent e) {
        cropLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
      }

      @Override
      public void mouseExited(MouseEvent e) {
        cropLabel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
    });
    cropLabel.setIcon(new ImageIcon(PreviewPanel.class
        .getResource("/com/famfamfam/silk/shape_handles.png")));
    cropLabel.setFont(UIManager.getFont("Label.font"));
    GridBagConstraints gbc_cropLabel = new GridBagConstraints();
    gbc_cropLabel.insets = new Insets(5, 5, 5, 5);
    gbc_cropLabel.gridx = 0;
    gbc_cropLabel.gridy = 0;
    imageTransformPanel.add(cropLabel, gbc_cropLabel);

    rotateLeftLabel = new JLabel(Localizer.localize("RotateLeftLabelText"));
    GridBagConstraints gbc_rotateLeftLabel = new GridBagConstraints();
    gbc_rotateLeftLabel.insets = new Insets(5, 5, 5, 5);
    gbc_rotateLeftLabel.anchor = GridBagConstraints.NORTHWEST;
    gbc_rotateLeftLabel.gridx = 2;
    gbc_rotateLeftLabel.gridy = 0;
    imageTransformPanel.add(rotateLeftLabel, gbc_rotateLeftLabel);
    rotateLeftLabel.setFont(UIManager.getFont("Label.font"));
    rotateLeftLabel.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        if (e.isConsumed()) {
          return;
        }
        rotateLeftActionPerformed();
        e.consume();
      }

      @Override
      public void mouseEntered(MouseEvent e) {
        rotateLeftLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
      }

      @Override
      public void mouseExited(MouseEvent e) {
        rotateLeftLabel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
    });
    rotateLeftLabel.setIcon(new ImageIcon(PreviewPanel.class
        .getResource("/com/famfamfam/silk/arrow_rotate_anticlockwise.png")));

    rotateRightLabel = new JLabel(Localizer.localize("RotateRightLabelText"));
    GridBagConstraints gbc_rotateRightLabel = new GridBagConstraints();
    gbc_rotateRightLabel.insets = new Insets(5, 5, 5, 5);
    gbc_rotateRightLabel.gridx = 3;
    gbc_rotateRightLabel.gridy = 0;
    imageTransformPanel.add(rotateRightLabel, gbc_rotateRightLabel);
    rotateRightLabel.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        if (e.isConsumed()) {
          return;
        }
        rotateRightActionPerformed();
        e.consume();
      }

      @Override
      public void mouseEntered(MouseEvent e) {
        rotateRightLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
      }

      @Override
      public void mouseExited(MouseEvent e) {
        rotateRightLabel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
    });
    rotateRightLabel.setFont(UIManager.getFont("Label.font"));
    rotateRightLabel.setIcon(new ImageIcon(PreviewPanel.class
        .getResource("/com/famfamfam/silk/arrow_rotate_clockwise.png")));
    dialogPane.setResizeWeight(0.9);
    add(dialogPane, BorderLayout.CENTER);

    scannedImagesActionsPanel = new JPanel();
    scannedImagesActionsPanel.setOpaque(false);
    GridBagConstraints gbc_scannedImagesActionsPanel = new GridBagConstraints();
    gbc_scannedImagesActionsPanel.fill = GridBagConstraints.HORIZONTAL;
    gbc_scannedImagesActionsPanel.gridx = 0;
    gbc_scannedImagesActionsPanel.gridy = 2;
    tempFileListPanel.add(scannedImagesActionsPanel, gbc_scannedImagesActionsPanel);
    GridBagLayout gbl_scannedImagesActionsPanel = new GridBagLayout();
    gbl_scannedImagesActionsPanel.columnWidths = new int[] { 0, 0 };
    gbl_scannedImagesActionsPanel.rowHeights = new int[] { 0, 0, 0, 0 };
    gbl_scannedImagesActionsPanel.columnWeights = new double[] { 1.0, Double.MIN_VALUE };
    gbl_scannedImagesActionsPanel.rowWeights = new double[] { 1.0, 1.0, 1.0, Double.MIN_VALUE };
    scannedImagesActionsPanel.setLayout(gbl_scannedImagesActionsPanel);

    JPanel exportTypePanel = new JPanel();
    exportTypePanel.setOpaque(false);
    GridBagConstraints gbc_exportTypePanel = new GridBagConstraints();
    gbc_exportTypePanel.insets = new Insets(0, 0, 5, 0);
    gbc_exportTypePanel.fill = GridBagConstraints.BOTH;
    gbc_exportTypePanel.gridx = 0;
    gbc_exportTypePanel.gridy = 1;
    scannedImagesActionsPanel.add(exportTypePanel, gbc_exportTypePanel);
    GridBagLayout gbl_exportTypePanel = new GridBagLayout();
    gbl_exportTypePanel.columnWidths = new int[] { 155, 0, 0 };
    gbl_exportTypePanel.rowHeights = new int[] { 24, 0 };
    gbl_exportTypePanel.columnWeights = new double[] { 1.0, 0.0, Double.MIN_VALUE };
    gbl_exportTypePanel.rowWeights = new double[] { 0.0, Double.MIN_VALUE };
    exportTypePanel.setLayout(gbl_exportTypePanel);

    exportTypeComboBox = new JComboBox<String>();
    exportTypeComboBox.setFont(UIManager.getFont("ComboBox.font"));
    exportTypeComboBox.setModel(new DefaultComboBoxModel<String>(new String[] {
        Localizer.localize("JPGSinglePageText"),
        Localizer.localize("PNGSinglePageText"),
        Localizer.localize("PDFSinglePageText"),
        Localizer.localize("PDFMultiPageText") }));
    exportTypeComboBox.setSelectedIndex(0);
    GridBagConstraints gbc_exportTypeComboBox = new GridBagConstraints();
    gbc_exportTypeComboBox.fill = GridBagConstraints.HORIZONTAL;
    gbc_exportTypeComboBox.insets = new Insets(0, 0, 0, 5);
    gbc_exportTypeComboBox.gridx = 0;
    gbc_exportTypeComboBox.gridy = 0;
    exportTypePanel.add(exportTypeComboBox, gbc_exportTypeComboBox);

    saveScanButton = new JButton(Localizer.localize("SaveButtonText"));
    GridBagConstraints gbc_saveScanButton = new GridBagConstraints();
    gbc_saveScanButton.gridx = 1;
    gbc_saveScanButton.gridy = 0;
    exportTypePanel.add(saveScanButton, gbc_saveScanButton);
    saveScanButton.setFont(UIManager.getFont("Button.font"));
    saveScanButton.setMargin(new Insets(1, 5, 1, 5));
    saveScanButton.setIcon(new ImageIcon(PreviewPanel.class
        .getResource("/com/famfamfam/silk/disk.png")));
    saveScanButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        saveScanActionPerformed(e);
      }
    });
  }

  public final void initialize() {
    tempFileList.setModel(tempFileListModel);
    imagePreviewScrollPane.addMouseMotionListener(this);
    imagePreviewScrollPane.addMouseListener(this);
    imagePreviewScrollPane.addMouseWheelListener(this);
    loadDebugImages();
  }

  private void loadDebugImages() {
    File userHomeDir = new File(System.getProperty("user.home"));
    if (userHomeDir.exists() && userHomeDir.canRead() && userHomeDir.isDirectory()) {
      File[] files = userHomeDir.listFiles();
      for (File file : files) {
        if (file.exists() && file.isFile() && file.canRead()) {
          if (file.getName().toLowerCase().startsWith("swingsane_debug_image")) {
            try {
              ScanEvent scanEvent = new ScanEvent(file);
              scanEvent.setBatchPrefix(file.getName());
              scanEvent.setPageNumber(1);
              scanEvent.setPagesToScan(1);
              scanEvent.setAcquiredImage(ImageIO.read(file));
              addImage(scanEvent);
            } catch (Exception ex) {
              LOG.debug(ex, ex);
            }
          }
        }
      }
    }
  }

  @Override
  public void mouseClicked(MouseEvent e) {
  }

  @Override
  public final void mouseDragged(MouseEvent e) {
    JViewport vport = ((JScrollPane) e.getSource()).getViewport();
    Point cp = e.getPoint();
    Point vp = vport.getViewPosition();
    vp.translate(pp.x - cp.x, pp.y - cp.y);
    imagePreviewLabel.scrollRectToVisible(new Rectangle(vp, vport.getSize()));
    pp.setLocation(cp);
  }

  @Override
  public void mouseEntered(MouseEvent e) {
  }

  @Override
  public void mouseExited(MouseEvent e) {
  }

  @Override
  public void mouseMoved(MouseEvent e) {
  }

  @Override
  public final void mousePressed(MouseEvent e) {
    imagePreviewLabel.setCursor(moveCursor);
    pp.setLocation(e.getPoint());
  }

  @Override
  public final void mouseReleased(MouseEvent e) {
    imagePreviewLabel.setCursor(defCursor);
    imagePreviewLabel.repaint();
  }

  @Override
  public final void mouseWheelMoved(MouseWheelEvent e) {

    Point clickPoint = e.getPoint();

    JViewport viewport = ((JScrollPane) e.getSource()).getViewport();
    Point viewPosition = viewport.getViewPosition();

    // 1. Get viewport rectangle.
    Rectangle viewportRectangle = new Rectangle(viewPosition, viewport.getSize());

    // 2. Get the midpoint of the viewport.
    Point viewportMidpoint = getViewportMidpoint(viewport);

    // 3. calculate the distance from the click point to the midpoint
    int deltaX = (viewPosition.x + clickPoint.x) - viewportMidpoint.x;
    int deltaY = (viewPosition.y + clickPoint.y) - viewportMidpoint.y;

    // 4. translate the viewport rectangle
    viewportRectangle.translate(deltaX, deltaY);

    // 5. scroll the viewport to the new viewport rectangle position
    imagePreviewLabel.scrollRectToVisible(viewportRectangle);

    int notches = e.getWheelRotation();
    if (notches < 0) {
      zoomIn();
    } else {
      zoomOut();
    }

  }

  private String parseBatchPrefix(String batchPrefix, String pageNumber, int pagesToScan) {
    String str = batchPrefix;
    str = str.replace(Localizer.localize("PageNumberToken"), pageNumber);
    str = str.replace(Localizer.localize("PageCountToken"), pagesToScan + "");
    return str;
  }

  private File parseOutputFileName(File file, String pageNumber, int pagesToScan) {
    return parseOutputFileName(file.getAbsolutePath(), pageNumber, pagesToScan);
  }

  private File parseOutputFileName(String prefix, String pageNumber, int pagesToScan) {
    File file = new File(parseBatchPrefix(prefix, pageNumber, pagesToScan));
    return file;
  }

  private void removeActionPerformed(ActionEvent e) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        int[] indicies = tempFileList.getSelectedIndices();
        if (indicies.length <= 0) {
          String message = Localizer.localize("RequireImagesToRemoveSelectionMessage");
          JOptionPane.showMessageDialog(getRootPane().getTopLevelAncestor(), message);
          return;
        }
        ArrayList<TempFileListItem> itemsToRemove = new ArrayList<TempFileListItem>();
        for (int index : indicies) {
          itemsToRemove.add(tempFileListModel.get(index));
        }
        for (TempFileListItem tempFileListItem : itemsToRemove) {
          tempFileListItem.tempFile.delete();
          tempFileListModel.removeElement(tempFileListItem);
        }
        tempFileList.revalidate();
        tempFileListSelectionChanged(null);
      }
    });
  }

  public final void renderImagePreview(Graphics2D g) {

    if (sourceImage != null) {

      if (convertedImage == null) {

        GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment()
            .getDefaultScreenDevice();
        GraphicsConfiguration gc = gd.getDefaultConfiguration();

        if (sourceImage.getColorModel() != gc.getColorModel()) {
          convertedImage = gc.createCompatibleImage(sourceImage.getWidth(),
              sourceImage.getHeight(), Transparency.OPAQUE);
          Graphics2D g2d = convertedImage.createGraphics();
          g2d.drawImage(sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight(), null);
          convertedImage.flush();
          g2d.dispose();
        } else {
          convertedImage = sourceImage;
        }

      }

      g.setColor(Color.darkGray);
      g.fill(new Rectangle2D.Double(0, 0, imagePreviewLabel.getWidth(), imagePreviewLabel
          .getHeight()));

      g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
          RenderingHints.VALUE_FRACTIONALMETRICS_ON);
      g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);

      double x = (imagePreviewLabel.getWidth() / 2) - ((sourceImage.getWidth() * realZoom) / 2);
      double y = (imagePreviewLabel.getHeight() / 2) - ((sourceImage.getHeight() * realZoom) / 2);
      AffineTransform at = AffineTransform.getTranslateInstance(x, y);
      at.scale(realZoom, realZoom);
      g.drawRenderedImage(convertedImage, at);

    }

  }

  private void resizeImageLabel() {

    JViewport viewport = imagePreviewScrollPane.getViewport();
    Dimension viewportSize = viewport.getSize();
    Point viewportMidpoint = getViewportMidpoint(viewport);

    viewportMidpoint.x /= lastZoom;
    viewportMidpoint.y /= lastZoom;
    viewportMidpoint.x *= realZoom;
    viewportMidpoint.y *= realZoom;
    viewportMidpoint.x -= (viewportSize.width / 2);
    viewportMidpoint.y -= (viewportSize.height / 2);

    // create a new scaled rectangle around the midpoint
    Rectangle scrollToRectangle = new Rectangle(viewportMidpoint, viewportSize);

    Dimension dim = null;
    if (sourceImage != null) {
      dim = new Dimension((int) (sourceImage.getWidth() * realZoom),
          (int) (sourceImage.getHeight() * realZoom));
    } else {
      dim = new Dimension((int) (10 * realZoom), (int) (10 * realZoom));
    }
    if (imagePreviewLabel != null) {
      imagePreviewLabel.setMaximumSize(dim);
      imagePreviewLabel.setMinimumSize(dim);
      imagePreviewLabel.setPreferredSize(dim);
    }
    imagePreviewLabel.revalidate();
    imagePreviewLabel.scrollRectToVisible(scrollToRectangle);

  }

  private void rotateLeftActionPerformed() {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        rotateSelection(Rotation.CW_270);
      }
    });
  }

  private void rotateRightActionPerformed() {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        rotateSelection(Rotation.CW_90);
      }
    });
  }

  private void rotateSelection(final Rotation rotation) {

    int[] indicies = tempFileList.getSelectedIndices();
    if (indicies.length <= 0) {
      String message = Localizer.localize("RequireImagesToRotateSelectionMessage");
      JOptionPane.showMessageDialog(getRootPane().getTopLevelAncestor(), message);
      return;
    }

    final INotification notification = new DialogNotificationImpl(getRootPane()
        .getTopLevelAncestor());

    SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {

      @Override
      protected Void doInBackground() throws Exception {
        notification.message(Localizer.localize("RotatingImageMessageText"));

        int[] indicies = tempFileList.getSelectedIndices();
        if (indicies.length <= 0) {
          throw new Exception(Localizer.localize("RequireImagesToRotateSelectionMessage"));
        }

        for (int index : indicies) {
          TempFileListItem tempFileListItem = tempFileList.getModel().getElementAt(index);
          RotateTransform rotateTransform = new RotateTransform();
          rotateTransform.setRotation(rotation);
          rotateTransform.setSourceImageFile(tempFileListItem.tempFile);
          rotateTransform.setOutputImageFile(tempFileListItem.tempFile);
          rotateTransform.transform();
        }

        return null;
      }

      @Override
      protected void done() {
        try {
          get();
          ((JDialog) notification).setVisible(false);
          tempFileListSelectionChanged(null);
        } catch (Exception ex) {
          LOG.error(ex, ex);
        } finally {
          ((JDialog) notification).dispose();
        }
      }
    };

    worker.execute();

    ((JDialog) notification).setModal(true);
    ((JDialog) notification).setVisible(true);

  }

  private void saveIndividualPDFFile(final ArrayList<TempFileListItem> selectedItems,
      final File outputFile, final String batchPrefix) {

    final INotification notification = new DialogNotificationImpl(getRootPane()
        .getTopLevelAncestor());

    SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
      private PDDocument document = null;

      @Override
      protected Void doInBackground() throws Exception {
        for (TempFileListItem tempFileListItem : selectedItems) {
          File destinationFile = parseOutputFileName(outputFile.getCanonicalFile() + File.separator
              + batchPrefix + ".pdf", tempFileListItem.pageNumber + "",
              tempFileListItem.pagesToScan);
          notification.message(String.format(Localizer.localize("ReadingImageFileMessage"),
              tempFileListItem.tempFile.getName()));
          BufferedImage bufferedImage = ImageIO.read(tempFileListItem.tempFile);
          document = new PDDocument();
          PDPage page = new PDPage(new PDRectangle(bufferedImage.getWidth(),
              bufferedImage.getHeight()));
          notification.message(Localizer.localize("AddingPageMessage"));
          document.addPage(page);
          PDXObjectImage ximage = null;
          ximage = new PDPixelMap(document, bufferedImage);
          PDPageContentStream contentStream = new PDPageContentStream(document, page, true, true);
          contentStream.drawXObject(ximage, 0, 0, bufferedImage.getWidth(),
                  bufferedImage.getHeight());
          contentStream.close();
          notification.message(String.format(Localizer.localize("SavingFileMessage"),
                  destinationFile.getCanonicalPath()));
          document.save(destinationFile);
        }
        return null;
      }

      @Override
      protected void done() {
        try {
          get();
          showSaveSuccessMessage();
          ((JDialog) notification).setVisible(false);
        } catch (Exception ex) {
          LOG.error(ex, ex);
          showSaveErrorMessage(ex);
        } finally {
          if (document != null) {
            try {
              document.close();
            } catch (IOException e) {
            }
          }
          ((JDialog) notification).dispose();
        }
      }
    };

    worker.execute();

    ((JDialog) notification).setModal(true);
    ((JDialog) notification).setVisible(true);

  }

  private void saveMultiPagePDFFile(final ArrayList<TempFileListItem> selectedItems,
      final File destinationFile) {

    final INotification notification = new DialogNotificationImpl(getRootPane()
        .getTopLevelAncestor());

    SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
      private PDDocument document = null;

      @Override
      protected Void doInBackground() throws Exception {
        document = new PDDocument();
        for (TempFileListItem tempFileListItem : selectedItems) {
          notification.message(String.format(Localizer.localize("ReadingImageFileMessage"),
              tempFileListItem.tempFile.getName()));
          BufferedImage bufferedImage = ImageIO.read(tempFileListItem.tempFile);
          PDPage page = new PDPage(new PDRectangle(bufferedImage.getWidth(),
              bufferedImage.getHeight()));
          notification.message(Localizer.localize("AddingPageMessage"));
          document.addPage(page);
          PDXObjectImage ximage = null;
          ximage = new PDPixelMap(document, bufferedImage);
          PDPageContentStream contentStream = new PDPageContentStream(document, page, true, true);
          contentStream.drawXObject(ximage, 0, 0, bufferedImage.getWidth(),
              bufferedImage.getHeight());
          contentStream.close();
        }
        notification.message(String.format(Localizer.localize("SavingFileMessage"),
            destinationFile.getCanonicalPath()));
        document.save(destinationFile);
        return null;
      }

      @Override
      protected void done() {
        try {
          get();
          showSaveSuccessMessage();
          ((JDialog) notification).setVisible(false);
        } catch (Exception ex) {
          LOG.error(ex, ex);
          showSaveErrorMessage(ex);
        } finally {
          if (document != null) {
            try {
              document.close();
            } catch (IOException e) {
            }
          }
          ((JDialog) notification).dispose();
        }
      }
    };

    worker.execute();

    ((JDialog) notification).setModal(true);
    ((JDialog) notification).setVisible(true);

  }

  private void savePNGFile(final ArrayList<TempFileListItem> selectedItems, final File outputFile,
                           final String batchPrefix) {

    final INotification notification = new DialogNotificationImpl(getRootPane()
            .getTopLevelAncestor());

    SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
      @Override
      protected Void doInBackground() throws Exception {
        for (TempFileListItem tempFileListItem : selectedItems) {
          notification.message(String.format(Localizer.localize("CopyingFileMessage"),
                  tempFileListItem.tempFile.getName()));
          final File destinationFile = parseOutputFileName(outputFile.getCanonicalFile()
                          + File.separator + batchPrefix + ".png", tempFileListItem.pageNumber + "",
                  tempFileListItem.pagesToScan);
          Files.copy(tempFileListItem.tempFile, destinationFile);
        }
        return null;
      }

      @Override
      protected void done() {
        try {
          get();
          showSaveSuccessMessage();
          ((JDialog) notification).setVisible(false);
        } catch (Exception ex) {
          LOG.error(ex, ex);
          showSaveErrorMessage(ex);
        } finally {
          ((JDialog) notification).dispose();
        }
      }
    };
    worker.execute();

    ((JDialog) notification).setModal(true);
    ((JDialog) notification).setVisible(true);

  }

  private void saveJPGFile(final ArrayList<TempFileListItem> selectedItems, final File outputFile,
                           final String batchPrefix) {

    final INotification notification = new DialogNotificationImpl(getRootPane()
            .getTopLevelAncestor());

    SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
      @Override
      protected Void doInBackground() throws Exception {
        for (TempFileListItem tempFileListItem : selectedItems) {
          notification.message(String.format(Localizer.localize("CopyingFileMessage"),
                  tempFileListItem.tempFile.getName()));
          final File destinationFile = parseOutputFileName(outputFile.getCanonicalFile()
                          + File.separator + batchPrefix + ".jpg", tempFileListItem.pageNumber + "",
                  tempFileListItem.pagesToScan);

          //convert to JPG
          BufferedImage img = ImageIO.read(tempFileListItem.tempFile);
          BufferedImage jpg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
          jpg.createGraphics().drawImage(img, 0, 0, Color.WHITE, null);
          ImageIO.write(jpg, "jpg", destinationFile);
        }
        return null;
      }

      @Override
      protected void done() {
        try {
          get();
          showSaveSuccessMessage();
          ((JDialog) notification).setVisible(false);
        } catch (Exception ex) {
          LOG.error(ex, ex);
          showSaveErrorMessage(ex);
        } finally {
          ((JDialog) notification).dispose();
        }
      }
    };
    worker.execute();

    ((JDialog) notification).setModal(true);
    ((JDialog) notification).setVisible(true);

  }

  private void saveScanActionPerformed(ActionEvent e) {

    ArrayList<TempFileListItem> selectedItems = new ArrayList<TempFileListItem>();
    int[] indicies = tempFileList.getSelectedIndices();
    if (indicies.length <= 0) {
      String message = Localizer.localize("RequireImagesToSaveSelectionMessage");
      JOptionPane.showMessageDialog(getRootPane().getTopLevelAncestor(), message);
      return;
    }

    for (int index : indicies) {
      selectedItems.add(tempFileListModel.get(index));
    }

    File outputFile = null;
    int selectedIndex = exportTypeComboBox.getSelectedIndex();

    TempFileListItem firstItem = selectedItems.get(0);
    if (firstItem == null) {
      return;
    }

    switch (selectedIndex) {
      case 0:
        outputFile = getSaveDirectory();
        if (outputFile == null) {
          return;
        }
        saveJPGFile(selectedItems, outputFile, firstItem.batchPrefix);
        break;
    case 1:
      outputFile = getSaveDirectory();
      if (outputFile == null) {
        return;
      }
      savePNGFile(selectedItems, outputFile, firstItem.batchPrefix);
      break;
    case 2:
      outputFile = getSaveDirectory();
      if (outputFile == null) {
        return;
      }
      saveIndividualPDFFile(selectedItems, outputFile, firstItem.batchPrefix);
      break;
    case 3:
    default:
      outputFile = getSaveFile(firstItem.batchPrefix, "pdf");
      if (outputFile == null) {
        return;
      }
      saveMultiPagePDFFile(
          selectedItems,
          parseOutputFileName(outputFile, Localizer.localize("ALLPagesText"), firstItem.pagesToScan));
      break;
    }

  }

  public final void setPreferences(ISwingSanePreferences preferences) {
    this.preferences = preferences;
  }

  public void setPreferredDefaults(IPreferredDefaults preferredDefaultsImpl) {
    preferredDefaults = preferredDefaultsImpl;
  }

  private void setSourceImage(BufferedImage sourceImage) {
    if (sourceImage != null) {
      sourceImage.flush();
    }
    this.sourceImage = sourceImage;
    convertedImage = null;
    resizeImageLabel();
    repaint();
  }

  private void showCropErrorMessage(Exception e) {
    Misc.showErrorMsg(getRootPane().getTopLevelAncestor(),
        String.format(Localizer.localize("FailureCroppingMessage"), e.getLocalizedMessage()));
  }

  private void showSaveErrorMessage(Exception e) {
    Misc.showErrorMsg(getRootPane().getTopLevelAncestor(),
        String.format(Localizer.localize("FailureSavingMessage"), e.getLocalizedMessage()));
  }

  private void showSaveSuccessMessage() {
    Misc.showSuccessMsg(getRootPane().getTopLevelAncestor(),
        Localizer.localize("SaveSuccessMessage"));
  }

  private void showTransformErrorMessage(Exception e) {
    Misc.showErrorMsg(getRootPane().getTopLevelAncestor(),
        String.format(Localizer.localize("FailureTransformingMessage"), e.getLocalizedMessage()));
  }

  private void tempFileListSelectionChanged(ListSelectionEvent e) {
    if ((e != null) && !(e.getValueIsAdjusting())) {
      return;
    }
    final TempFileListItem tempFileListItem = tempFileList.getSelectedValue();
    if (tempFileListItem == null) {
      setSourceImage(null);
      return;
    }
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        try {
          BufferedImage bufferedImage = ImageIO.read(tempFileListItem.tempFile);
          setSourceImage(bufferedImage);
        } catch (IOException ex) {
          LOG.error(ex, ex);
        }
      }
    });
  }

  private void transformActionPerformed(ActionEvent e) {

    final int[] indicies = tempFileList.getSelectedIndices();
    if (indicies.length <= 0) {
      String message = Localizer.localize("RequireImagesToTransformSelectionMessage");
      JOptionPane.showMessageDialog(getRootPane().getTopLevelAncestor(), message);
      return;
    }

    ImageTransformDialog imageTransformDialog = new ImageTransformDialog(getRootPane()
        .getTopLevelAncestor());
    imageTransformDialog.setModal(true);
    imageTransformDialog.setVisible(true);
    if (imageTransformDialog.getDialogResult() == JOptionPane.OK_OPTION) {

      final ArrayList<IImageTransform> transforms = imageTransformDialog.getTransforms();
      for (int index : indicies) {
        TempFileListItem tempFileListItem = tempFileList.getModel().getElementAt(index);

        for (final IImageTransform transform : transforms) {
          transform.setSourceImageFile(tempFileListItem.tempFile);
          transform.setOutputImageFile(tempFileListItem.tempFile);
          try {
            transform.configure(preferredDefaults);
          } catch (Exception ex) {
            LOG.error(ex, ex);
            showTransformErrorMessage(ex);
            return;
          }
        }
      }

      final INotification notification = new DialogNotificationImpl(getRootPane()
          .getTopLevelAncestor());

      SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {

        @Override
        protected Void doInBackground() throws Exception {
          notification.message(Localizer.localize("TransformImageMessageText"));
          for (int index : indicies) {
            TempFileListItem tempFileListItem = tempFileList.getModel().getElementAt(index);
            for (final IImageTransform transform : transforms) {
              transform.setSourceImageFile(tempFileListItem.tempFile);
              transform.setOutputImageFile(tempFileListItem.tempFile);
              transform.transform();
            }
          }
          return null;
        }

        @Override
        protected void done() {
          try {
            get();
            ((JDialog) notification).setVisible(false);
            tempFileListSelectionChanged(null);
          } catch (Exception ex) {
            LOG.error(ex, ex);
          } finally {
            ((JDialog) notification).dispose();
          }
        }
      };

      worker.execute();

      ((JDialog) notification).setModal(true);
      ((JDialog) notification).setVisible(true);

    }

  }

  private void zoomBoxItemStateChanged(ItemEvent e) {
    String unparsedString = (String) zoomBox.getSelectedObjects()[0];
    String zoomString = "";
    for (int i = 0; i < unparsedString.length(); i++) {
      if ((unparsedString.charAt(i) >= 48) && (unparsedString.charAt(i) <= 57)) {
        zoomString += unparsedString.charAt(i);
      }
    }
    lastZoom = realZoom;
    realZoom = Float.parseFloat(zoomString) / 100.0f;
    resizeImageLabel();
    repaint();
  }

  private void zoomIn() {
    int selectedIndex = zoomBox.getSelectedIndex();

    if (selectedIndex < (zoomBox.getItemCount() - 1)) {
      zoomBox.setSelectedIndex(selectedIndex + 1);
    }
  }

  private void zoomInLabelMouseClicked(MouseEvent e) {
    zoomIn();
  }

  private void zoomOut() {
    int selectedIndex = zoomBox.getSelectedIndex();

    if (selectedIndex > 0) {
      zoomBox.setSelectedIndex(selectedIndex - 1);
    }
  }

  private void zoomOutLabelMouseClicked(MouseEvent e) {
    zoomOut();
  }

}