/*
 * Copyright (c) 2002-2007 TeamDev Ltd. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * The complete licence text can be found at
 * http://www.teamdev.com/winpack/license.jsf
 */
package teamdev.jxcapture.samples.demo;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;

/**
 * @author Ikryanov Vladimir
 */
public class ImageViewer extends JFrame {
    private static final String ZOOM_IN_PROPERTY = "zoomInEnabled";
    private static final String ZOOM_OUT_PROPERTY = "zoomOutEnabled";

    private ResourceBundle resource = ApplicationSettings.getResourceBundle();

    private OpenAction openAction = new OpenAction();
    private SaveAction saveAction = new SaveAction();
    private CloseAction closeAction = new CloseAction();
    private ZoomInAction zoomInAction = new ZoomInAction();
    private ZoomOutAction zoomOutAction = new ZoomOutAction();
    private ZoomToAction zoomToAction = new ZoomToAction();
    private ActualSizeAction actualSizeAction = new ActualSizeAction();
    private FitToWindowAction fitToWindowAction = new FitToWindowAction();
    private CopyAction copyAction = new CopyAction();
    private SettingsAction settingsAction = new SettingsAction();
    private AboutAction aboutAction = new AboutAction();

    private ImageComponent imageComponent;
    private JButton zoomToButton;

    private boolean zoomInEnabled = true;
    private boolean zoomOutEnabled = true;
    private boolean imageModified = false;

    private List percentActions = new LinkedList();
    private JPopupMenu zoomToPopupMenu = new JPopupMenu();

