package CH.ifa.draw.standard;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JCheckBox;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionListener;

import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.FigureEnumeration;
import de.renew.draw.ui.ontology.DrawingView;


/**
 * Command to change a font.
 * <p>
 * The command's effects are undoable step by step.
 * Each use of the apply button can be undone separately.
 * So this command doesn't need to inherit UndoableCommand.
 * </p>
 * @author Sven Offermann
 * @deprecated This class is not to be used externally and will later be hidden.
 */
@Deprecated(since = "5.0", forRemoval = true)
@SuppressWarnings("removal")
public class ChooseFontCommand extends ChooseAttributeCommand {
    private JList<String> _fontList;
    private JList<Object> _sizeList;
    private JCheckBox _cbBold;
    private JCheckBox _cbItalic;
    private JTextArea _txtSample;
    private static final int[] SIZES =
        new int[] { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 30, 36, 48, 72 };
    private SizeListModel _sizeModel;

    /**
     * Constructs a dialog with a FontChooser to
     * change the font of inscriptions etc.
     *
     * @param displayName the dialog name
     * @param name the command name
     * @param attributeName the name of the attribute to be changed
     * @param type the attribute type (class)
     */
    public ChooseFontCommand(String displayName, String name, String attributeName, Class<?> type) {
        super(displayName, name, attributeName, type);
    }

    @Override
    protected void specializeDialog() {
        // create all components
        _fontList = new JList<>(
            GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames())
        {
            @Override
            public Dimension getPreferredScrollableViewportSize() {
                return new Dimension(150, 144);
            }
        };
        _fontList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        _sizeModel = new SizeListModel(SIZES);
        _sizeList = new JList<>(_sizeModel) {
            @Override
            public Dimension getPreferredScrollableViewportSize() {
                return new Dimension(25, 144);
            }
        };
        _sizeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        _cbBold = new JCheckBox("Bold");

        _cbItalic = new JCheckBox("Italic");

        _txtSample = new JTextArea() {
            @Override
            public Dimension getPreferredScrollableViewportSize() {
                return new Dimension(385, 80);
            }
        };
        _txtSample.setText("This is a sample text.");

        // set the default font
        setFont(null);

        // add the listeners
        ListSelectionListener listListener = e -> _txtSample.setFont(getCurrentFont());

        _fontList.addListSelectionListener(listListener);
        _sizeList.addListSelectionListener(listListener);


        ActionListener cbListener = e -> _txtSample.setFont(getCurrentFont());

        _cbBold.addActionListener(cbListener);
        _cbItalic.addActionListener(cbListener);

        JPanel fontChoosePanel = new JPanel();
        fontChoosePanel.setLayout(new BorderLayout());

        JPanel leftPanel = new JPanel();
        leftPanel.setLayout(new java.awt.BorderLayout());

        leftPanel.add(new JScrollPane(_fontList), java.awt.BorderLayout.CENTER);
        leftPanel.add(new JScrollPane(_sizeList), java.awt.BorderLayout.EAST);

        fontChoosePanel.add(leftPanel, java.awt.BorderLayout.CENTER);


        JPanel rightPanel = new JPanel();
        rightPanel.setLayout(new java.awt.FlowLayout());

        rightPanel.add(_cbBold);
        rightPanel.add(_cbItalic);

        fontChoosePanel.add(rightPanel, java.awt.BorderLayout.EAST);

        fontChoosePanel.add(new JScrollPane(_txtSample), java.awt.BorderLayout.SOUTH);

        dialog.getContentPane().add(fontChoosePanel, BorderLayout.CENTER);

        dialog.setSize(200, 200);
        dialog.setResizable(true);

        dialog.pack();
    }

    private void setFont(Font font) {
        if (font == null) {
            font = _txtSample.getFont();
        }

        _fontList.setSelectedValue(font.getName(), true);
        _fontList.ensureIndexIsVisible(_fontList.getSelectedIndex());
        Integer sizeInt = font.getSize();
        _sizeList.setSelectedValue(sizeInt, true);
        if (!sizeInt.equals(_sizeList.getSelectedValue())) {
            // The current font size is missing in the list.
            // Let's add it and select it again.
            _sizeModel.addSize(sizeInt);
            _sizeList.setSelectedValue(sizeInt, true);
        }
        _sizeList.ensureIndexIsVisible(_sizeList.getSelectedIndex());

        _cbBold.setSelected(font.isBold());
        _cbItalic.setSelected(font.isItalic());

        _txtSample.setFont(font);
    }

