/*******************************************************************************
 * Copyright (c) 2000, 2006 QNX Software Systems 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:
 *     QNX Software Systems - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.debug.mi.core.cdi;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import org.eclipse.cdt.debug.core.cdi.CDIException;
import org.eclipse.cdt.debug.core.cdi.model.ICDISignal;
import org.eclipse.cdt.debug.mi.core.MIException;
import org.eclipse.cdt.debug.mi.core.MISession;
import org.eclipse.cdt.debug.mi.core.RxThread;
import org.eclipse.cdt.debug.mi.core.cdi.model.Signal;
import org.eclipse.cdt.debug.mi.core.cdi.model.Target;
import org.eclipse.cdt.debug.mi.core.command.CommandFactory;
import org.eclipse.cdt.debug.mi.core.command.CLIHandle;
import org.eclipse.cdt.debug.mi.core.command.CLIInfoSignals;
import org.eclipse.cdt.debug.mi.core.event.MIEvent;
import org.eclipse.cdt.debug.mi.core.event.MISignalChangedEvent;
import org.eclipse.cdt.debug.mi.core.output.CLIInfoSignalsInfo;
import org.eclipse.cdt.debug.mi.core.output.MISigHandle;

/**
 */
public class SignalManager extends Manager  {

	ICDISignal[] EMPTY_SIGNALS = {};
	MISigHandle[] noSigs =  new MISigHandle[0];
	Map signalsMap;

	public SignalManager(Session session) {
		super(session, false);
		signalsMap = new Hashtable();
	}

	synchronized List getSignalsList(Target target) {
		List signalsList = (List)signalsMap.get(target);
		if (signalsList == null) {
			signalsList = Collections.synchronizedList(new ArrayList());
			signalsMap.put(target, signalsList);
		}
		return signalsList;
	}

	MISigHandle[] getMISignals(MISession miSession) throws CDIException {
		MISigHandle[] miSigs;
		CommandFactory factory = miSession.getCommandFactory();
		CLIInfoSignals sigs = factory.createCLIInfoSignals();
		try {
			RxThread rxThread = miSession.getRxThread();
			rxThread.setEnableConsole(false);
			miSession.postCommand(sigs);
			CLIInfoSignalsInfo info = sigs.getMIInfoSignalsInfo();
			if (info == null) {
				throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$
			}
			miSigs =  info.getMISignals();
		} catch (MIException e) {
			throw new MI2CDIException(e);
		} finally {
			RxThread rxThread = miSession.getRxThread();
			rxThread.setEnableConsole(true);
		}
		return miSigs;
	}

	MISigHandle getMISignal(MISession miSession, String name) throws CDIException {
		MISigHandle sig = null;
		CommandFactory factory = miSession.getCommandFactory();
		CLIInfoSignals sigs = factory.createCLIInfoSignals(name);
		try {
			RxThread rxThread = miSession.getRxThread();
			rxThread.setEnableConsole(false);
			miSession.postCommand(sigs);
			CLIInfoSignalsInfo info = sigs.getMIInfoSignalsInfo();
			if (info == null) {
				throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$
			}
			MISigHandle[] miSigs =  info.getMISignals();
			if (miSigs.length > 0) {
				sig = miSigs[0];
			}
		} catch (MIException e) {
			throw new MI2CDIException(e);
		} finally {
			RxThread rxThread = miSession.getRxThread();
			rxThread.setEnableConsole(true);
		}
		return sig;
	}

	/**
	 * Method hasSignalChanged.
	 * @param sig
	 * @param mISignal
	 * @return boolean
	 */
	private boolean hasSignalChanged(ICDISignal sig, MISigHandle miSignal) {
		return !sig.getName().equals(miSignal.getName()) ||
			sig.isStopSet() != miSignal.isStop() ||
			sig.isIgnore() != !miSignal.isPass();
	}

	protected ICDISignal findSignal(Target target, String name) {
		ICDISignal sig = null;
		List signalsList = (List) signalsMap.get(target);
		if (signalsList != null) {
			ICDISignal[] sigs = (ICDISignal[])signalsList.toArray(new ICDISignal[0]);
			for (int i = 0; i < sigs.length; i++) {
				if (sigs[i].getName().equals(name)) {
					sig = sigs[i];
					break;
				}
			}
		}
		return sig;
	}

	public ICDISignal getSignal(MISession miSession, String name) {
		Session session = (Session)getSession();
		Target target = session.getTarget(miSession);
		return getSignal(target, name);
	}
	public ICDISignal getSignal(Target target, String name) {
		ICDISignal sig = findSignal(target, name);
		if (sig == null) {
			MISigHandle miSig = null;
			try {
				miSig = getMISignal(target.getMISession(), name);
				sig = new Signal(target, miSig);
				List signalsList = getSignalsList(target);
				signalsList.add(sig);
			} catch (CDIException e) {
				// The session maybe terminated because of the signal.
				miSig = new MISigHandle(name, false, false, false, name);
				sig = new Signal(target, miSig);
			}
		}
		return sig;
	}

	public void handle(Signal sig, boolean isIgnore, boolean isStop) throws CDIException {
		Target target = (Target)sig.getTarget();
		MISession miSession = target.getMISession();
		CommandFactory factory = miSession.getCommandFactory();
		StringBuffer buffer = new StringBuffer(sig.getName());
		buffer.append(" "); //$NON-NLS-1$
		if (isIgnore) {
			buffer.append("ignore"); //$NON-NLS-1$
		} else {
			buffer.append("noignore"); //$NON-NLS-1$
		}
		buffer.append(" "); //$NON-NLS-1$
		if (isStop) {
			buffer.append("stop"); //$NON-NLS-1$
		} else  {
			buffer.append("nostop"); //$NON-NLS-1$
		}
		CLIHandle handle = factory.createCLIHandle(buffer.toString());
		try {
			miSession.postCommand(handle);
			handle.getMIInfo();
		} catch (MIException e) {
			throw new MI2CDIException(e);
		}
		sig.getMISignal().handle(isIgnore, isStop);
		miSession.fireEvent(new MISignalChangedEvent(miSession, sig.getName()));
	}

	public ICDISignal[] getSignals(Target target) throws CDIException {
		List signalsList = (List)signalsMap.get(target);
		if (signalsList == null) {
			update(target);
		}
		signalsList = (List)signalsMap.get(target);
		if (signalsList != null) {
			return (ICDISignal[])signalsList.toArray(new ICDISignal[0]);
		}
		return EMPTY_SIGNALS;
	}

	public void update(Target target) throws CDIException {
		MISession miSession = target.getMISession();
		MISigHandle[] miSigs = getMISignals(miSession);
		List eventList = new ArrayList(miSigs.length);
		List signalsList = getSignalsList(target);
		for (int i = 0; i < miSigs.length; i++) {
			ICDISignal sig = findSignal(target, miSigs[i].getName());
			if (sig != null) {
				if (hasSignalChanged(sig, miSigs[i])) {
					// Fire ChangedEvent
					((Signal)sig).setMISignal(miSigs[i]);
					eventList.add(new MISignalChangedEvent(miSession, miSigs[i].getName())); 
				}
			} else {
				// add the new breakpoint and fire CreatedEvent
				signalsList.add(new Signal(target, miSigs[i]));
				//eventList.add(new MISignCreatedEvent(miSession, miSigs[i].getName()));
			}
		}
		MIEvent[] events = (MIEvent[])eventList.toArray(new MIEvent[0]);
		miSession.fireEvents(events);
	}

} 
