package CH.ifa.draw.standard;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

import CH.ifa.draw.DrawPlugin;
import CH.ifa.draw.application.DrawApplication;
import CH.ifa.draw.application.MenuManager;
import CH.ifa.draw.figures.TextFigure;
import CH.ifa.draw.util.GUIProperties;
import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.FigureEnumeration;

/**
 * Constructs a SearchFrame or SearchAndReplaceFrame
 * according to the selected mode and displays it.
 * <br>
 * @author Lawrence Cabac, Marcin Hewelt (changes 2010-2012)
 * @deprecated This class is not to be used externally and will later be hidden.
 */
@Deprecated(since = "5.0", forRemoval = true)
@SuppressWarnings("removal")
public class SearchReplaceFrame {
    public static final int SEARCHMODE = 0;
    public static final int SEARCHREPLACEMODE = 1;

    // private DrawApplication application;
    private Enumeration<Drawing> _drawings;
    private Set<Drawing> _drawingsSet;
    private FigureEnumeration _figures;
    private Drawing _drawing;
    private final JFrame _frame;
    private boolean _stringMatcherValid = false;
    private SubstringMatcher _substringMatcher;
    private final int _mode;
    private boolean _cancelSearchInAllDrawings = true;
    private boolean _searchAll = true;
    private boolean _ignoreCase = true;
    private boolean _newSearch = true;

