/*******************************************************************************
 * Copyright (c) 2013 Ericsson and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Alvaro Sanchez-Leon (Ericsson AB) - Each memory context needs a different MemoryRetrieval (Bug 250323)
 *******************************************************************************/
package org.eclipse.cdt.dsf.debug.internal.provisional.model;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.model.DsfMemoryBlockRetrieval;
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent;
import org.eclipse.cdt.dsf.internal.DsfPlugin;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.IMemoryBlockManager;
import org.eclipse.debug.core.model.IMemoryBlock;
import org.eclipse.debug.core.model.IMemoryBlockRetrieval;

/* (non-Javadoc)
 * @see org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockRetrievalManager
 */
public class MemoryBlockRetrievalManager implements IMemoryBlockRetrievalManager {

	private final String fModelId;
	private final DsfSession fSession;
	private final ILaunchConfiguration fLaunchConfig;
	private final Map<IMemoryDMContext, IMemoryBlockRetrieval> fMapMemDMCToBlockRetrieval = new HashMap<IMemoryDMContext, IMemoryBlockRetrieval>();

	/**
	 * Constructor
	 */
	public MemoryBlockRetrievalManager(String modelId, ILaunchConfiguration config, DsfSession session) {
		fModelId = modelId;
		fSession = session;
		fLaunchConfig = config;
	}

	@DsfServiceEventHandler
	public void eventDispatched(IStartedDMEvent event) {
		// If a new memory context is starting, create its memory retrieval instance
		if (event.getDMContext() instanceof IMemoryDMContext) {
			IMemoryDMContext memDmc = (IMemoryDMContext) event.getDMContext();
			if (!fMapMemDMCToBlockRetrieval.containsKey(memDmc)) {
				// We need a new memory retrieval for this new memory context
				IMemoryBlockRetrieval memRetrieval = null;
				try {
					memRetrieval = createMemoryBlockRetrieval(fModelId, fLaunchConfig, fSession);
				} catch (DebugException e) {
					DsfPlugin.getDefault().getLog().log(e.getStatus());
					return;
				}

				if (memRetrieval != null) {
					if (memRetrieval instanceof DsfMemoryBlockRetrieval) {
						((DsfMemoryBlockRetrieval) memRetrieval).initialize(memDmc);
					}

					fMapMemDMCToBlockRetrieval.put(memDmc, memRetrieval);
				}
			}
		}
	}

	@DsfServiceEventHandler
	public void eventDispatched(IExitedDMEvent event) {
		// If a memory context is exiting, save expressions and clean its used resources
		if (event.getDMContext() instanceof IMemoryDMContext) {
			IMemoryDMContext memDmc = (IMemoryDMContext) event.getDMContext();
			// Remove entry if it exists
			final IMemoryBlockRetrieval retrieval = fMapMemDMCToBlockRetrieval.remove(memDmc);
			if (retrieval != null) {
				if (retrieval instanceof DsfMemoryBlockRetrieval) {
					((DsfMemoryBlockRetrieval) retrieval).saveMemoryBlocks();
				}
				// Fire a terminate event for the memory retrieval object so
				// that the hosting memory views can clean up. See 255120 and
				// 283586
				DebugPlugin.getDefault().fireDebugEventSet(
						new DebugEvent[] { new DebugEvent(retrieval, DebugEvent.TERMINATE) });

				Job removeJob = new Job("Removing memory blocks") { //$NON-NLS-1$

					@Override
					protected IStatus run(IProgressMonitor monitor) {
						IMemoryBlockManager mbm = DebugPlugin.getDefault().getMemoryBlockManager();
						IMemoryBlock[] deletedMemoryBlocks = mbm.getMemoryBlocks(retrieval);
						mbm.removeMemoryBlocks(deletedMemoryBlocks);
						return Status.OK_STATUS;
					}
				};
				removeJob.schedule();
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockRetrievalManager#getMemoryBlockRetrieval(org.eclipse.cdt.dsf.datamodel.IDMContext)
	 */
	@Override
	public IMemoryBlockRetrieval getMemoryBlockRetrieval(IDMContext dmc) {
		IMemoryBlockRetrieval memRetrieval = null;
		IMemoryDMContext memDmc = DMContexts.getAncestorOfType(dmc, IMemoryDMContext.class);
		if (memDmc != null) {
			memRetrieval = fMapMemDMCToBlockRetrieval.get(memDmc);
		}

		return memRetrieval;
	}

	protected IMemoryBlockRetrieval createMemoryBlockRetrieval(String model, ILaunchConfiguration config,
			DsfSession session) throws DebugException {
		return new DsfMemoryBlockRetrieval(model, config, session);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockRetrievalManager#dispose()
	 */
	@Override
	public void dispose() {
		// release resources / references
		fMapMemDMCToBlockRetrieval.clear();
	}
}
