/*******************************************************************************
 * Copyright (C) 2021, 1C-Soft LLC 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:
 *     1C-Soft LLC - initial API and implementation
 *******************************************************************************/
package com.e1c.v8codestyle.md.ui;

import static com._1c.g5.v8.dt.metadata.mdclass.MdClassPackage.Literals.MD_OBJECT__NAME;

import java.util.Map.Entry;

import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.conversion.IConverter;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.databinding.EMFProperties;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.jface.databinding.swt.typed.WidgetProperties;
import org.eclipse.jface.databinding.viewers.IViewerObservableValue;
import org.eclipse.jface.databinding.viewers.typed.ViewerProperties;
import org.eclipse.jface.dialogs.IPageChangeProvider;
import org.eclipse.jface.dialogs.IPageChangedListener;
import org.eclipse.jface.dialogs.PageChangedEvent;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

import com._1c.g5.v8.dt.md.ui.shared.MdUiSharedImages;
import com._1c.g5.v8.dt.metadata.mdclass.CommonModule;
import com._1c.g5.v8.dt.metadata.mdclass.ScriptVariant;
import com._1c.g5.v8.dt.metadata.mdclass.util.MdClassUtil;
import com._1c.g5.v8.dt.ui.wizards.DtNewWizardPage;
import com.e1c.v8codestyle.md.CommonModuleTypes;

/**
 * DT "new Wizard" page that contribute to extension, allows to select {@link CommonModule common module} type and
 * set EMF features according to the type, also adds type suffix to the base name.
 * This page also detects type by user's input name of module with suffix and then sets EMF features.
 * Page make modification only if user switched next to the page.
 *
 * @author Dmitriy Marmyshev
 */