    public SearchReplaceFrame(int aMode) {
        String frameTitle;
        String buttonTitle;

        _mode = aMode;

        if (_mode == SEARCHMODE) {
            frameTitle = "Search";
        } else {
            frameTitle = "Search & Replace";
        }

        _frame = new JFrame(frameTitle);

        if (!GUIProperties.avoidFrameReshape()) {
            _frame.setSize(600, 200);
        }
        GridBagLayout gridBag = new GridBagLayout();
        _frame.getContentPane().setLayout(gridBag);

        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1;

        JLabel label = new JLabel("Search for:");
        gridBag.setConstraints(label, c);
        _frame.getContentPane().add(label);

        c.gridwidth = GridBagConstraints.REMAINDER; //end row
        final JTextField searchTextField = new JTextField("", 30);
        gridBag.setConstraints(searchTextField, c);
        searchTextField.addKeyListener(new KeyAdapter() {
            @Override
            public void keyTyped(KeyEvent e) {
                reset(); //search string is modified: reset search process
            }
        });
        _frame.getContentPane().add(searchTextField);

        final JTextField replaceTextField = new JTextField("", 30);
        if (_mode == SEARCHREPLACEMODE) {
            c.gridwidth = 1;
            label = new JLabel("Replace with:");
            gridBag.setConstraints(label, c);
            _frame.getContentPane().add(label);

            c.gridwidth = GridBagConstraints.REMAINDER; //end row
            gridBag.setConstraints(replaceTextField, c);
            replaceTextField.addKeyListener(new KeyAdapter() {
                @Override
                public void keyTyped(KeyEvent e) {
                    _stringMatcherValid = false; //replace string is modified
                }
            });
            _frame.getContentPane().add(replaceTextField);
        }

        final JCheckBox searchAllCheckBox = new JCheckBox("Search all drawings", _searchAll);
        _frame.getContentPane().add(searchAllCheckBox);
        searchAllCheckBox.addChangeListener(e -> {
            _searchAll = !_searchAll;
            reset();
        });
        c.gridwidth = GridBagConstraints.EAST;
        gridBag.setConstraints(searchAllCheckBox, c);

        final JCheckBox ignoreCaseCheckBox = new JCheckBox("Ignore case", _ignoreCase);
        _frame.getContentPane().add(ignoreCaseCheckBox);
        ignoreCaseCheckBox.addChangeListener(e -> _ignoreCase = !_ignoreCase);
        c.gridwidth = GridBagConstraints.REMAINDER; //end row
        gridBag.setConstraints(ignoreCaseCheckBox, c);

        c.weightx = 1;

        if (_mode == SEARCHMODE) {
            buttonTitle = "Search";
        } else {
            buttonTitle = "Search & Replace";
        }
        JButton searchButton = new JButton(buttonTitle);
        gridBag.setConstraints(searchButton, c);
        _frame.getContentPane().add(searchButton);
        _frame.getRootPane().setDefaultButton(searchButton);
        searchButton.addActionListener(e -> {
            if ("".equals(searchTextField.getText())) {
                Toolkit.getDefaultToolkit().beep();
                return;
            }
            if (!_stringMatcherValid) {
                if (_mode == SEARCHMODE) {
                    _substringMatcher = new SubstringMatcher(searchTextField.getText(), null);
                } else {
                    _substringMatcher =
                        new SubstringMatcher(searchTextField.getText(), replaceTextField.getText());
                }
                _stringMatcherValid = true;
            }
            searchInDrawings();
        });

        c.gridwidth = GridBagConstraints.REMAINDER; //end row
        JButton cancelButton = new JButton("Cancel");
        gridBag.setConstraints(cancelButton, c);
        _frame.getContentPane().add(cancelButton);
        cancelButton.addActionListener(e -> cancel());

        _frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent w) {
                _frame.setVisible(false);
                MenuManager.getInstance().getWindowsMenu().removeFrame(_frame);
            }
        });

        _frame.pack();

        reset();

    }

    /**
     * Handles a found TextFigure wrt. the actual mode. In case of a
     * SEARCHREPLACEMODE this is done by prompting the user with the
     * replacement.
     */
    private void handleFoundFigure(TextFigure foundFigure) {
        if (_mode == SEARCHREPLACEMODE) {
            String foundText = foundFigure.getText();
            String replacement;
            int nextFromIndex = 0;
            int index;
            int answer;
            while (_substringMatcher.matches(foundText, nextFromIndex, _ignoreCase)) {
                replacement = _substringMatcher.replacement(foundText, nextFromIndex);
                index = _substringMatcher.indexOf(foundText, nextFromIndex);
                answer = JOptionPane.showConfirmDialog(
                    _frame, "Replace:\n" + foundText + "\nwith:\n" + replacement,
                    "Confirm Replacement", JOptionPane.YES_NO_CANCEL_OPTION);

                switch (answer) {
                    case JOptionPane.CANCEL_OPTION:
                        reset();
                        _cancelSearchInAllDrawings = true;
                        setVisible(false);
                        return;
                    case JOptionPane.YES_OPTION:
                        DrawApplication application = DrawPlugin.getGui();
                        application.prepareUndoSnapshot();
                        foundFigure.setText(replacement);
                        foundText = replacement;
                        nextFromIndex = index + _substringMatcher.getReplaceString().length();
                        application.commitUndoSnapshot();
                        application.view().checkDamage();
                        break;
                    case JOptionPane.NO_OPTION:
                        nextFromIndex = index + _substringMatcher.getSearchString().length();
                        break;
                }
            }
        }
    }

    /**
     * Searches for a matching string in @see CH.ifa.draw.figures.TextFigure s
     *   and calls @see handleFoundFigure(TextFigure t) for each match.
     *
     * @author  Michael Koehler, Heiko Roelke
     *
     *
     */
    private void searchInDrawings() {
        if (_mode == SEARCHREPLACEMODE) {
            reset(); //init search and replace
        } else if (!_drawing.equals(DrawPlugin.getGui().drawing()) && !_searchAll) { // if the current drawing changed the search has to be reset.
            reset();
        }
        if (_mode == SEARCHMODE && _searchAll) {
            Set<Drawing> currentDrawingsSet = new HashSet<>();
            Enumeration<Drawing> currentDrawings = DrawPlugin.getGui().drawings();
            while (currentDrawings.hasMoreElements()) {
                currentDrawingsSet.add(currentDrawings.nextElement());
            }
            if (!currentDrawingsSet.equals(_drawingsSet)) {
                // reset if a drawing is closed or opened
                reset();
            }
        }

        TextFigure foundFigure = null;
        this._cancelSearchInAllDrawings = false;
        while (!_cancelSearchInAllDrawings && (_drawings.hasMoreElements() || _drawing != null)) {
            if (_newSearch) { //start a new search in first/next drawing
                _newSearch = false;
                if (_drawings.hasMoreElements()) {
                    _drawing = _drawings.nextElement();
                    _figures = _drawing.figures();
                } else {
                    Toolkit.getDefaultToolkit().beep();
                    return;
                }
            }

            foundFigure = searchInOneDrawing();
            if (foundFigure != null) {
                DrawApplication application = DrawPlugin.getGui();

                if (_mode == SEARCHMODE) {
                    FigureException e = new FigureException(
                        "Heureka", 1, _substringMatcher.getLastPosition() + 1, _drawing,
                        foundFigure);
                    application.selectOffendingElements(e);
                    return;
                } else {
                    FigureException e = new FigureException("Heureka", "", _drawing, foundFigure);
                    application.selectOffendingElements(e);
                    handleFoundFigure(foundFigure);
                }
            } else {
                // no more figures in this drawing, next one.
                _newSearch = true;
                _drawing = null;
            }
        }
        if (!_cancelSearchInAllDrawings && !_figures.hasMoreElements() & foundFigure == null) {
            Toolkit.getDefaultToolkit().beep();
            // optional: reset at this point provides automatic wrap of search
            reset();
        }
    }

    /**
     * Warning figures is a Enumeration field. Each call continues with the
     * search instead of starting a new one.
     * @return next found matching figure in search context
     */
    private TextFigure searchInOneDrawing() {
        while (_figures.hasMoreElements()) {
            Figure figure = _figures.nextFigure();
            if (figure instanceof TextFigure textFigure) {
                String figureText = textFigure.getText();
                if (_substringMatcher.matches(figureText, _ignoreCase)) {
                    return textFigure; //Heureka!
                }
            }
        }
        return null;
    }

    /**
     * Resets the search or search and replace context to start a new search.
     * Either for all drawing or for the active drawing in GUI depending on
     * checkbox "Search all drawings" settings.
     */
    protected void reset() {
        DrawApplication application = DrawPlugin.getGui();
        if (application != null) {
            application.tool().deactivate();
            _drawing = application.drawing();

            if (_searchAll) {
                // search in all open drawings
                _drawings = application.drawings();
                _drawingsSet = new HashSet<>();
                while (_drawings.hasMoreElements()) {
                    _drawingsSet.add(_drawings.nextElement());
                }
                _drawings = application.drawings();
            } else {
                // search in the active drawing
                Vector<Drawing> list = new Vector<>();
                list.add(_drawing);
                _drawings = list.elements();
                _drawingsSet = new HashSet<>(list);
            }

            _figures = _drawing.figures();
            _newSearch = true;
            _stringMatcherValid = false;
        }
    }

    private void cancel() {
        this.setVisible(false);
        DrawPlugin.getGui().toolDone();
    }

    protected void setVisible(boolean b) {
        _frame.setVisible(b);
        if (b) {
            MenuManager.getInstance().getWindowsMenu()
                .addFrame(DrawPlugin.WINDOWS_CATEGORY_TOOLS, _frame);
        } else {
            MenuManager.getInstance().getWindowsMenu().removeFrame(_frame);
        }
    }
}