/*
 * Steganography utility to hide messages into cover files
 * Author: Samir Vaidya (mailto:[email protected])
 * Copyright (c) Samir Vaidya
 */

package com.openstego.desktop.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.KeyEvent;
import java.util.Enumeration;
import java.util.Locale;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.metal.MetalToggleButtonUI;

import com.openstego.desktop.OpenStego;
import com.openstego.desktop.util.LabelUtil;

/**
 * Frame class to build the Swing UI for OpenStego. This class includes only graphics rendering
 * code. Listeners are implemented in {@link com.openstego.desktop.ui.OpenStegoUI} class.
 */
public class OpenStegoFrame extends JFrame {
    private static final long serialVersionUID = -880718904125121559L;
    private static final boolean toggleUiHack = false;

    /**
     * LabelUtil instance to retrieve labels
     */
    private static LabelUtil labelUtil = LabelUtil.getInstance(OpenStego.NAMESPACE);

    /**
     * Number of columns for text fields
     */
    public static final int TEXTFIELD_SIZE = 30;
    /**
     * Number of columns for password fields
     */
    public static final int PWD_FIELD_SIZE = 15;

    private JMenuBar topMenuBar;
    private JMenu fileMenu;
    private JMenuItem fileExitMenuItem;
    private JMenu helpMenu;
    private JMenuItem helpAboutMenuItem;

    private JPanel mainContentPane;

    private JScrollPane accordionPane;
    private JPanel accordion;
    private ButtonGroup actionButtonGroup = new ButtonGroup();
    private JToggleButton embedButton;
    private JToggleButton extractButton;
    private JToggleButton genSigButton;
    private JToggleButton signWmButton;
    private JToggleButton verifyWmButton;

    private JPanel headerPanel;
    private JLabel header;

    private JPanel mainPanel;
    private EmbedPanel embedPanel;
    private ExtractPanel extractPanel;
    private GenerateSignaturePanel genSigPanel;
    private EmbedWatermarkPanel embedWmPanel;
    private VerifyWatermarkPanel verifyWmPanel;

    /**
     * Default constructor
     */
    public OpenStegoFrame() {
        super();
        initialize();
        setActionCommands();
        setupUI();
    }

    /**
     * Getter method for topMenuBar
     *
     * @return topMenuBar
     */
    public JMenuBar getTopMenuBar() {
        if (this.topMenuBar == null) {
            this.topMenuBar = new JMenuBar();
            this.topMenuBar.add(getFileMenu());
            this.topMenuBar.add(getHelpMenu());
        }
        return this.topMenuBar;
    }

    /**
     * Getter method for fileMenu
     *
     * @return fileMenu
     */
    public JMenu getFileMenu() {
        if (this.fileMenu == null) {
            this.fileMenu = new JMenu(labelUtil.getString("gui.menu.file"));
            this.fileMenu.setMnemonic(KeyEvent.VK_F);
            this.fileMenu.add(getFileExitMenuItem());
        }
        return this.fileMenu;
    }

    /**
     * Getter method for fileExitMenuItem
     *
     * @return fileExitMenuItem
     */
    public JMenuItem getFileExitMenuItem() {
        if (this.fileExitMenuItem == null) {
            this.fileExitMenuItem = new JMenuItem(labelUtil.getString("gui.menu.file.exit"));
            this.fileExitMenuItem.setMnemonic(KeyEvent.VK_X);
        }
        return this.fileExitMenuItem;
    }

    /**
     * Getter method for helpMenu
     *
     * @return helpMenu
     */
    public JMenu getHelpMenu() {
        if (this.helpMenu == null) {
            this.helpMenu = new JMenu(labelUtil.getString("gui.menu.help"));
            this.helpMenu.setMnemonic(KeyEvent.VK_H);
            this.helpMenu.add(getHelpAboutMenuItem());
        }
        return this.helpMenu;
    }

    /**
     * Getter method for helpAboutMenuItem
     *
     * @return helpAboutMenuItem
     */
    public JMenuItem getHelpAboutMenuItem() {
        if (this.helpAboutMenuItem == null) {
            this.helpAboutMenuItem = new JMenuItem(labelUtil.getString("gui.menu.help.about"));
            this.helpAboutMenuItem.setMnemonic(KeyEvent.VK_A);
        }
        return this.helpAboutMenuItem;
    }