public class CommonModuleTypeDtNewWizardPage
    extends DtNewWizardPage<CommonModule>
    implements IPageChangedListener
{

    private ListViewer moduleTypeViewer;

    private String currentSuffix = ""; //$NON-NLS-1$

    public CommonModuleTypeDtNewWizardPage()
    {
        super("CommonModuleType"); //$NON-NLS-1$
        setTitle(Messages.CommonModuleTypeDtNewWizardPage_Select_common_module_type);
        setDescription(Messages.CommonModuleTypeDtNewWizardPage_Choose_valid_common_module_type_from_list);
        setImageDescriptor(MdUiSharedImages.getImageDescriptor(MdUiSharedImages.IMG_NEW_COMMON_MODULE));
    }

    @SuppressWarnings("unchecked")
    @Override
    public void createPageControls(Composite container)
    {

        Composite composite = new Composite(container, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(composite);
        GridLayoutFactory.swtDefaults().numColumns(2).applyTo(composite);

        Label label = new Label(composite, SWT.WRAP);
        String title = com._1c.g5.v8.dt.common.Functions.featureToLabel().apply(MD_OBJECT__NAME);
        label.setText(title);
        Text newName = new Text(composite, SWT.BORDER | SWT.READ_ONLY);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(newName);

        Label message = new Label(composite, SWT.WRAP);
        message.setText(Messages.CommonModuleTypeDtNewWizardPage_Select_type);
        moduleTypeViewer = new ListViewer(composite, SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);

        moduleTypeViewer.setContentProvider(ArrayContentProvider.getInstance());
        moduleTypeViewer.setLabelProvider(new CommonModuleTypesLableProvider());
        moduleTypeViewer.setInput(CommonModuleTypes.values());
        moduleTypeViewer.addSelectionChangedListener(e -> validate());
        GridDataFactory.fillDefaults().grab(true, true).applyTo(moduleTypeViewer.getControl());

        IObservableValue<String> nameValue = EMFProperties.value(MD_OBJECT__NAME).observe(getContext().getModel());

        IViewerObservableValue<CommonModuleTypes> selection =
            ViewerProperties.singlePostSelection(CommonModuleTypes.class).observe(moduleTypeViewer);

        UpdateValueStrategy<CommonModuleTypes, String> covert =
            new UpdateValueStrategy<>(UpdateValueStrategy.POLICY_UPDATE);
        covert.setConverter(IConverter.create(this::convertTypeToName));
        getDataBindingContext().bindValue(nameValue, selection, UpdateValueStrategy.never(), covert);

        getDataBindingContext().bindValue(WidgetProperties.text().observe(newName), nameValue,
            UpdateValueStrategy.never(), null);

        if (getWizard().getContainer() instanceof IPageChangeProvider)
        {
            ((IPageChangeProvider)getWizard().getContainer()).addPageChangedListener(this);
        }

        validate();

    }

    @Override
    public void dispose()
    {
        if (getWizard().getContainer() instanceof IPageChangeProvider)
        {
            ((IPageChangeProvider)getWizard().getContainer()).removePageChangedListener(this);
        }
        super.dispose();
    }

    @Override
    public void finish(IProgressMonitor monitor)
    {
        updateModuleType(getSelection());
    }

    @Override
    public void pageChanged(PageChangedEvent event)
    {
        if (event.getSelectedPage() == this)
        {
            onPageChanged();
        }
    }

    private void onPageChanged()
    {
        CommonModule model = getContext().getModel();
        String name = model.getName();
        ScriptVariant script = getContext().getV8project().getScriptVariant();
        CommonModuleTypes type = CommonModuleTypes.findClosestTypeByName(name, script);

        currentSuffix = type.getNameSuffix(script);
        moduleTypeViewer.setSelection(new StructuredSelection(type), true);
        moduleTypeViewer.getControl().setFocus();

    }

    private void validate()
    {
        CommonModuleTypes type = getSelection();
        if (type != null)
        {
            setErrorMessage(null);
            setPageComplete(true);
        }
        else
        {
            setErrorMessage(Messages.CommonModuleTypeDtNewWizardPage_Select_type_from_list);
            setPageComplete(false);
        }
    }

    private void updateModuleType(CommonModuleTypes type)
    {
        if (type == null)
        {
            return;
        }

        boolean mobileOnly = MdClassUtil.isMobileApplicationUsePurposes(getContext().getV8project().getUsePurposes());

        CommonModule module = getContext().getModel();
        for (Entry<EStructuralFeature, Object> entry : type.getFeatureValues(mobileOnly).entrySet())
        {
            module.eSet(entry.getKey(), entry.getValue());
        }

        String name = convertTypeToName(type);
        module.setName(name);
    }

    private String convertTypeToName(CommonModuleTypes type)
    {
        CommonModule module = getContext().getModel();
        ScriptVariant scriptVariant = getContext().getV8project().getScriptVariant();
        String name = module.getName();
        if (currentSuffix != null && !currentSuffix.isEmpty() && name.endsWith(currentSuffix))
        {
            name = name.substring(0, name.length() - currentSuffix.length());
        }
        String suffix = ""; //$NON-NLS-1$
        if (type != null)
        {
            suffix = type.getNameSuffix(scriptVariant);
        }
        currentSuffix = suffix;
        return name + suffix;
    }

    private CommonModuleTypes getSelection()
    {
        ISelection selection = moduleTypeViewer.getSelection();
        if (selection instanceof IStructuredSelection && !selection.isEmpty())
        {
            return (CommonModuleTypes)((IStructuredSelection)selection).getFirstElement();
        }
        return null;
    }

    private final class CommonModuleTypesLableProvider
        extends LabelProvider
    {

        @Override
        public String getText(Object element)
        {
            if (element instanceof CommonModuleTypes)
            {
                CommonModuleTypes type = (CommonModuleTypes)element;
                ScriptVariant script = getContext().getV8project().getScriptVariant();
                String suffix = type.getNameSuffix(script);
                if (suffix.isEmpty())
                {
                    return type.getTitle();
                }
                return type.getTitle() + " (" + suffix + ")"; //$NON-NLS-1$ //$NON-NLS-2$
            }
            return super.getText(element);
        }

    }

}