    public ImageViewer() throws HeadlessException {
        setSize(750, 500);
        setLocationRelativeTo(null);
        setTitle(resource.getString("ImageViewer.Title"));
        URL imageURL = getClass().getResource("resources/images/JxCapture.png");
        setIconImage(new ImageIcon(imageURL).getImage());

        createMenuBar();
        createContentPane();

        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        addPropertyChangeListener(new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                String propertyName = evt.getPropertyName();
                if (propertyName.equals(ZOOM_IN_PROPERTY)) {
                    zoomInAction.setEnabled(((Boolean)evt.getNewValue()).booleanValue());
                } else if (propertyName.equals(ZOOM_OUT_PROPERTY)) {
                    zoomOutAction.setEnabled(((Boolean)evt.getNewValue()).booleanValue());
                }
            }
        });
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                close();
            }
        });
    }

    private void createMenuBar() {
        JMenuItem openMenuItem = new JMenuItem(openAction);
        JMenuItem saveMenuItem = new JMenuItem(saveAction);
        JMenuItem closeMenuItem = new JMenuItem(closeAction);
        JMenuItem zoomInMenuItem = new JMenuItem(zoomInAction);
        JMenuItem zoomOutMenuItem = new JMenuItem(zoomOutAction);
        JMenuItem actualSizeMenuItem = new JMenuItem(actualSizeAction);
        JMenuItem fitToWindowMenuItem = new JMenuItem(fitToWindowAction);
        JMenuItem copyMenuItem = new JMenuItem(copyAction);
        JMenuItem settingsMenuItem = new JMenuItem(settingsAction);
        JMenuItem aboutMenuItem = new JMenuItem(aboutAction);

        JMenu zoomToMenuItem = new JMenu(resource.getString("ImageViewer.Action.ZoomTo.Text"));
        configZoomToMenu(zoomToMenuItem);

        openMenuItem.setIcon(getIcon("Open", "Menu"));
        saveMenuItem.setIcon(getIcon("Save", "Menu"));
        closeMenuItem.setIcon(getIcon("Close", "Menu"));
        zoomInMenuItem.setIcon(getIcon("ZoomIn", "Menu"));
        zoomOutMenuItem.setIcon(getIcon("ZoomOut", "Menu"));
        zoomToMenuItem.setIcon(getIcon("ZoomTo", "Menu"));
        copyMenuItem.setIcon(getIcon("Copy", "Menu"));
        settingsMenuItem.setIcon(getIcon("Settings", "Menu"));

        JMenu fileMenu = new JMenu(resource.getString("ImageViewer.Menu.FileMenu.Text"));
        JMenu viewMenu = new JMenu(resource.getString("ImageViewer.Menu.ViewMenu.Text"));
        JMenu editMenu = new JMenu(resource.getString("ImageViewer.Menu.EditMenu.Text"));
        JMenu settingsMenu = new JMenu(resource.getString("ImageViewer.Menu.SettingsMenu.Text"));
        JMenu helpMenu = new JMenu(resource.getString("ImageViewer.Menu.HelpMenu.Text"));

        fileMenu.add(openMenuItem);
        fileMenu.add(saveMenuItem);
        fileMenu.addSeparator();
        fileMenu.add(closeMenuItem);

        viewMenu.add(zoomInMenuItem);
        viewMenu.add(zoomOutMenuItem);
        viewMenu.add(zoomToMenuItem);
        viewMenu.addSeparator();
        viewMenu.add(actualSizeMenuItem);
        viewMenu.add(fitToWindowMenuItem);

        editMenu.add(copyMenuItem);

        settingsMenu.add(settingsMenuItem);

        helpMenu.add(aboutMenuItem);

        JMenuBar menuBar = new JMenuBar();
        menuBar.add(fileMenu);
        menuBar.add(viewMenu);
        menuBar.add(editMenu);
        menuBar.add(settingsMenu);
        menuBar.add(helpMenu);
        setJMenuBar(menuBar);
    }

    private Icon getIcon(String actionName, String contentType) {
        String imageName = resource.getString("ImageViewer.Action." + actionName + "." + contentType + ".Icon");
        URL imageURL = ImageViewer.class.getResource(imageName);
        return new ImageIcon(imageURL);
    }

    private JToolBar createToolBar() {
        JToolBar toolBar = new JToolBar();
        toolBar.setFloatable(false);
        toolBar.setBorderPainted(false);
        toolBar.add(createToolBarButton(openAction, "Open"));
        toolBar.add(createToolBarButton(saveAction, "Save"));
        toolBar.addSeparator();
        toolBar.add(createToolBarButton(copyAction, "Copy"));
        toolBar.addSeparator();
        toolBar.add(createToolBarButton(zoomInAction, "ZoomIn"));
        toolBar.add(createToolBarButton(zoomOutAction, "ZoomOut"));
        toolBar.add(createZoomToButton());
        toolBar.addSeparator();
        toolBar.add(createToolBarButton(settingsAction, "Settings"));
        return toolBar;
    }

    private JButton createToolBarButton(AbstractAction action, String actionName) {
        JButton button = new JButton();
        button.setBorder(new EmptyBorder(5, 5, 5, 5));
        button.setVerticalTextPosition(JButton.BOTTOM);
        button.setHorizontalTextPosition(JButton.CENTER);
        button.setAction(action);
        button.setIcon(getIcon(actionName, "Button"));
        button.setFocusable(false);
        return button;
    }

    private ImageComponent getImageComponent() {
        return imageComponent == null ? imageComponent = new ImageComponent() : imageComponent;
    }

    private JButton createZoomToButton() {
        zoomToButton = createToolBarButton(zoomToAction, "ZoomTo");

        zoomToPopupMenu.add(new JMenuItem(actualSizeAction));
        zoomToPopupMenu.add(new JMenuItem(fitToWindowAction));
        zoomToPopupMenu.addSeparator();

        List zoomToActions = getZoomToActions();
        for (int i = 0; i < zoomToActions.size(); i++) {
            PercentAction action = (PercentAction) zoomToActions.get(i);
            zoomToPopupMenu.add(new JMenuItem(action));
        }

        zoomToPopupMenu.addPopupMenuListener(new PopupMenuListener() {
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                zoomToButton.setSelected(true);
            }

            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
                zoomToButton.setSelected(false);
            }

            public void popupMenuCanceled(PopupMenuEvent e) {
            }
        });

        return zoomToButton;
    }

    private void configZoomToMenu(JMenu menu) {
        java.util.List zoomToActions = getZoomToActions();
        for (int i = 0; i < zoomToActions.size(); i++) {
            PercentAction action = (PercentAction) zoomToActions.get(i);
            menu.add(new JMenuItem(action));
        }
    }

    private java.util.List getZoomToActions() {
        if (percentActions == null || percentActions.isEmpty()) {
            int[] percents = getImageComponent().getPercents();
            for (int i = 0; i < percents.length; i++) {
                percentActions.add(new PercentAction(percents[i], i));
            }
        }
        return percentActions;
    }

    private void createContentPane() {
        JPanel contentPane = new JPanel(new BorderLayout());
        contentPane.add(createToolBar(), BorderLayout.NORTH);
        contentPane.add(new JScrollPane(getImageComponent()), BorderLayout.CENTER);
        setContentPane(contentPane);
    }

    private void updateZoomActionState() {
        setZoomInEnabled(getImageComponent().canZoomIn());
        setZoomOutEnabled(getImageComponent().canZoomOut());
    }

    private boolean isImageModified() {
        return imageModified;
    }

    private void setImageModified(boolean imageModified) {
        this.imageModified = imageModified;
    }

    private boolean confirmSaveImage() {
        String title = resource.getString("ImageViewer.Confirm.SaveImageDialog.Title");
        String message = resource.getString("ImageViewer.Confirm.SaveImageDialog.Message");
        int selectedValue = JOptionPane.showConfirmDialog(this, message, title, JOptionPane.YES_NO_CANCEL_OPTION);
        switch (selectedValue) {
            case JOptionPane.YES_OPTION:
                return save();
            case JOptionPane.NO_OPTION:
                return true;
            default:
                return false;
        }
    }

    public void setImage(BufferedImage image) {
        getImageComponent().setImage(image);
        setImageModified(true);
    }

    public boolean setImage(File imageFile) {
        try {
            getImageComponent().setImage(imageFile);
        } catch (Exception e) {
            String title = resource.getString("ImageViewer.Error.OpenImageDialog.Title");
            String message = resource.getString("ImageViewer.Error.OpenImageDialog.Message");
            JOptionPane.showMessageDialog(this, message, title, JOptionPane.ERROR_MESSAGE);
            return false;
        }

        setImageModified(false);
        return true;
    }

    public void setZoomInEnabled(boolean zoomInEnabled) {
        boolean oldValue = this.zoomInEnabled;
        this.zoomInEnabled = zoomInEnabled;
        firePropertyChange(ZOOM_IN_PROPERTY, oldValue, this.zoomInEnabled);
    }

    public void setZoomOutEnabled(boolean zoomOutEnabled) {
        boolean oldValue = this.zoomOutEnabled;
        this.zoomOutEnabled = zoomOutEnabled;
        firePropertyChange(ZOOM_OUT_PROPERTY, oldValue, this.zoomOutEnabled);
    }

    public boolean close() {
        if (isImageModified() && !confirmSaveImage())
            return false;

        getImageComponent().dispose();
        setVisible(false);
        dispose();

        return true;
    }

    public boolean save() {
        ImageFileChooser fileChooser = new ImageFileChooser(this);
        File imageFile = fileChooser.saveImageFile();
        if (imageFile != null) {
            try {
                getImageComponent().saveImage(imageFile);
                setImageModified(false);
                return true;
            } catch (Exception e) {
                String title = resource.getString("ImageViewer.Error.SaveImageDialog.Title");
                String message = resource.getString("ImageViewer.Error.SaveImageDialog.Message");
                JOptionPane.showMessageDialog(this, message, title, JOptionPane.ERROR_MESSAGE);
                return false;
            }
        }
        return false;
    }

    private class AboutAction extends FullAction {
        private AboutAction() {
            super("About");
        }

        public void actionPerformed(ActionEvent e) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    AboutDialog aboutDialog = AboutDialog.getInstance();
                    aboutDialog.setLocationRelativeTo(ImageViewer.this);
                    aboutDialog.setModal(true);
                    aboutDialog.setVisible(true);
                    aboutDialog.toFront();
                }
            });
        }
    }

    private class SettingsAction extends FullAction {
        private SettingsAction() {
            super("Settings");
        }

        public void actionPerformed(ActionEvent e) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    SettingsDialog settingsDialog = SettingsDialog.getInstance();
                    settingsDialog.setLocationRelativeTo(ImageViewer.this);
                    settingsDialog.setModal(true);
                    settingsDialog.setVisible(true);
                    settingsDialog.toFront();
                }
            });
        }
    }

    private class CloseAction extends FullAction {
        private CloseAction() {
            super("Close");
        }

        public void actionPerformed(ActionEvent e) {
            close();
        }
    }

    private class OpenAction extends FullAction {
        private OpenAction() {
            super("Open");
        }

        public void actionPerformed(ActionEvent e) {
            if (isImageModified() && !confirmSaveImage()) return;

            ImageFileChooser fileChooser = new ImageFileChooser(ImageViewer.this);
            File imageFile = fileChooser.openImageFile();
            if (imageFile != null) {
                setImage(imageFile);
            }
        }
    }

    private class SaveAction extends FullAction {
        private SaveAction() {
            super("Save");
        }

        public void actionPerformed(ActionEvent e) {
            save();
        }
    }

    private class CopyAction extends FullAction {
        private CopyAction() {
            super("Copy");
        }

        public void actionPerformed(ActionEvent e) {
            CaptureOperations.getInstance().copyToClipboard(getImageComponent().getImage());
        }
    }

    private class ZoomInAction extends FullAction {
        private ZoomInAction() {
            super("ZoomIn");
        }

        public void actionPerformed(ActionEvent e) {
            ImageComponent component = getImageComponent();
            if (component.canZoomIn()) component.zoomIn();
            updateZoomActionState();
        }
    }

    private class ZoomOutAction extends FullAction {
        private ZoomOutAction() {
            super("ZoomOut");
        }

        public void actionPerformed(ActionEvent e) {
            ImageComponent component = getImageComponent();
            if (component.canZoomOut()) component.zoomOut();
            updateZoomActionState();
        }
    }

    private class ZoomToAction extends SimpleAction {
        private ZoomToAction() {
            super("ZoomTo");
        }

        public void actionPerformed(ActionEvent e) {
            zoomToPopupMenu.show(zoomToButton, 0, zoomToButton.getBounds().height);
        }
    }

    private class ActualSizeAction extends FullAction {
        private ActualSizeAction() {
            super("ActualSize");
        }

        public void actionPerformed(ActionEvent e) {
            getImageComponent().actualSize();
            updateZoomActionState();
        }
    }

    private class FitToWindowAction extends FullAction {
        private FitToWindowAction() {
            super("FitToWindow");
        }

        public void actionPerformed(ActionEvent e) {
            ImageComponent component = getImageComponent();
            Dimension parentContainerSize = component.getParent().getSize();
            component.fitToWindow(parentContainerSize);
        }
    }

    private class PercentAction extends AbstractAction {
        private int index = 0;

        public PercentAction(int percent, int index) {
            this.index = index;
            putValue(javax.swing.Action.NAME, percent + " %");
            putValue(javax.swing.Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(String.valueOf(index + 1)));
        }

        public void actionPerformed(ActionEvent e) {
            getImageComponent().zoomTo(index);
            updateZoomActionState();
        }
    }

    private abstract class SimpleAction extends AbstractAction {
        protected SimpleAction(String actionName) {
            String description = resource.getString("ImageViewer.Action." + actionName + ".ShortDescription");
            putValue(Action.SHORT_DESCRIPTION, description);
            putValue(Action.NAME, resource.getString("ImageViewer.Action." + actionName + ".Text"));
        }
    }

    private abstract class FullAction extends SimpleAction {
        protected FullAction(String actionName) {
            super(actionName);
            String accelerator = resource.getString("ImageViewer.Action." + actionName + ".Accelerator");
            putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(accelerator));
        }
    }
}