/*******************************************************************************
 *  Copyright (c) 2000, 2018 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.pde.internal.ui.editor.feature;

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

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.pde.core.IEditable;
import org.eclipse.pde.core.IModelChangedEvent;
import org.eclipse.pde.internal.core.ifeature.IFeature;
import org.eclipse.pde.internal.core.ifeature.IFeatureInfo;
import org.eclipse.pde.internal.core.ifeature.IFeatureModel;
import org.eclipse.pde.internal.core.ifeature.IFeatureURLElement;
import org.eclipse.pde.internal.ui.PDEPlugin;
import org.eclipse.pde.internal.ui.PDEPluginImages;
import org.eclipse.pde.internal.ui.PDEUIMessages;
import org.eclipse.pde.internal.ui.editor.FormLayoutFactory;
import org.eclipse.pde.internal.ui.editor.PDEFormPage;
import org.eclipse.pde.internal.ui.editor.PDESection;
import org.eclipse.pde.internal.ui.editor.context.XMLDocumentSetupParticpant;
import org.eclipse.pde.internal.ui.editor.text.IColorManager;
import org.eclipse.pde.internal.ui.editor.text.XMLConfiguration;
import org.eclipse.pde.internal.ui.elements.NamedElement;
import org.eclipse.pde.internal.ui.util.TextUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;

public class InfoSection extends PDESection {
	private final IDocument fDocument;

	private final XMLConfiguration fSourceConfiguration;

	private SourceViewer fSourceViewer;

	private CTabFolder fTabFolder;

	private Text fUrlText;

	private Object fElement;

	private int fElementIndex;

	private boolean fIgnoreChange;

	private Composite fNotebook;

	private StackLayout fNotebookLayout;

	private Control fInfoPage;

	private Control fUrlsPage;

	private Control fLicensePage;

	public InfoSection(PDEFormPage page, Composite parent, IColorManager colorManager) {
		super(page, parent, Section.DESCRIPTION | ExpandableComposite.NO_TITLE, false);
		String description = PDEUIMessages.FeatureEditor_InfoSection_desc;
		getSection().setDescription(description);
		fSourceConfiguration = new XMLConfiguration(colorManager);
		fDocument = new Document();
		new XMLDocumentSetupParticpant().setup(fDocument);
		createClient(getSection(), page.getManagedForm().getToolkit());
	}

	@Override
	public void commit(boolean onSave) {
		handleApply();
		super.commit(onSave);
	}

	@Override
	public void createClient(Section section, FormToolkit toolkit) {

		section.setLayout(FormLayoutFactory.createClearGridLayout(false, 1));
		GridData data = new GridData(GridData.FILL_BOTH);
		section.setLayoutData(data);

		Composite container = toolkit.createComposite(section);
		container.setLayout(FormLayoutFactory.createSectionClientGridLayout(false, 2));
		container.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		GridData gd;

		toolkit.createLabel(container, null);
		fTabFolder = new CTabFolder(container, SWT.FLAT | SWT.TOP);
		toolkit.adapt(fTabFolder, true, true);
		gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
		fTabFolder.setLayoutData(gd);
		gd.heightHint = 2;
		toolkit.getColors().initializeSectionToolBarColors();
		Color selectedColor = toolkit.getColors().getColor(IFormColors.TB_BG);
		fTabFolder.setSelectionBackground(new Color[] {selectedColor, toolkit.getColors().getBackground()}, new int[] {100}, true);

		fTabFolder.addSelectionListener(widgetSelectedAdapter(e -> updateTabSelection()));

		fNotebook = toolkit.createComposite(container);
		gd = new GridData(GridData.FILL_BOTH);
		gd.horizontalSpan = 2;
		fNotebook.setLayoutData(gd);
		fNotebookLayout = new StackLayout();
		fNotebook.setLayout(fNotebookLayout);

		fInfoPage = createInfoPage(toolkit, fNotebook);
		fLicensePage = createLicensePage(toolkit, fNotebook);
		fUrlsPage = createUrlsPage(toolkit, fNotebook);
		fNotebookLayout.topControl = fInfoPage;

		createTabs();
		section.setClient(container);
		initialize();
		if (fTabFolder.getItemCount() > 0) {
			fTabFolder.setSelection(0);
			updateTabSelection();
		}
	}

	private Control createLicensePage(FormToolkit toolkit, Composite parent) {
		Composite page = toolkit.createComposite(parent);
		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		layout.marginWidth = 2;
		layout.marginHeight = 0;
		layout.verticalSpacing = 8;
		layout.marginTop = 0;
		page.setLayout(layout);

		LicenseFeatureSection licenseSection = new LicenseFeatureSection(getPage(), page, fSourceConfiguration);
		licenseSection.getSection().setLayoutData(new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_BEGINNING));

		getManagedForm().addPart(licenseSection);
		return page;
	}

	private Control createInfoPage(FormToolkit toolkit, Composite parent) {
		Composite page = toolkit.createComposite(parent);
		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		layout.marginWidth = 2;
		layout.marginHeight = 5;
		layout.verticalSpacing = 8;
		page.setLayout(layout);

		GridData gd;
		Label label = toolkit.createLabel(page, PDEUIMessages.FeatureEditor_InfoSection_url);
		label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
		fUrlText = toolkit.createText(page, null, SWT.SINGLE);
		fUrlText.addModifyListener(e -> infoModified());
		gd = new GridData(GridData.FILL_HORIZONTAL);
		fUrlText.setLayoutData(gd);
		label = toolkit.createLabel(page, PDEUIMessages.FeatureEditor_InfoSection_text);
		label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
		gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING);
		label.setLayoutData(gd);
		int styles = SWT.MULTI | SWT.WRAP | SWT.V_SCROLL | SWT.H_SCROLL;
		fSourceViewer = new SourceViewer(page, null, styles);
		fSourceViewer.configure(fSourceConfiguration);
		fSourceViewer.setDocument(fDocument);
		fSourceViewer.addSelectionChangedListener(event -> updateSelection(event.getSelection()));
		StyledText styledText = fSourceViewer.getTextWidget();
		styledText.setFont(JFaceResources.getTextFont());
		styledText.setMenu(getPage().getPDEEditor().getContextMenu());
		styledText.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
		//
		if (SWT.getPlatform().equals("motif") == false) //$NON-NLS-1$
			toolkit.paintBordersFor(page);
		Control[] children = page.getChildren();
		Control control = children[children.length - 1];
		gd = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
		gd.widthHint = 50;
		gd.heightHint = 50;
		control.setLayoutData(gd);

		return page;
	}

	private Control createUrlsPage(FormToolkit toolkit, Composite parent) {
		Composite page = toolkit.createComposite(parent);
		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		layout.makeColumnsEqualWidth = true;
		layout.marginWidth = 2;
		layout.marginHeight = 5;
		layout.verticalSpacing = 8;
		page.setLayout(layout);

		URLSection urlSection = new URLSection(getPage(), page);
		urlSection.getSection().setLayoutData(new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_BEGINNING));

		URLDetailsSection urlDetailsSection = new URLDetailsSection(getPage(), page);
		urlDetailsSection.getSection().setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING));

		getManagedForm().addPart(urlSection);
		getManagedForm().addPart(urlDetailsSection);
		return page;
	}

	private void updateSelection(ISelection selection) {
		getPage().getPDEEditor().setSelection(selection);
	}

	@Override
	public boolean doGlobalAction(String actionId) {
		if (actionId.equals(ActionFactory.CUT.getId())) {
			fSourceViewer.doOperation(ITextOperationTarget.CUT);
			return true;
		} else if (actionId.equals(ActionFactory.COPY.getId())) {
			fSourceViewer.doOperation(ITextOperationTarget.COPY);
			return true;
		} else if (actionId.equals(ActionFactory.PASTE.getId())) {
			fSourceViewer.doOperation(ITextOperationTarget.PASTE);
			return true;
		} else if (actionId.equals(ActionFactory.DELETE.getId())) {
			fSourceViewer.doOperation(ITextOperationTarget.DELETE);
			return true;
		} else if (actionId.equals(ActionFactory.UNDO.getId())) {
			fSourceViewer.doOperation(ITextOperationTarget.UNDO);
			return true;
		} else if (actionId.equals(ActionFactory.REDO.getId())) {
			fSourceViewer.doOperation(ITextOperationTarget.REDO);
			return true;
		} else if (actionId.equals(ActionFactory.SELECT_ALL.getId())) {
			fSourceViewer.doOperation(ITextOperationTarget.SELECT_ALL);
		}
		return false;
	}

	@Override
	public boolean setFormInput(Object input) {
		if (input instanceof IFeatureInfo info) {
			int index = info.getIndex();
			if (index != -1)
				fTabFolder.setSelection(index);
			updateEditorInput(input, false);
			return true;
		}
		if (input instanceof IFeatureURLElement || input instanceof NamedElement) {
			fTabFolder.setSelection(3);
			updateEditorInput(input, false);
			return true;
		}
		return false;
	}

	private void handleApply() {
		if (0 <= fElementIndex && fElementIndex < 2 && fElement != null) {
			handleApply((IFeatureInfo) fElement, fTabFolder.getSelectionIndex());
		} else {
			handleApply(null, fTabFolder.getSelectionIndex());
		}
	}

	private void handleApply(IFeatureInfo info, int index) {
		if (index >= 2)
			return;
		String urlName = fUrlText.getText();
		String text = fDocument.get();
		applyInfoText(info, urlName, text, index);
		updateTabImage(fTabFolder.getSelection());
	}

	private void applyInfoText(IFeatureInfo targetInfo, String urlText, String text, int index) {
		String url = null;

		if (urlText.length() > 0) {
			url = urlText;
		}
		try {
			IFeatureModel model = (IFeatureModel) getPage().getModel();
			IFeature feature = model.getFeature();

			if (targetInfo == null)
				targetInfo = feature.getFeatureInfo(index);

			if (targetInfo == null) {
				targetInfo = model.getFactory().createInfo(index);
				feature.setFeatureInfo(targetInfo, index);
			}
			targetInfo.setURL(url);
			targetInfo.setDescription(text);
		} catch (CoreException e) {
			// TODO: This should probably at least be logged
		}
	}

	protected void fillContextMenu(IMenuManager manager) {
		getPage().getPDEEditor().getContributor().contextMenuAboutToShow(manager);
	}

	public void initialize() {
		IFeatureModel featureModel = (IFeatureModel) getPage().getModel();
		fDocument.addDocumentListener(new IDocumentListener() {
			@Override
			public void documentChanged(DocumentEvent e) {
				infoModified();
			}

			@Override
			public void documentAboutToBeChanged(DocumentEvent e) {
			}
		});
		fUrlText.setEditable(featureModel.isEditable());
		fSourceViewer.getTextWidget().setEditable(featureModel.isEditable());
		featureModel.addModelChangedListener(this);
		updateEditorInput(featureModel.getFeature().getFeatureInfo(0), false);
	}

	@Override
	public void dispose() {
		// Dispose of the source configuration
		if (fSourceConfiguration != null) {
			fSourceConfiguration.dispose();
		}
		IFeatureModel featureModel = (IFeatureModel) getPage().getModel();
		if (featureModel != null)
			featureModel.removeModelChangedListener(this);
		super.dispose();
	}

	private void infoModified() {
		IFeatureModel featureModel = (IFeatureModel) getPage().getModel();
		if (!fIgnoreChange && featureModel instanceof IEditable) {
			markDirty();
		}
	}

	@Override
	public void modelChanged(IModelChangedEvent e) {
		if (e.getChangeType() == IModelChangedEvent.WORLD_CHANGED) {
			markStale();
		}
	}

	@Override
	public void refresh() {
		IFeatureModel model = (IFeatureModel) getPage().getModel();
		int index = fTabFolder.getSelectionIndex();
		if (index < 3) {
			IFeatureInfo info = model.getFeature().getFeatureInfo(index);
			fElement = null;
			fElementIndex = -1;
			updateEditorInput(info, false);
		}
		super.refresh();
	}

	private void createTabs() {
		IFeatureModel model = (IFeatureModel) getPage().getModel();
		IFeature feature = model.getFeature();
		addTab(PDEUIMessages.FeatureEditor_info_description, feature.getFeatureInfo(0));
		addTab(PDEUIMessages.FeatureEditor_info_copyright, feature.getFeatureInfo(1));
		addTab(PDEUIMessages.FeatureEditor_info_license, feature.getFeatureInfo(2));
		addTab(PDEUIMessages.FeatureEditor_info_discoveryUrls, null);
	}

	private void addTab(String label, IFeatureInfo info) {
		CTabItem item = new CTabItem(fTabFolder, SWT.NULL);
		item.setText(label);
		item.setData(info);
		updateTabImage(item);
	}

	private void updateTabImage(CTabItem item) {
		if (item == null)
			return;
		Object info = item.getData();
		if (info != null) {
			item.setImage(PDEPlugin.getDefault().getLabelProvider().getImage(info));
		} else {
			item.setImage(PDEPlugin.getDefault().getLabelProvider().get(PDEPluginImages.DESC_DOC_SECTION_OBJ));
		}
	}

	private void updateTabSelection() {
		IFeatureModel model = (IFeatureModel) getPage().getModel();
		IFeature feature = model.getFeature();
		int index = fTabFolder.getSelectionIndex();
		if (index < 3) {
			IFeatureInfo info = feature.getFeatureInfo(index);
			updateEditorInput(info, true);
		}

		// TODO: Why is this code in both updateEditorInput and updateTabSelection?
		Control oldPage = fNotebookLayout.topControl;
		switch (index) {
			case 0 :
			case 1 :
				fNotebookLayout.topControl = fInfoPage;
				break;
			case 2 :
				fNotebookLayout.topControl = fLicensePage;
				break;
			case 3 :
				fNotebookLayout.topControl = fUrlsPage;
				break;
		}
		if (oldPage != fNotebookLayout.topControl)
			fNotebook.layout();
	}

	@Override
	public void setFocus() {
		fSourceViewer.getTextWidget().setFocus();
		updateSelection(fSourceViewer.getSelection());
	}

	private void commitPrevious() {
		IFeatureInfo previous = (IFeatureInfo) fElement;
		handleApply(previous, fElementIndex);
	}

	public void updateEditorInput(Object input, boolean commitPrevious) {
		if (isDirty() && commitPrevious /*
											 * && element != null && element !=
											 * input
											 */) {
			commitPrevious();
		}
		fIgnoreChange = true;
		String text = ""; //$NON-NLS-1$
		String url = null;
		if (input instanceof IFeatureInfo info) {
			text = info.getDescription();
			url = info.getURL();
		}
		if (text == null)
			text = ""; //$NON-NLS-1$
		else
			text = TextUtil.createMultiLine(text, 60, false);
		fDocument.set(text);
		if (url == null)
			fUrlText.setText(""); //$NON-NLS-1$
		else
			fUrlText.setText(url.toString());
		fElement = input;
		fElementIndex = fTabFolder.getSelectionIndex();

		Control oldPage = fNotebookLayout.topControl;
		switch (fElementIndex) {
			case 0 :
			case 1 :
				fNotebookLayout.topControl = fInfoPage;
				break;
			case 2 :
				fNotebookLayout.topControl = fLicensePage;
				break;
			case 3 :
				fNotebookLayout.topControl = fUrlsPage;
				break;
		}
		if (oldPage != fNotebookLayout.topControl)
			fNotebook.layout();

		fIgnoreChange = false;
	}

	@Override
	public boolean canPaste(Clipboard clipboard) {
		return fSourceViewer.canDoOperation(ITextOperationTarget.PASTE);
	}
}
