/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jface.preference;

import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter;

import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.StringConverter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.FontDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

/**
 * A field editor for a font type preference.
 */
public class FontFieldEditor extends FieldEditor {

    /**
     * The change font button, or <code>null</code> if none
     * (before creation and after disposal).
     */
    private Button changeFontButton = null;

    /**
     * The text for the change font button, or <code>null</code>
     * if missing.
     */
    private String changeButtonText;

    /**
     * The text for the preview, or <code>null</code> if no preview is desired
     */
    private String previewText;

    /**
     * Font data for the chosen font button, or <code>null</code> if none.
     */
    private FontData[] chosenFont;

    /**
     * The label that displays the selected font, or <code>null</code> if none.
     */
    private Label valueControl;

    /**
     * The previewer, or <code>null</code> if none.
     */
    private DefaultPreviewer previewer;

    /**
     * Internal font previewer implementation.
     */
    private static class DefaultPreviewer {
        private Text text;

        private String string;

        private Font font;

        /**
         * Constructor for the previewer.
         * @param s
         * @param parent
         */
        public DefaultPreviewer(String s, Composite parent) {
            string = s;
            text = new Text(parent, SWT.READ_ONLY | SWT.BORDER);
            text.addDisposeListener(e -> {
			    if (font != null) {
					font.dispose();
				}
			});
            if (string != null) {
				text.setText(string);
			}
        }

        /**
         * @return the control the previewer is using
         */
        public Control getControl() {
            return text;
        }

        /**
         * Set the font to display with
         * @param fontData
         */
        public void setFont(FontData[] fontData) {
            if (font != null) {
				font.dispose();
			}
            font = new Font(text.getDisplay(), fontData);
            text.setFont(font);
        }

        /**
         * @return the preferred size of the previewer.
         */
        public int getPreferredExtent() {
            return 40;
        }
    }

    /**
     * Creates a new font field editor
     */
    protected FontFieldEditor() {
    }

    /**
     * Creates a font field editor with an optional preview area.
     *
     * @param name the name of the preference this field editor works on
     * @param labelText the label text of the field editor
     * @param previewAreaText the text used for the preview window. If it is
     * <code>null</code> there will be no preview area,
     * @param parent the parent of the field editor's control
     */
    public FontFieldEditor(String name, String labelText,
            String previewAreaText, Composite parent) {
        init(name, labelText);
        previewText = previewAreaText;
        changeButtonText = JFaceResources.getString("openChange"); //$NON-NLS-1$
        createControl(parent);

    }

    /**
     * Creates a font field editor without a preview.
     *
     * @param name the name of the preference this field editor works on
     * @param labelText the label text of the field editor
     * @param parent the parent of the field editor's control
     */
    public FontFieldEditor(String name, String labelText, Composite parent) {
        this(name, labelText, null, parent);

    }

    @Override
	protected void adjustForNumColumns(int numColumns) {

        GridData data = new GridData();
        if (valueControl.getLayoutData() != null) {
			data = (GridData) valueControl.getLayoutData();
		}

        data.horizontalSpan = numColumns - getNumberOfControls() + 1;
        valueControl.setLayoutData(data);
    }

    @Override
	protected void applyFont() {
        if (chosenFont != null && previewer != null) {
			previewer.setFont(chosenFont);
		}
    }

    @Override
	protected void doFillIntoGrid(Composite parent, int numColumns) {
        getLabelControl(parent);

        valueControl = getValueControl(parent);

        GridData gd = new GridData(GridData.FILL_HORIZONTAL
                | GridData.GRAB_HORIZONTAL);
        gd.horizontalSpan = numColumns - getNumberOfControls() + 1;
        valueControl.setLayoutData(gd);
        if (previewText != null) {
            previewer = new DefaultPreviewer(previewText, parent);
            gd = new GridData(GridData.FILL_HORIZONTAL);
            gd.heightHint = previewer.getPreferredExtent();
            gd.widthHint = previewer.getPreferredExtent();
            previewer.getControl().setLayoutData(gd);
        }

        changeFontButton = getChangeControl(parent);
        gd = new GridData();
        int widthHint = convertHorizontalDLUsToPixels(changeFontButton,
                IDialogConstants.BUTTON_WIDTH);
        gd.widthHint = Math.max(widthHint, changeFontButton.computeSize(
                SWT.DEFAULT, SWT.DEFAULT, true).x);
        changeFontButton.setLayoutData(gd);

    }

    @Override
	protected void doLoad() {
        if (changeFontButton == null) {
			return;
		}
        updateFont(PreferenceConverter.getFontDataArray(getPreferenceStore(),
                getPreferenceName()));
    }

