/*
 * ARX Data Anonymization Tool
 * Copyright 2012 - 2023 Fabian Prasser and contributors
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.deidentifier.arx.gui.view.impl.wizard;

import java.util.ArrayList;
import java.util.Locale;

import org.deidentifier.arx.DataType;
import org.deidentifier.arx.DataType.DataTypeDescription;
import org.deidentifier.arx.gui.Controller;
import org.deidentifier.arx.gui.resources.Resources;
import org.deidentifier.arx.gui.view.SWTUtil;
import org.deidentifier.arx.gui.view.impl.common.ComponentTable;
import org.deidentifier.arx.gui.view.impl.common.table.CTConfiguration;
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;

/**
 * A page for configuring the order-based builder.
 *
 * @author Fabian Prasser
 * @param <T>
 */
public class HierarchyWizardPageOrder<T> extends HierarchyWizardPageBuilder<T> {

    /** Var. */
    private final HierarchyWizardModelOrder<T> model;
    
    /** Var. */
    private final Controller                   controller;
    
    /** Var. */
    private ComponentTable                     table;
    
    /** Var. */
    private Combo                              combo;
    
    /** Var. */
    private HierarchyWizardEditor<T>           editor; 
    
    /**
     * Creates a new instance.
     *
     * @param controller
     * @param wizard
     * @param model
     * @param finalPage
     */
    public HierarchyWizardPageOrder(final Controller controller,
                                    final HierarchyWizard<T> wizard,
                                    final HierarchyWizardModel<T> model,
                                    final HierarchyWizardPageFinal<T> finalPage) {
        super(wizard, model.getOrderModel(), finalPage);
        this.model = model.getOrderModel();
        this.controller = controller;
        setTitle(Resources.getMessage("HierarchyWizardPageOrder.0")); //$NON-NLS-1$
        setDescription(Resources.getMessage("HierarchyWizardPageOrder.4")); //$NON-NLS-1$
        setPageComplete(true);
    }
    
    @Override
    public void createControl(final Composite parent) {
        
        Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayout(SWTUtil.createGridLayout(2, false));
        createOrder(composite);
        createGroups(composite);
        setControl(composite);
    }

    @Override
    public void updatePage() {
        table.setData(getDataProvider(model.getData()), new String[]{Resources.getMessage("HierarchyWizardPageOrder.6")}); //$NON-NLS-1$
        combo.select(getIndexOfDataType(model.getDataType()));
        if (editor != null) editor.setFunction(model.getDefaultFunction());
        model.update();
    }
    
    /**
     * Moves the selected item down.
     */
    private void actionDown() {
        Integer index = table.getSelectedRow();
        if (index == null) return;
        if (model.moveDown(index)) {
            table.refresh();
            table.setSelection(index+1, 0);
            update();
        }
    }
    
