/* Copyright (C) 2005-2011 Fabio Riccardi */

package com.lightcrafts.ui.export;

import com.lightcrafts.image.export.ImageExportOptions;
import com.lightcrafts.image.export.IntegerExportOption;
import com.lightcrafts.image.export.ImageFileExportOptions;
import com.lightcrafts.ui.LightZoneSkin;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;
import java.awt.event.*;
import java.awt.*;

/**
 * A pair of editable text fields that validate input as positive integers
 * and update each other to preserve their ratio.
 */

class ExportSizeFields {

    private JTextField xText;
    private JTextField yText;

    private double ratio;

    // Prevent update loops:
    private boolean isXUpdating;
    private boolean isYUpdating;

    ExportSizeFields(ImageExportOptions options) {
        final ImageFileExportOptions fileOptions =
            (ImageFileExportOptions)options;

        IntegerExportOption widthOption = fileOptions.resizeWidth;
        IntegerExportOption heightOption = fileOptions.resizeHeight;

        int x = widthOption.getValue();
        int y = heightOption.getValue();

        xText = createTextField(x);
        yText = createTextField(y);

        ratio = x / (double) y;

        // Push valid text updates into the export options:
        new ExportUpdater(widthOption, xText);
        new ExportUpdater(heightOption, yText);
    }

    JTextField getXText() {
        return xText;
    }

    JTextField getYText() {
        return yText;
    }

    private static JTextField createTextField(int value) {
        JTextField text = new JTextField(4);
        text.setText(Integer.toString(value));
        text.setInputVerifier(Verifier);
        text.addFocusListener(FocusSelector);
        text.setHorizontalAlignment(SwingConstants.RIGHT);
        text.registerKeyboardAction(
            UpAction,
            "Increment",
            KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
            JComponent.WHEN_FOCUSED
        );
        text.registerKeyboardAction(
            DownAction,
            "Decrement",
            KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
            JComponent.WHEN_FOCUSED
        );
        return text;
    }

    private void xUpdated() {
        if (! isYUpdating) {
            isXUpdating = true;
            int x = Integer.parseInt(xText.getText());
            int y = (int) Math.round(x / ratio);
            yText.setText(Integer.toString(y));
            isXUpdating = false;
        }
    }

    private void yUpdated() {
        if (! isXUpdating) {
            isYUpdating = true;
            int y = Integer.parseInt(yText.getText());
            int x = (int) Math.round(y * ratio);
            xText.setText(Integer.toString(x));
            isYUpdating = false;
        }
    }

    // Select-all when one of the text fields gains focus:

    private final static FocusListener FocusSelector = new FocusAdapter() {
        public void focusGained(FocusEvent event) {
            JTextField text = (JTextField) event.getSource();
            text.selectAll();
        }
    };

    // Increment whenever the up arrow is pressed:

    private final static ActionListener UpAction = new ActionListener() {
        public void actionPerformed(ActionEvent event) {
            final JTextField source = (JTextField) event.getSource();
            InputVerifier verifier = source.getInputVerifier();
            if (! verifier.verify(source)) {
                return;
            }
            final int number = Integer.parseInt(source.getText());
            EventQueue.invokeLater(
                new Runnable() {
                    public void run() {
                        source.setText(Integer.toString(number + 1));
                    }
                }
            );
        }
    };

    // Decrement whenever the down arrow is pressed:

    private final static ActionListener DownAction = new ActionListener() {
        public void actionPerformed(ActionEvent event) {
            final JTextField source = (JTextField) event.getSource();
            InputVerifier verifier = source.getInputVerifier();
            if (! verifier.verify(source)) {
                return;
            }
            final int number = Integer.parseInt(source.getText());
            if (number > 1) {
                EventQueue.invokeLater(
                    new Runnable() {
                        public void run() {
                            source.setText(Integer.toString(number - 1));
                        }
                    }
                );
            }
        }
    };

    // Verify positive integer text:

    private final static InputVerifier Verifier = new InputVerifier() {
        public boolean verify(JComponent input) {
            String text = ((JTextField) input).getText();
            double x;
            try {
                x = Integer.parseInt(text);
            }
            catch (NumberFormatException e) {
                return false;
            }
            return x > 0;
        }
    };

    // Repsond to valid text changes with color changes and updates to the
    // ImageExportOptions object:

    private final class ExportUpdater implements DocumentListener {

        private IntegerExportOption option;
        private JTextField text;

        ExportUpdater(IntegerExportOption option, JTextField text) {
            this.option = option;
            this.text = text;
            Document doc = text.getDocument();
            doc.addDocumentListener(this);
        }
        public void changedUpdate(DocumentEvent e) {
            handleDocumentChange();
        }
        public void insertUpdate(DocumentEvent e) {
            handleDocumentChange();
        }
        public void removeUpdate(DocumentEvent e) {
            handleDocumentChange();
        }
        private void handleDocumentChange() {
            InputVerifier verifier = text.getInputVerifier();
            boolean verified = verifier.verify(text);
            if (! verified) {
                text.setForeground(Color.red);
            }
            else {
                text.setForeground(LightZoneSkin.Colors.ToolPanesForeground);
                int value = Integer.parseInt(text.getText());
                option.setValue(value);

                // Trigger the text field synchronizations:

                if (text == xText) {
                    xUpdated();
                }
                if (text == yText) {
                    yUpdated();
                }
            }
        }
    }
}
