/*******************************************************************************
 * Copyright (c) 2009, 2018 STMicroelectronics and others.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Marzia Maugeri <marzia.maugeri@st.com> - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.dataviewers.dialogs;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TrayDialog;
import org.eclipse.linuxtools.dataviewers.abstractviewers.ISTDataViewersField;
import org.eclipse.linuxtools.dataviewers.abstractviewers.STDataViewersComparator;
import org.eclipse.linuxtools.dataviewers.abstractviewers.STDataViewersMessages;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

/**
 * This dialog is used to choose how the data is sorted in the viewer
 *
 */
public class STDataViewersSortDialog extends TrayDialog {

    private final STDataViewersComparator sorter;

    private Combo[] priorityCombos;

    private Button[] ascendingButtons;

    private Button[] descendingButtons;

    private boolean dirty;

    /**
     * Create a new instance of the receiver.
     *
     * @param parentShell The parent shell for the dialog.
     * @param sorter The sorter to be used
     */
    public STDataViewersSortDialog(Shell parentShell, STDataViewersComparator sorter) {
        super(parentShell);
        this.sorter = sorter;
        this.dirty = false;
    }

    @Override
    protected void configureShell(Shell newShell) {
        super.configureShell(newShell);
        newShell.setText(STDataViewersMessages.sortDialog_title);
    }

    @Override
    protected Control createDialogArea(Composite parent) {
        Composite composite = (Composite) super.createDialogArea(parent);
        if (sorter == null) {
            return composite;
        }

        initializeDialogUnits(composite);

        createPrioritiesArea(composite);
        createRestoreDefaultsButton(composite);
        createSeparatorLine(composite);

        Dialog.applyDialogFont(composite);

        return composite;
    }