    /**
     * Sorts according to the index of a data type.
     *
     * @param index
     * @return
     */
    private int actionSort(int index) {
        
        // Initial data type
        DataType<?> type = model.getDataType();
        int returnIndex = index;
 
        // If not default
        if (index>0) {
            
            // Extract chosen type
            String label = combo.getItem(combo.getSelectionIndex());
            DataTypeDescription<?> description = getDataType(label);
    
            // Open format dialog
            if (description.getLabel().equals("Ordinal")) { //$NON-NLS-1$
                final String text1 = Resources.getMessage("AttributeDefinitionView.9"); //$NON-NLS-1$
                final String text2 = Resources.getMessage("AttributeDefinitionView.10"); //$NON-NLS-1$
                String[] array = controller.actionShowOrderValuesDialog(getShell(), 
                                                                        text1, text2,
                                                                        DataType.STRING,
                                                                        model.getLocale(),
                                                                        model.getData());
                if (array == null) {
                    type = DataType.STRING;
                } else {
                    try {
                        type = DataType.createOrderedString(array);
                        if (!isValid(type, model.getData())) {
                            type = DataType.STRING;
                        }
                    } catch (Exception e) {
                        controller.actionShowInfoDialog(getShell(), 
                                                        Resources.getMessage("HierarchyWizardPageOrder.15"), Resources.getMessage("HierarchyWizardPageOrder.16") + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
                        type = DataType.STRING;
                    }
                }
            } else if (description.hasFormat()) {
                final String text1 = Resources.getMessage("AttributeDefinitionView.9"); //$NON-NLS-1$
                final String text2 = Resources.getMessage("AttributeDefinitionView.10"); //$NON-NLS-1$
                final String format[] = controller.actionShowFormatInputDialog(getShell(),
                                                                             text1,
                                                                             text2,
                                                                             model.getLocale(),
                                                                             description,
                                                                             model.getData());
                if (format == null || format[0] == null) {
                    type = DataType.STRING;
                } else {
                    type = description.newInstance(format[0], format[1] != null ? getLocale(format[1]) : model.getLocale());
                }
            } else {
                type = description.newInstance();
                if (!isValid(type, model.getData())) {
                    type = DataType.STRING;
                }
            }
            returnIndex = getIndexOfDataType(type) + 1;
        }
        if (!model.sort(type)) {
            model.sort(DataType.STRING);
        }
        table.refresh();
        return returnIndex;
    }
    
    /**
     * Moves the selected item up.
     */
    private void actionUp() {
        Integer index = table.getSelectedRow();
        if (index == null) return;
        if (model.moveUp(index)) {
            table.refresh();
            table.setSelection(index-1, 0);
            update();
        }
    }

    /**
     * Create the grouping-part of the page.
     *
     * @param parent
     */
    private void createGroups(Composite parent){
        Group composite = new Group(parent, SWT.NONE);
        composite.setText(Resources.getMessage("HierarchyWizardPageOrder.17")); //$NON-NLS-1$
        composite.setLayout(SWTUtil.createGridLayout(1, false));
        composite.setLayoutData(SWTUtil.createFillGridData());
        editor =  new HierarchyWizardEditor<T>(composite, (HierarchyWizardModelGrouping<T>) model);
        editor.setLayoutData(SWTUtil.createFillGridData());
    }

    /**
     * Create the ordering-part of the page.
     *
     * @param parent
     */
    private void createOrder(Composite parent){
        Group composite = new Group(parent, SWT.NONE);
        composite.setText(Resources.getMessage("HierarchyWizardPageOrder.18")); //$NON-NLS-1$
        composite.setLayout(SWTUtil.createGridLayout(1, false));
        composite.setLayoutData(SWTUtil.createFillVerticallyGridData());
        
        // Configure table
        CTConfiguration config = new CTConfiguration(parent, CTConfiguration.STYLE_TABLE);
        config.setHorizontalAlignment(SWT.CENTER);
        config.setCellSelectionEnabled(false);
        config.setColumnSelectionEnabled(false);
        config.setRowSelectionEnabled(false);
        config.setColumnHeaderLayout(CTConfiguration.COLUMN_HEADER_LAYOUT_FILL_EQUAL);
        config.setRowHeaderLayout(CTConfiguration.ROW_HEADER_LAYOUT_FILL);

        // Create table
        this.table = new ComponentTable(composite, SWT.BORDER, config);
        this.table.getControl().setLayoutData(SWTUtil.createFillGridData());
        
        final Button up = new Button(composite, SWT.NONE);
        up.setText(Resources.getMessage("HierarchyWizardPageOrder.3")); //$NON-NLS-1$
        up.setImage(controller.getResources().getManagedImage("arrow_up.png")); //$NON-NLS-1$
        up.setLayoutData(SWTUtil.createFillHorizontallyGridData());
        up.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent arg0) {
                actionUp();
            }
        });

        final Button down = new Button(composite, SWT.NONE);
        down.setText(Resources.getMessage("HierarchyWizardPageOrder.5")); //$NON-NLS-1$
        down.setImage(controller.getResources().getManagedImage("arrow_down.png")); //$NON-NLS-1$
        down.setLayoutData(SWTUtil.createFillHorizontallyGridData());
        down.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent arg0) {
                actionDown();
            }
        });

        final Composite bottom1 = new Composite(composite, SWT.NONE);
        bottom1.setLayoutData(SWTUtil.createFillHorizontallyGridData());
        bottom1.setLayout(SWTUtil.createGridLayout(2, false));

        final Label text = new Label(bottom1, SWT.NONE);
        text.setText(Resources.getMessage("HierarchyWizardPageOrder.7")); //$NON-NLS-1$

        combo = new Combo(bottom1, SWT.NONE);
        combo.setLayoutData(SWTUtil.createFillHorizontallyGridData());
        combo.add(Resources.getMessage("HierarchyWizardPageOrder.8")); //$NON-NLS-1$
        for (String type : getDataTypes()){
            combo.add(type);
        }
        combo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent arg0) {
                int index = combo.getSelectionIndex();
                if (index >=0 ){
                    combo.select(actionSort(index));
                }
            }
        });
        updatePage();
    }

    /**
     * Returns a data provider for the given array.
     *
     * @param array
     * @return
     */
    private IDataProvider getDataProvider(final String[] array){
        return new IDataProvider(){
            @Override
            public int getColumnCount() {
                return 1;
            }
            @Override
            public Object getDataValue(int columnIndex, int rowIndex) {
                return array[rowIndex];
            }
            @Override
            public int getRowCount() {
                return array.length;
            }
            @Override
            public void setDataValue(int columnIndex,
                                     int rowIndex,
                                     Object newValue) {
                /* Ignore*/
            }
        };
    }
    
    /**
     * Returns a description for the given label.
     *
     * @param label
     * @return
     */
    private DataTypeDescription<?> getDataType(String label){
        for (DataTypeDescription<?> desc : DataType.list()){
            if (label.equals(desc.getLabel())){
                return desc;
            }
        }
        throw new RuntimeException(Resources.getMessage("HierarchyWizardPageOrder.19")+label); //$NON-NLS-1$
    }
    
    /**
     * Returns the labels of all available data types.
     *
     * @return
     */
    private String[] getDataTypes(){
        ArrayList<String> list = new ArrayList<String>();
        for (DataTypeDescription<?> desc : DataType.list()){
            list.add(desc.getLabel());
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     * Returns the index of a given data type.
     *
     * @param type
     * @return
     */
    private int getIndexOfDataType(DataType<?> type){
        int idx = 0;
        for (DataTypeDescription<?> desc : DataType.list()){
            if (desc.getLabel().equals(type.getDescription().getLabel())) {
                return idx;
            }
            idx++;
        }
        return -1;
    }

    /**
     * Returns the local for the given isoLanguage
     * @param isoLanguage
     * @return
     */
    private Locale getLocale(String isoLanguage) {
        for (Locale locale : Locale.getAvailableLocales()) {
            if (locale.getLanguage().toUpperCase().equals(isoLanguage.toUpperCase())) {
                return locale;
            }
        }
        throw new IllegalStateException("Unknown locale");
    }
    
    /**
     * Checks whether the data type is valid.
     *
     * @param type
     * @param values
     * @return
     */
    private boolean isValid(DataType<?> type, String[] values){
        for (String value : values){
            if (!type.isValid(value)) {
                return false;
            }
        }
        return true;
    }
}
