package org.eclipse.emf.henshin.interpreter.debug;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResource;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IMemoryBlock;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IThread;
import org.eclipse.emf.henshin.interpreter.matching.conditions.DebugApplicationCondition;

public class HenshinDebugTarget extends HenshinDebugElement implements IDebugTarget {

	private ILaunch launch;
	
	/**
	 * The debug thread (containing the DebugApplicationCondition).
	 * Has to be set separately using {@link #initTarget(DebugApplicationCondition)}.
	 */
	private HenshinDebugThread debugThread;
	
	private IResource moduleResource;
	
	/**
	 * The DebugTarget's name (displays in the debug view)
	 */
	private String name;
	
	private IThread[] threads;
	
	/**
	 * HenshinDebugTarget Constructor. <br>
	 * NOTE: The associated debugThread has to be set separately
	 * using {@link #initTarget(DebugApplicationCondition)}.
	 * @param launch
	 * @param ruleName
	 */
	public HenshinDebugTarget(ILaunch launch, String ruleName) {
		super(null);
		this.launch = launch;
		if (ruleName != null && !ruleName.isEmpty()) {
			this.setName("Debugging Rule \"" + ruleName + "\"");
		} else {
			this.setName("HenshinDebugTarget");
		}
	}
	
	/**
	 * Connects the given DebugApplicationCondition to this DebugTarget
	 * @param applicationCondition the DebugApplicationCondition that will be used for debugging.
	 */
	public void initTarget(DebugApplicationCondition applicationCondition, IResource moduleFile) {
		this.debugThread = new HenshinDebugThread(this, applicationCondition);
		this.moduleResource = moduleFile;
		fireCreationEvent();
	}

	@Override
	public ILaunch getLaunch() {
		return launch;
	}
	
	@Override
	public IDebugTarget getDebugTarget() {
		return this;
	}
	
	public IResource getModuleResource() {
		return this.moduleResource;
	}
	
	@Override
	public boolean canTerminate() {
		return debugThread.canTerminate();
	}

	@Override
	public boolean isTerminated() {
		return debugThread.isTerminated();
	}

	@Override
	public void terminate() throws DebugException {
		debugThread.terminate();
	}

	@Override
	public boolean canResume() {
		return debugThread.canResume();
	}

	@Override
	public boolean canSuspend() {
		return debugThread.canSuspend();
	}

	@Override
	public boolean isSuspended() {
		return debugThread.isSuspended();
	}

	@Override
	public void resume() throws DebugException {
		debugThread.resume();
	}

	@Override
	public void suspend() throws DebugException {
		debugThread.suspend();
	}

	@Override
	public void breakpointAdded(IBreakpoint breakpoint) { }

	@Override
	public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {	}

	@Override
	public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) { }

	@Override
	public boolean canDisconnect() {
		return false;
	}

	@Override
	public void disconnect() throws DebugException { }

	@Override
	public boolean isDisconnected() {
		return false;
	}

	@Override
	public boolean supportsStorageRetrieval() {
		return false;
	}

	@Override
	public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
		return null;
	}

	@Override
	public IProcess getProcess() {
		return null;
	}

	@Override
	public IThread[] getThreads() throws DebugException {
		if (threads == null) {
			threads = new IThread[]{debugThread};
		}
		return threads;
	}

	@Override
	public boolean hasThreads() throws DebugException {
		return true;
	}

	@Override
	public String getName() throws DebugException {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	public boolean supportsBreakpoint(IBreakpoint breakpoint) {
		// maybe add this in the future
		return false;
	}

	public void dispose() {
        fireTerminateEvent();
    }
	
	@Override
	public void fireEvent(DebugEvent event) {
		// All events that are fired on the debug target should be fired on the thread as well.
		try {
			DebugEvent threadEvent = new DebugEvent(getThreads()[0], event.getKind(), event.getDetail());
			if (DebugPlugin.getDefault() != null)
				DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] {event,threadEvent});
		} catch (DebugException e) {
		}
	}
}
