package net.sf.jabref.gui;

import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;

import net.sf.jabref.Globals;
import net.sf.jabref.gui.keyboard.KeyBinding;
import net.sf.jabref.gui.undo.NamedCompound;
import net.sf.jabref.gui.undo.UndoableFieldChange;
import net.sf.jabref.logic.l10n.Localization;
import net.sf.jabref.model.entry.BibEntry;

/**
 * Dialog for replacing strings.
 */
class ReplaceStringDialog extends JDialog {

    private final JTextField fieldsField = new JTextField("", 30);
    private final JTextField fromField = new JTextField("", 30);
    private final JTextField toField = new JTextField("", 30);

    private final JCheckBox selOnly = new JCheckBox(Localization.lang("Limit to selected entries"), false);
    private final JRadioButton allFi = new JRadioButton(Localization.lang("All fields"), true);
    private final JRadioButton field = new JRadioButton(Localization.lang("Limit to fields") + ":", false);
    private boolean okPressed;
    private String[] fieldStrings;
    private String fromString;
    private String toString;


    public ReplaceStringDialog(JabRefFrame parent) {
        super(parent, Localization.lang("Replace string"), true);

        ButtonGroup bg = new ButtonGroup();
        bg.add(allFi);
        bg.add(field);
        ActionListener okListener = e -> {
            fromString = fromField.getText();
            toString = toField.getText();
            if ("".equals(fromString)) {
                return;
            }
            okPressed = true;
            fieldStrings = fieldsField.getText().toLowerCase().split(";");
            dispose();
        };
        JButton ok = new JButton(Localization.lang("OK"));
        ok.addActionListener(okListener);
        toField.addActionListener(okListener);
        fieldsField.addActionListener(okListener);
        AbstractAction cancelAction = new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent e) {
                dispose();
            }
        };
        JButton cancel = new JButton(Localization.lang("Cancel"));
        cancel.addActionListener(cancelAction);

        // Key bindings:
        JPanel settings = new JPanel();
        ActionMap am = settings.getActionMap();
        InputMap im = settings.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close");
        am.put("close", cancelAction);

        // Layout starts here.
        GridBagLayout gbl = new GridBagLayout();
        settings.setLayout(gbl);
        JPanel opt = new JPanel();
        opt.setLayout(gbl);
        JPanel main = new JPanel();
        main.setLayout(gbl);

        settings.setBorder(BorderFactory.createTitledBorder
                (BorderFactory.createEtchedBorder(),
                        Localization.lang("Replace string")));
        main.setBorder(BorderFactory.createTitledBorder
                (BorderFactory.createEtchedBorder(),
                        Localization.lang("Strings")));

        // Settings panel:
        GridBagConstraints con = new GridBagConstraints();
        con.fill = GridBagConstraints.HORIZONTAL;
        con.gridwidth = 2;
        con.weightx = 0;
        con.anchor = GridBagConstraints.WEST;
        con.gridy = 0;
        con.gridx = 0;
        con.insets = new Insets(3, 5, 3, 5);
        gbl.setConstraints(selOnly, con);
        settings.add(selOnly);
        con.gridy = 1;
        con.insets = new Insets(13, 5, 3, 5);
        gbl.setConstraints(allFi, con);
        settings.add(allFi);
        con.gridwidth = 1;
        con.gridy = 2;
        con.gridx = 0;
        con.insets = new Insets(3, 5, 3, 5);
        gbl.setConstraints(field, con);
        settings.add(field);
        con.gridx = 1;
        con.weightx = 1;
        gbl.setConstraints(fieldsField, con);
        settings.add(fieldsField);

        con.weightx = 0;
        con.gridx = 0;
        con.gridy = 0;
        JLabel fl = new JLabel(Localization.lang("Search for") + ":");
        gbl.setConstraints(fl, con);
        main.add(fl);
        con.gridy = 1;
        JLabel tl = new JLabel(Localization.lang("Replace with") + ":");
        gbl.setConstraints(tl, con);
        main.add(tl);
        con.weightx = 1;
        con.gridx = 1;
        con.gridy = 0;
        gbl.setConstraints(fromField, con);
        main.add(fromField);
        con.gridy = 1;
        gbl.setConstraints(toField, con);
        main.add(toField);

        // Option buttons:
        con.gridx = GridBagConstraints.RELATIVE;
        con.gridy = GridBagConstraints.RELATIVE;
        con.weightx = 1;
        con.gridwidth = 1;
        con.anchor = GridBagConstraints.EAST;
        con.fill = GridBagConstraints.NONE;
        gbl.setConstraints(ok, con);
        opt.add(ok);
        con.anchor = GridBagConstraints.WEST;
        con.gridwidth = GridBagConstraints.REMAINDER;
        gbl.setConstraints(cancel, con);
        opt.add(cancel);

        getContentPane().add(main, BorderLayout.NORTH);
        getContentPane().add(settings, BorderLayout.CENTER);
        getContentPane().add(opt, BorderLayout.SOUTH);

        pack();

        this.setLocationRelativeTo(parent);
    }

    public boolean okPressed() {
        return okPressed;
    }

    private boolean allFields() {
        return allFi.isSelected();
    }

    public boolean selOnly() {
        return selOnly.isSelected();
    }

    /**
     * Does the actual operation on a Bibtex entry based on the
     * settings specified in this same dialog. Returns the number of
     * occurences replaced.
     */
    public int replace(BibEntry be, NamedCompound ce) {
        int counter = 0;
        if (allFields()) {

            for (String s : be.getFieldNames()) {
                if (!s.equals(BibEntry.KEY_FIELD)) {
                    counter += replaceField(be, s, ce);
                }
            }
        } else {
            for (String fld : fieldStrings) {
                if (!fld.equals(BibEntry.KEY_FIELD)) {
                    counter += replaceField(be, fld, ce);
                }
            }

        }
        return counter;
    }

    private int replaceField(BibEntry be, String fieldname, NamedCompound ce) {
        if (!be.hasField(fieldname)) {
            return 0;
        }
        String txt = be.getField(fieldname).get();
        StringBuilder sb = new StringBuilder();
        int ind;
        int piv = 0;
        int counter = 0;
        int len1 = fromString.length();
        while ((ind = txt.indexOf(fromString, piv)) >= 0) {
            counter++;
            sb.append(txt.substring(piv, ind)); // Text leading up to s1
            sb.append(toString); // Insert s2
            piv = ind + len1;
        }
        sb.append(txt.substring(piv));
        String newStr = sb.toString();
        be.setField(fieldname, newStr);
        ce.addEdit(new UndoableFieldChange(be, fieldname, txt, newStr));
        return counter;
    }
}