    private Font getCurrentFont() {
        String fontFamily = _fontList.getSelectedValue();
        int fontSize = (Integer) _sizeList.getSelectedValue();

        int fontType = Font.PLAIN;

        if (_cbBold.isSelected()) {
            fontType += Font.BOLD;
        }
        if (_cbItalic.isSelected()) {
            fontType += Font.ITALIC;
        }

        return new Font(fontFamily, fontType, fontSize);
    }

    @Override
    protected void updateFromFigure() {
        DrawingView view = getEditor().view();
        Font font = null;
        FigureEnumeration k = view.selectionElements();
        while ((k.hasMoreElements()) && (font == null)) {
            Figure f = k.nextFigure();
            Object name = f.getAttribute("FontName");
            Object style = f.getAttribute("FontStyle");
            Object size = f.getAttribute("FontSize");

            if ((name != null) && (style != null) && (size != null)) {
                font = new Font((String) name, (Integer) style, (Integer) size);
            }
        }

        setFont(font);
    }

    @Override
    protected void apply() {
        String fontFamily = _fontList.getSelectedValue();
        Integer fontSize = (Integer) _sizeList.getSelectedValue();

        int fontType = Font.PLAIN;
        if (_cbBold.isSelected()) {
            fontType += Font.BOLD;
        }
        if (_cbItalic.isSelected()) {
            fontType += Font.ITALIC;
        }

        // set the new font attributes of the selected figures
        // TODO: Do the attribute changes in a new ChangeAttributeCommand,
        //       which changes the three font attributes in one step, so
        //       the undo function works better.
        new ChangeAttributeCommand("", "FontName", fontFamily).execute();
        new ChangeAttributeCommand("", "FontSize", fontSize).execute();
        new ChangeAttributeCommand("", "FontStyle", fontType).execute();
    }

    /**
     * Maintains a sorted list of font sizes to be used as data model
     * for the JList within the choose font dialog.  Starting with a
     * base list of sizes, more sizes can be added when needed.
     * <br>
     * Created: 25 Feb 2009
     * @author Michael Duvigneau
     **/
    private static class SizeListModel implements ListModel<Object> {
        private final List<Integer> _sizeList;
        private final List<ListDataListener> _listeners;

        /**
         * Creates a new font size list data model filled with the
         * given array of default font sizes.
         * @param sizes  an array of default font size values.
         *               May not be null.
         **/
        public SizeListModel(final int[] sizes) {
            _sizeList = new ArrayList<>(sizes.length * 2);
            for (int size : sizes) {
                _sizeList.add(size);
            }
            Collections.sort(_sizeList);
            _listeners = new ArrayList<>();
        }

        @Override
        public Object getElementAt(final int index) {
            return _sizeList.get(index);
        }

        @Override
        public int getSize() {
            return _sizeList.size();
        }

        @Override
        public void addListDataListener(final ListDataListener listener) {
            _listeners.add(listener);
        }

        @Override
        public void removeListDataListener(final ListDataListener listener) {
            _listeners.remove(listener);
        }

        /**
         * Inserts the given font size into the list, unless it is already
         * included.  The list remains sorted.
         *
         * @param size  the font size to include in the list.  May not be null.
         **/
        public void addSize(final Integer size) {
            assert (size != null);
            assert (_sizeList != null);
            final int index = Collections.binarySearch(_sizeList, size);
            if (index <= 0) {
                final int indexToInsert = -index - 1;
                _sizeList.add(indexToInsert, size);
                final ListDataEvent event = new ListDataEvent(
                    this, ListDataEvent.INTERVAL_ADDED, indexToInsert, indexToInsert);
                for (ListDataListener listener : _listeners) {
                    listener.intervalAdded(event);
                }
            }
            assert (_sizeList.contains(size));
        }
    }
}