    /**
     * Getter method for mainContentPane
     *
     * @return mainContentPane
     */
    public JPanel getMainContentPane() {
        if (this.mainContentPane == null) {
            this.mainContentPane = new JPanel();
            this.mainContentPane.setLayout(new BorderLayout());

            this.mainContentPane.add(getAccordionPane(), BorderLayout.LINE_START);

            JPanel rightPane = new JPanel();
            rightPane.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.DARK_GRAY));
            rightPane.setLayout(new BorderLayout());
            this.mainContentPane.add(rightPane, BorderLayout.CENTER);

            rightPane.add(getHeaderPanel(), BorderLayout.PAGE_START);
            rightPane.add(getMainPanel(), BorderLayout.CENTER);
        }
        return this.mainContentPane;
    }

    /**
     * Getter method for accordionPane
     *
     * @return accordionPane
     */
    public JScrollPane getAccordionPane() {
        if (this.accordionPane == null) {
            this.accordionPane = new JScrollPane();
            this.accordionPane.setBorder(null);
            this.accordionPane.setViewportView(getAccordion());
        }
        return this.accordionPane;
    }

    /**
     * Getter method for accordion
     *
     * @return accordion
     */
    public JPanel getAccordion() {
        if (this.accordion == null) {
            this.accordion = new JPanel();
            this.accordion.setLayout(new GridBagLayout());

            int pad = 20;
            int gridy = 0;
            GridBagConstraints c = new GridBagConstraints();
            c.fill = GridBagConstraints.HORIZONTAL;

            c.gridy = gridy++;
            this.accordion.add(createAccordionHeader(labelUtil.getString("gui.label.tabHeader.dataHiding")), c);

            c.gridy = gridy++;
            c.insets = new Insets(0, pad, 0, pad);
            this.accordion.add(getEmbedButton(), c);

            c.gridy = gridy++;
            this.accordion.add(getExtractButton(), c);

            c.gridy = gridy++;
            this.accordion.add(Box.createVerticalStrut(20), c);

            c.gridy = gridy++;
            c.insets = new Insets(0, 0, 0, 0);
            this.accordion.add(createAccordionHeader(labelUtil.getString("gui.label.tabHeader.watermarking")), c);

            c.gridy = gridy++;
            c.insets = new Insets(0, pad, 0, pad);
            this.accordion.add(getGenSigButton(), c);

            c.gridy = gridy++;
            this.accordion.add(getSignWmButton(), c);

            c.gridy = gridy++;
            this.accordion.add(getVerifyWmButton(), c);

            c.gridy = gridy++;
            c.weighty = 1.0;
            this.accordion.add(new JPanel(), c);
        }
        return this.accordion;
    }

    private Component createAccordionHeader(String name) {
        GradientPanel panel = new GradientPanel((new JPanel()).getBackground(), (new JPanel()).getBackground().darker());
        panel.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.DARK_GRAY));
        panel.setLayout(new GridLayout(1, 1));

        JButton button = new JButton(name);
        button.setContentAreaFilled(false);
        button.setOpaque(false);
        button.setMargin(new Insets(3, 3, 3, 3));
        button.setFont(button.getFont().deriveFont(Font.BOLD));
        button.setFocusable(false);
        panel.add(button);

        return panel;
    }

    /**
     * Getter method for embedButton
     *
     * @return embedButton
     */
    public JToggleButton getEmbedButton() {
        if (this.embedButton == null) {
            this.embedButton = new JToggleButton(labelUtil.getString("gui.label.tab.dhEmbed"),
                    new ImageIcon(getClass().getResource("/image/EmbedIcon.png")), true);
            if (toggleUiHack) {
                this.embedButton.setUI(new MetalToggleButtonUI());
            }
            this.embedButton.setVerticalTextPosition(SwingConstants.BOTTOM);
            this.embedButton.setHorizontalTextPosition(SwingConstants.CENTER);
            this.embedButton.setFocusable(false);
            this.actionButtonGroup.add(this.embedButton);
        }
        return this.embedButton;
    }

    /**
     * Getter method for extractButton
     *
     * @return extractButton
     */
    public JToggleButton getExtractButton() {
        if (this.extractButton == null) {
            this.extractButton = new JToggleButton(labelUtil.getString("gui.label.tab.dhExtract"),
                    new ImageIcon(getClass().getResource("/image/ExtractIcon.png")));
            if (toggleUiHack) {
                this.extractButton.setUI(new MetalToggleButtonUI());
            }
            this.extractButton.setVerticalTextPosition(SwingConstants.BOTTOM);
            this.extractButton.setHorizontalTextPosition(SwingConstants.CENTER);
            this.extractButton.setFocusable(false);
            this.actionButtonGroup.add(this.extractButton);
        }
        return this.extractButton;
    }

    /**
     * Getter method for genSigButton
     *
     * @return genSigButton
     */
    public JToggleButton getGenSigButton() {
        if (this.genSigButton == null) {
            this.genSigButton = new JToggleButton(labelUtil.getString("gui.label.tab.wmGenSig"),
                    new ImageIcon(getClass().getResource("/image/EmbedIcon.png"))); // TODO
            if (toggleUiHack) {
                this.genSigButton.setUI(new MetalToggleButtonUI());
            }
            this.genSigButton.setVerticalTextPosition(SwingConstants.BOTTOM);
            this.genSigButton.setHorizontalTextPosition(SwingConstants.CENTER);
            this.genSigButton.setFocusable(false);
            this.actionButtonGroup.add(this.genSigButton);
        }
        return this.genSigButton;
    }

    /**
     * Getter method for signWmButton
     *
     * @return signWmButton
     */
    public JToggleButton getSignWmButton() {
        if (this.signWmButton == null) {
            this.signWmButton = new JToggleButton(labelUtil.getString("gui.label.tab.wmEmbed"),
                    new ImageIcon(getClass().getResource("/image/EmbedIcon.png")));
            if (toggleUiHack) {
                this.signWmButton.setUI(new MetalToggleButtonUI());
            }
            this.signWmButton.setVerticalTextPosition(SwingConstants.BOTTOM);
            this.signWmButton.setHorizontalTextPosition(SwingConstants.CENTER);
            this.signWmButton.setFocusable(false);
            this.actionButtonGroup.add(this.signWmButton);
        }
        return this.signWmButton;
    }

    /**
     * Getter method for verifyWmButton
     *
     * @return verifyWmButton
     */
    public JToggleButton getVerifyWmButton() {
        if (this.verifyWmButton == null) {
            this.verifyWmButton = new JToggleButton(labelUtil.getString("gui.label.tab.wmVerify"),
                    new ImageIcon(getClass().getResource("/image/ExtractIcon.png")));
            if (toggleUiHack) {
                this.verifyWmButton.setUI(new MetalToggleButtonUI());
            }
            this.verifyWmButton.setVerticalTextPosition(SwingConstants.BOTTOM);
            this.verifyWmButton.setHorizontalTextPosition(SwingConstants.CENTER);
            this.verifyWmButton.setFocusable(false);
            this.actionButtonGroup.add(this.verifyWmButton);
        }
        return this.verifyWmButton;
    }

    /**
     * Getter method for headerPanel
     *
     * @return headerPanel
     */
    public JPanel getHeaderPanel() {
        if (this.headerPanel == null) {
            this.headerPanel = new JPanel();
            this.headerPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.DARK_GRAY),
                BorderFactory.createEmptyBorder(5, 5, 5, 5)));
            this.headerPanel.setLayout(new GridLayout());
            this.headerPanel.add(getHeader());
        }
        return this.headerPanel;
    }

    /**
     * Getter method for header
     *
     * @return header
     */
    public JLabel getHeader() {
        if (this.header == null) {
            this.header = new JLabel();
            this.header.setFont(this.header.getFont().deriveFont(Font.BOLD, this.header.getFont().getSize2D() + 3f));
        }
        return this.header;
    }

    /**
     * Getter method for mainPanel
     *
     * @return mainPanel
     */
    public JPanel getMainPanel() {
        if (this.mainPanel == null) {
            this.mainPanel = new JPanel();
            this.mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
            this.mainPanel.setLayout(new GridLayout());
        }
        return this.mainPanel;
    }

    /**
     * Getter method for embedPanel
     *
     * @return embedPanel
     */
    public EmbedPanel getEmbedPanel() {
        if (this.embedPanel == null) {
            this.embedPanel = new EmbedPanel();
        }
        return this.embedPanel;
    }

    /**
     * Getter method for extractPanel
     *
     * @return extractPanel
     */
    public ExtractPanel getExtractPanel() {
        if (this.extractPanel == null) {
            this.extractPanel = new ExtractPanel();
        }
        return this.extractPanel;
    }

    /**
     * Getter method for genSigPanel
     *
     * @return genSigPanel
     */
    public GenerateSignaturePanel getGenSigPanel() {
        if (this.genSigPanel == null) {
            this.genSigPanel = new GenerateSignaturePanel();
        }
        return this.genSigPanel;
    }

    /**
     * Getter method for embedWmPanel
     *
     * @return embedWmPanel
     */
    public EmbedWatermarkPanel getEmbedWmPanel() {
        if (this.embedWmPanel == null) {
            this.embedWmPanel = new EmbedWatermarkPanel();
        }
        return this.embedWmPanel;
    }

    /**
     * Getter method for verifyWmPanel
     *
     * @return verifyWmPanel
     */
    public VerifyWatermarkPanel getVerifyWmPanel() {
        if (this.verifyWmPanel == null) {
            this.verifyWmPanel = new VerifyWatermarkPanel();
        }
        return this.verifyWmPanel;
    }

    /**
     * This method initializes the UI resources like fonts, size, etc.
     */
    private void setupUI() {
        // Special handling to ensure that Japanese fonts are readable
        if (Locale.getDefault().getLanguage().equals(Locale.JAPANESE.getLanguage())) {
            Object key = null;
            Object value = null;
            Enumeration<?> keys = UIManager.getDefaults().keys();
            while (keys.hasMoreElements()) {
                key = keys.nextElement();
                value = UIManager.get(key);
                if (value instanceof FontUIResource) {
                    UIManager.put(key, ((FontUIResource) value).deriveFont(12.0f));
                }
            }
            getMainContentPane().setFont(new Font("Japanese", Font.PLAIN, 12));
        }
    }

    /**
     * This method is called from within the constructor to
     * initialize the form.
     */
    private void initialize() {
        if (toggleUiHack) {
            UIManager.put("ToggleButton.select", new MetalLookAndFeel().getDefaults().getColor("ToggleButton.select").darker());
        }
        this.setContentPane(getMainContentPane());
        this.setTitle(labelUtil.getString("gui.window.title"));
        this.setJMenuBar(getTopMenuBar());

        getMainPanel().add(getEmbedPanel());
        getHeader().setText(labelUtil.getString("gui.label.panelHeader.dhEmbed"));
    }

    /**
     * Method to set the action commands for interactive UI items
     */
    private void setActionCommands() {
        getFileExitMenuItem().setActionCommand(ActionCommands.MENU_FILE_EXIT);
        getHelpAboutMenuItem().setActionCommand(ActionCommands.MENU_HELP_ABOUT);

        getEmbedButton().setActionCommand(ActionCommands.SWITCH_DH_EMBED);
        getExtractButton().setActionCommand(ActionCommands.SWITCH_DH_EXTRACT);
        getGenSigButton().setActionCommand(ActionCommands.SWITCH_WM_GENSIG);
        getSignWmButton().setActionCommand(ActionCommands.SWITCH_WM_EMBED);
        getVerifyWmButton().setActionCommand(ActionCommands.SWITCH_WM_VERIFY);

        getEmbedPanel().getMsgFileButton().setActionCommand(ActionCommands.BROWSE_DH_EMB_MSGFILE);
        getEmbedPanel().getCoverFileButton().setActionCommand(ActionCommands.BROWSE_DH_EMB_CVRFILE);
        getEmbedPanel().getStegoFileButton().setActionCommand(ActionCommands.BROWSE_DH_EMB_STGFILE);
        getEmbedPanel().getRunEmbedButton().setActionCommand(ActionCommands.RUN_DH_EMBED);

        getExtractPanel().getInputStegoFileButton().setActionCommand(ActionCommands.BROWSE_DH_EXT_STGFILE);
        getExtractPanel().getOutputFolderButton().setActionCommand(ActionCommands.BROWSE_DH_EXT_OUTDIR);
        getExtractPanel().getRunExtractButton().setActionCommand(ActionCommands.RUN_DH_EXTRACT);

        getGenSigPanel().getSignatureFileButton().setActionCommand(ActionCommands.BROWSE_WM_GSG_SIGFILE);
        getGenSigPanel().getRunGenSigButton().setActionCommand(ActionCommands.RUN_WM_GENSIG);

        getEmbedWmPanel().getFileForWmButton().setActionCommand(ActionCommands.BROWSE_WM_EMB_INPFILE);
        getEmbedWmPanel().getSignatureFileButton().setActionCommand(ActionCommands.BROWSE_WM_EMB_SIGFILE);
        getEmbedWmPanel().getOutputWmFileButton().setActionCommand(ActionCommands.BROWSE_WM_EMB_OUTFILE);
        getEmbedWmPanel().getRunEmbedWmButton().setActionCommand(ActionCommands.RUN_WM_EMBED);

        getVerifyWmPanel().getInputFileButton().setActionCommand(ActionCommands.BROWSE_WM_VER_INPFILE);
        getVerifyWmPanel().getSignatureFileButton().setActionCommand(ActionCommands.BROWSE_WM_VER_SIGFILE);
        getVerifyWmPanel().getRunVerifyWmButton().setActionCommand(ActionCommands.RUN_WM_VERIFY);
    }

    /**
     * Enumeration for button actions
     */
    public interface ActionCommands {
        /**
         * Menu - File - Exit
         */
        public static String MENU_FILE_EXIT = "MENU_FILE_EXIT";
        /**
         * Menu - Help - About
         */
        public static String MENU_HELP_ABOUT = "MENU_HELP_ABOUT";

        /**
         * Switch to Data Hiding - Embed panel
         */
        public static String SWITCH_DH_EMBED = "SWITCH_DH_EMBED";
        /**
         * Switch to Data Hiding - Extract panel
         */
        public static String SWITCH_DH_EXTRACT = "SWITCH_DH_EXTRACT";
        /**
         * Switch to Watermarking - GenSig panel
         */
        public static String SWITCH_WM_GENSIG = "SWITCH_WM_GENSIG";
        /**
         * Switch to Watermarking - Embed panel
         */
        public static String SWITCH_WM_EMBED = "SWITCH_WM_EMBED";
        /**
         * Switch to Watermarking - Verify panel
         */
        public static String SWITCH_WM_VERIFY = "SWITCH_WM_VERIFY";

        /**
         * Browse action for DH-Embed-MessageFile
         */
        public static String BROWSE_DH_EMB_MSGFILE = "BROWSE_DH_EMB_MSGFILE";
        /**
         * Browse action for DH-Embed-CoverFile
         */
        public static String BROWSE_DH_EMB_CVRFILE = "BROWSE_DH_EMB_CVRFILE";
        /**
         * Browse action for DH-Embed-StegoFile
         */
        public static String BROWSE_DH_EMB_STGFILE = "BROWSE_DH_EMB_STGFILE";
        /**
         * Execute DH-Embed
         */
        public static String RUN_DH_EMBED = "RUN_DH_EMBED";

        /**
         * Browse action for DH-Extract-StegoFile
         */
        public static String BROWSE_DH_EXT_STGFILE = "BROWSE_DH_EXT_STGFILE";
        /**
         * Browse action for DH-Extract-OutputFolder
         */
        public static String BROWSE_DH_EXT_OUTDIR = "BROWSE_DH_EXT_OUTDIR";
        /**
         * Execute DH-Extract
         */
        public static String RUN_DH_EXTRACT = "RUN_DH_EXTRACT";

        /**
         * Browse action for WM-GenSig-SigFile
         */
        public static String BROWSE_WM_GSG_SIGFILE = "BROWSE_WM_GSG_SIGFILE";
        /**
         * Execute WM-GenSig
         */
        public static String RUN_WM_GENSIG = "RUN_WM_GENSIG";

        /**
         * Browse action for WM-Embed-InputFile
         */
        public static String BROWSE_WM_EMB_INPFILE = "BROWSE_WM_EMB_INPFILE";
        /**
         * Browse action for WM-Embed-SignatureFile
         */
        public static String BROWSE_WM_EMB_SIGFILE = "BROWSE_WM_EMB_SIGFILE";
        /**
         * Browse action for WM-Embed-OutputFile
         */
        public static String BROWSE_WM_EMB_OUTFILE = "BROWSE_WM_EMB_OUTFILE";
        /**
         * Execute WM-Embed
         */
        public static String RUN_WM_EMBED = "RUN_WM_EMBED";

        /**
         * Browse action for WM-Verify-InputFile
         */
        public static String BROWSE_WM_VER_INPFILE = "BROWSE_WM_VER_INPFILE";
        /**
         * Browse action for WM-Verify-SignatureFile
         */
        public static String BROWSE_WM_VER_SIGFILE = "BROWSE_WM_VER_SIGFILE";
        /**
         * Execute WM-Verify
         */
        public static String RUN_WM_VERIFY = "RUN_WM_VERIFY";
    }

    class GradientPanel extends JPanel {
        private static final long serialVersionUID = 3865918400221647086L;
        private Color startColor;
        private Color endColor;

        /**
         * Default constructor
         *
         * @param startColor
         * @param endColor
         */
        public GradientPanel(Color startColor, Color endColor) {
            this.startColor = startColor;
            this.endColor = endColor;
        }

        /*
         * (non-Javadoc)
         * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
         */
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            int panelHeight = getHeight();
            int panelWidth = getWidth();

            GradientPaint gradientPaint = new GradientPaint(0, 0, this.startColor, 0, panelHeight, this.endColor);
            if (g instanceof Graphics2D) {
                Graphics2D graphics2D = (Graphics2D) g;
                graphics2D.setPaint(gradientPaint);
                graphics2D.fillRect(0, 0, panelWidth, panelHeight);
            }
        }
    }
}