    /**
     * Create the proirities area.
     *
     * @param parent
     */
    private void createPrioritiesArea(Composite parent) {
        Composite prioritiesArea = new Composite(parent, SWT.NULL);
        prioritiesArea.setLayout(new GridLayout(3, false));

        int[] priorities = sorter.getPriorities();

        ascendingButtons = new Button[priorities.length];
        descendingButtons = new Button[priorities.length];
        priorityCombos = new Combo[Math.min(priorities.length, STDataViewersComparator.MAX_DEPTH)];

        Label sortByLabel = new Label(prioritiesArea, SWT.NULL);
        sortByLabel.setText(STDataViewersMessages.sortDialog_label);
        GridData data = new GridData();
        data.horizontalSpan = 3;
        sortByLabel.setLayoutData(data);

        for (int i = 0; i < priorityCombos.length; i++) {
            final int index = i;
            Label numberLabel = new Label(prioritiesArea, SWT.NULL);
            numberLabel.setText(NLS.bind(STDataViewersMessages.sortDialog_columnLabel, i + 1));

            priorityCombos[i] = new Combo(prioritiesArea, SWT.READ_ONLY);
            priorityCombos[i].setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

            Composite directionGroup = new Composite(prioritiesArea, SWT.NONE);
            directionGroup.setLayout(new GridLayout(2, false));

            ascendingButtons[i] = new Button(directionGroup, SWT.RADIO);
            ascendingButtons[i].setText(getAscendingText(i));
			ascendingButtons[i].addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> markDirty()));
            descendingButtons[i] = new Button(directionGroup, SWT.RADIO);
            descendingButtons[i].setText(getDescendingText(i));
			descendingButtons[i].addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> markDirty()));

            if (i < priorityCombos.length - 1) {
                priorityCombos[i].addSelectionListener(new SelectionAdapter() {
                    @Override
                    public void widgetSelected(SelectionEvent e) {
                        List<String> allItems = new ArrayList<>(Arrays.asList(priorityCombos[index].getItems()));
                        computeSelectionItems(index, allItems);
                        markDirty();
                    }

                    private void computeSelectionItems(int index, List<String> allItems) {
                        // if not after the last
                        if (index < priorityCombos.length) {
                            // target combo
                            Combo priorityCombo = priorityCombos[index];
                            // target combo's "old selection" (current selection)
                            String oldSelection = priorityCombo.getItem(priorityCombo.getSelectionIndex());
                            // setting new items list
                            priorityCombo.setItems(allItems.toArray(new String[allItems.size()]));

                            if (allItems.contains(oldSelection)) {
                                // old selection can be kept.
                                String newSelection = oldSelection;
                                priorityCombo.select(allItems.indexOf(oldSelection));
                                allItems.remove(newSelection);
                            } else {
                                // old selection has been removed by another combo.
                                // selecting a new element (the first) in the items list.
                                String newSelection = allItems.get(0);
                                priorityCombo.select(0);
                                allItems.remove(newSelection);
                            }
                            computeSelectionItems(index + 1, allItems);
                        }
                    }
                });
            } else {
				priorityCombos[i].addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> markDirty()));
            }
        }

        // set widget's values from sorter data
        // (combos and radio buttons)
        updateUIFromSorter();
    }

    /**
     * Get the descending label for the Descending field at i. Use the index to determine the mnemonic.
     *
     * @param index
     * @return String
     */
    private String getDescendingText(int index) {
        switch (index) {
        case 1:
            return STDataViewersMessages.sortDirectionDescending_text2;
        case 2:
            return STDataViewersMessages.sortDirectionDescending_text3;
        case 3:
            return STDataViewersMessages.sortDirectionDescending_text4;
        default:
            return STDataViewersMessages.sortDirectionDescending_text;
        }

    }

    /**
     * Get the ascending label for the Ascending field at i. Use the index to determine the mnemonic.
     *
     * @param index
     * @return String
     */
    private String getAscendingText(int index) {
        switch (index) {
        case 1:
            return STDataViewersMessages.sortDirectionAscending_text2;
        case 2:
            return STDataViewersMessages.sortDirectionAscending_text3;
        case 3:
            return STDataViewersMessages.sortDirectionAscending_text4;
        default:
            return STDataViewersMessages.sortDirectionAscending_text;
        }

    }

    /**
     * Create the restore defaults button.
     *
     * @param parent
     */
    private void createRestoreDefaultsButton(Composite parent) {
        Button defaultsButton = new Button(parent, SWT.PUSH);
        defaultsButton.setText(STDataViewersMessages.restoreDefaults_text);
        setButtonSize(defaultsButton, new GridData(GridData.HORIZONTAL_ALIGN_END | GridData.FILL_HORIZONTAL));
		defaultsButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
			restoreDefaults();
			markDirty();
		}));
    }

    private void createSeparatorLine(Composite parent) {
        Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
        separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER));
    }

    private void restoreDefaults() {
        updateUI(null, null);
    }

    private void updateUIFromSorter() {
        updateUI(sorter.getPriorities(), sorter.getDirections());
    }

    private void updateUI(int[] priorities, int[] directions) {
        Item[] columns = sorter.getColumns();
        List<String> allItems = new ArrayList<>();
        List<Integer> allDirections = new ArrayList<>();
        for (int i = 0; i < columns.length; i++) {
            if (priorities == null || directions == null) {
                allItems.add(columns[i].getText());
                ISTDataViewersField field = (ISTDataViewersField) columns[i].getData();
                allDirections.add(field.getDefaultDirection());
            } else {
                allItems.add(columns[priorities[i]].getText());
                allDirections.add(directions[priorities[i]]);
            }
        }

        for (int i = 0; i < priorityCombos.length; i++) {
            priorityCombos[i].removeAll();
            priorityCombos[i].setItems(allItems.toArray(new String[allItems.size()]));
            priorityCombos[i].select(0);
            allItems.remove(0);
            ascendingButtons[i].setSelection(allDirections.get(0) == STDataViewersComparator.ASCENDING);
            descendingButtons[i].setSelection(allDirections.get(0) == STDataViewersComparator.DESCENDING);
            allDirections.remove(0);
        }
    }

    @Override
    public int open() {
        dirty = false;
        return super.open();
    }

    @Override
    protected void okPressed() {
        if (isDirty()) {
            outerfor: for (int i = priorityCombos.length - 1; i >= 0; i--) {
                Combo combo = priorityCombos[i];
                int index = combo.getSelectionIndex();
                String item = combo.getItem(index);
                Item[] columns = sorter.getColumns();
                for (Item column : columns) {
                    if (item.equals(column.getText())) {
                        ISTDataViewersField field = (ISTDataViewersField) column.getData();
                        sorter.setTopPriority(column, field);
                        int direction = STDataViewersComparator.ASCENDING;
                        if (descendingButtons[i].getSelection()) {
                            direction = STDataViewersComparator.DESCENDING;
                        }
                        sorter.setTopPriorityDirection(direction);
                        continue outerfor;
                    }
                }
                sorter.resetState();
                return;
            }
        }
        super.okPressed();
    }

    /**
     * @return boolean
     */
    public boolean isDirty() {
        return dirty;
    }

    /**
     * Sets the dirty flag to true.
     */
    public void markDirty() {
        dirty = true;
    }

    /**
     * Set the layout data of the button to a GridData with appropriate heights and widths.
     *
     * @param button
     */
    private void setButtonSize(Button button, GridData buttonData) {
        button.setFont(button.getParent().getFont());
        int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
        buttonData.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
        button.setLayoutData(buttonData);
    }

    /**
     * Return the sorter for the receiver.
     *
     * @return TableSorter
     */
    public STDataViewersComparator getSorter() {
        return new STDataViewersComparator(sorter);
    }

}
