/*******************************************************************************
 * Copyright (c) 2006, 2012 Wind River Systems 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:
 *     Wind River Systems - initial API and implementation
 *     Ericsson AB		  - Modules view for DSF implementation
 *******************************************************************************/
package org.eclipse.cdt.dsf.debug.ui.viewmodel.modules;

import java.util.Map;
import java.util.concurrent.RejectedExecutionException;

import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IModules;
import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMContext;
import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMData;
import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl;
import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants;
import org.eclipse.cdt.dsf.internal.ui.DsfUILabelImage;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor;
import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.IElementPropertiesProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelAttribute;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelColumnInfo;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelFont;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertiesBasedLabelProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.update.StaleDataLabelBackground;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.resource.JFaceResources;

/**
 * @since 1.0
 */
public class ModulesVMNode extends AbstractDMVMNode implements IElementLabelProvider, IElementPropertiesProvider {
	/**
	 * Marker type for the modules VM context.  It allows action enablement
	 * expressions to check for module context type.
	 */
	public class ModuleVMContext extends DMVMContext {
		protected ModuleVMContext(IDMContext dmc) {
			super(dmc);
		}
	}

	/**
	 * @since 2.0
	 */
	public static final String PROP_IS_LOADED = "is_loaded"; //$NON-NLS-1$

	/**
	 * The label provider delegate.  This VM node will delegate label updates to this provider
	 * which can be created by sub-classes.
	 *
	 * @since 2.0
	 */
	private IElementLabelProvider fLabelProvider;

	/**
	 * Creates the label provider delegate.  This VM node will delegate label
	 * updates to this provider which can be created by sub-classes.
	 *
	 * @return Returns the label provider for this node.
	 *
	 * @since 2.0
	 */
	protected IElementLabelProvider createLabelProvider() {
		PropertiesBasedLabelProvider provider = new PropertiesBasedLabelProvider();

		provider.setColumnInfo(PropertiesBasedLabelProvider.ID_COLUMN_NO_COLUMNS,
				new LabelColumnInfo(new LabelAttribute[] {
						new LabelText(MessagesForModulesVM.ModulesVMNode_No_columns__text_format,
								new String[] { PROP_NAME }),
						new DsfUILabelImage(IDsfDebugUIConstants.IMG_OBJS_SHARED_LIBRARY_SYMBOLS_LOADED) {
							{
								setPropertyNames(new String[] { PROP_IS_LOADED });
							}

							@Override
							public boolean checkProperty(String propertyName, IStatus status,
									Map<String, Object> properties) {
								if (PROP_IS_LOADED.equals(propertyName)) {
									return Boolean.TRUE.equals(properties.get(propertyName));
								}
								return super.checkProperty(propertyName, status, properties);
							}
						}, new DsfUILabelImage(IDsfDebugUIConstants.IMG_OBJS_SHARED_LIBRARY_SYMBOLS_UNLOADED),
						new StaleDataLabelBackground(), new LabelFont(JFaceResources
								.getFontDescriptor(IDebugUIConstants.PREF_VARIABLE_TEXT_FONT).getFontData()[0]) }));

		return provider;
	}

	public ModulesVMNode(AbstractDMVMProvider provider, DsfSession session) {
		super(provider, session, IModuleDMContext.class);

		fLabelProvider = createLabelProvider();
	}

	@Override
	public String toString() {
		return "ModulesVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
	}

	@Override
	protected void updateElementsInSessionThread(final IChildrenUpdate update) {
		IModules modulesService = getServicesTracker().getService(IModules.class);
		final ISymbolDMContext symDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(),
				ISymbolDMContext.class);

		if (modulesService == null || symDmc == null) {
			handleFailedUpdate(update);
			return;
		}

		modulesService.getModules(symDmc,
				new ViewerDataRequestMonitor<IModuleDMContext[]>(getSession().getExecutor(), update) {
					@Override
					public void handleCompleted() {
						if (!isSuccess()) {
							update.done();
							return;
						}
						fillUpdateWithVMCs(update, getData());
						update.done();
					}
				});
	}

	@Override
	protected IDMVMContext createVMContext(IDMContext dmc) {
		return new ModuleVMContext(dmc);
	}

	/*
	 * @since 2.0
	 */
	@Override
	public void update(final IPropertiesUpdate[] updates) {
		try {
			getSession().getExecutor().execute(new DsfRunnable() {
				@Override
				public void run() {
					updatePropertiesInSessionThread(updates);
				}
			});
		} catch (RejectedExecutionException e) {
			for (IPropertiesUpdate update : updates) {
				handleFailedUpdate(update);
			}
		}
	}

	@Override
	public void update(final ILabelUpdate[] updates) {
		fLabelProvider.update(updates);
	}

	/**
	 * @since 2.0
	 */
	@ConfinedToDsfExecutor("getSession().getExecutor()")
	protected void updatePropertiesInSessionThread(final IPropertiesUpdate[] updates) {
		IModules modulesService = getServicesTracker().getService(IModules.class);
		for (final IPropertiesUpdate update : updates) {
			final IModuleDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(),
					IModuleDMContext.class);
			// If either update or service are not valid, fail the update and exit.
			if (modulesService == null || dmc == null) {
				handleFailedUpdate(update);
				return;
			}

			modulesService.getModuleData(dmc,
					new ViewerDataRequestMonitor<IModuleDMData>(getSession().getExecutor(), update) {
						@Override
						protected void handleSuccess() {
							fillModuleDataProperties(update, getData());
							update.done();
						}
					});
		}
	}

	/**
	 * @since 2.0
	 */
	protected void fillModuleDataProperties(IPropertiesUpdate update, IModuleDMData data) {
		update.setProperty(PROP_NAME, data.getName());
		update.setProperty(PROP_IS_LOADED, data.isSymbolsLoaded());
	}

	@Override
	public int getDeltaFlags(Object e) {
		if (e instanceof IRunControl.ISuspendedDMEvent) {
			return IModelDelta.CONTENT;
		}
		return IModelDelta.NO_CHANGE;
	}

	@Override
	public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) {
		if (e instanceof IRunControl.ISuspendedDMEvent) {
			// Create a delta that indicates all groups have changed
			parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);
		}

		rm.done();
	}
}