    @Override
	protected void doLoadDefault() {
        if (changeFontButton == null) {
			return;
		}
        updateFont(PreferenceConverter.getDefaultFontDataArray(
                getPreferenceStore(), getPreferenceName()));
    }

    @Override
	protected void doStore() {
        if (chosenFont != null) {
			PreferenceConverter.setValue(getPreferenceStore(),
                    getPreferenceName(), chosenFont);
		}
    }

    /**
     * Returns the change button for this field editor.
     *
     * @param parent The Composite to create the button in if required.
     * @return the change button
     */
    protected Button getChangeControl(Composite parent) {
        if (changeFontButton == null) {
            changeFontButton = new Button(parent, SWT.PUSH);
            if (changeButtonText != null) {
				changeFontButton.setText(changeButtonText);
			}
            changeFontButton.addSelectionListener(widgetSelectedAdapter(event -> {
				FontDialog fontDialog = new FontDialog(changeFontButton.getShell());
			    if (chosenFont != null) {
					fontDialog.setFontList(chosenFont);
				}
			    FontData font = fontDialog.open();
			    if (font != null) {
			        FontData[] oldFont = chosenFont;
			        if (oldFont == null) {
						oldFont = JFaceResources.getDefaultFont().getFontData();
					}
			        setPresentsDefaultValue(false);
			        FontData[] newData = new FontData[1];
			        newData[0] = font;
			        updateFont(newData);
			        fireValueChanged(VALUE, oldFont[0], font);
			    }
			}));
            changeFontButton.addDisposeListener(event -> changeFontButton = null);
            changeFontButton.setFont(parent.getFont());
            setButtonLayoutData(changeFontButton);
        } else {
            checkParent(changeFontButton, parent);
        }
        return changeFontButton;
    }

    @Override
	public int getNumberOfControls() {
        if (previewer == null) {
			return 3;
		}

        return 4;
    }

    /**
     * Returns the preferred preview height.
     *
     * @return the height, or <code>-1</code> if no previewer
     *  is installed
     */
    public int getPreferredPreviewHeight() {
        if (previewer == null) {
			return -1;
		}
        return previewer.getPreferredExtent();
    }

    /**
     * Returns the preview control for this field editor.
     *
     * @return the preview control
     */
    public Control getPreviewControl() {
        if (previewer == null) {
			return null;
		}

        return previewer.getControl();
    }

    /**
     * Returns the value control for this field editor. The value control
     * displays the currently selected font name.
     * @param parent The Composite to create the viewer in if required
     * @return the value control
     */
    protected Label getValueControl(Composite parent) {
        if (valueControl == null) {
            valueControl = new Label(parent, SWT.LEFT);
            valueControl.setFont(parent.getFont());
            valueControl.addDisposeListener(event -> valueControl = null);
        } else {
            checkParent(valueControl, parent);
        }
        return valueControl;
    }

    /**
     * Sets the text of the change button.
     *
     * @param text the new text
     */
    public void setChangeButtonText(String text) {
        Assert.isNotNull(text);
        changeButtonText = text;
        if (changeFontButton != null) {
			changeFontButton.setText(text);
		}
    }

    /**
     * Updates the change font button and the previewer to reflect the
     * newly selected font.
     * @param font The FontData[] to update with.
     */
    private void updateFont(FontData font[]) {
        FontData[] bestFont = JFaceResources.getFontRegistry().filterData(
                font, valueControl.getDisplay());

        //if we have nothing valid do as best we can
        if (bestFont == null) {
			bestFont = getDefaultFontData();
		}

        //Now cache this value in the receiver
        this.chosenFont = bestFont;

        if (valueControl != null) {
            valueControl.setText(StringConverter.asString(chosenFont[0]));
        }
        if (previewer != null) {
            previewer.setFont(bestFont);
        }
    }

    /**
     * Store the default preference for the field
     * being edited
     */
    protected void setToDefault() {
        FontData[] defaultFontData = PreferenceConverter
                .getDefaultFontDataArray(getPreferenceStore(),
                        getPreferenceName());
        PreferenceConverter.setValue(getPreferenceStore(), getPreferenceName(),
                defaultFontData);
    }

    /**
     * Get the system default font data.
     * @return FontData[]
     */
    private FontData[] getDefaultFontData() {
        return valueControl.getDisplay().getSystemFont().getFontData();
    }

    /*
     * @see FieldEditor.setEnabled(boolean,Composite).
     */
    @Override
	public void setEnabled(boolean enabled, Composite parent) {
        super.setEnabled(enabled, parent);
        getChangeControl(parent).setEnabled(enabled);
        getValueControl(parent).setEnabled(enabled);
    }

}
