package sbt

object Signals
{
	val CONT = "CONT"
	val INT = "INT"
	def withHandler[T](handler: () => Unit, signal: String = INT)(action: () => T): T =
	{
		val result = 
			try
			{
				val signals = new Signals0
				signals.withHandler(signal, handler, action)
			}
			catch { case e: LinkageError => Right(action()) }

		result match {
			case Left(e) => throw e
			case Right(v) => v
		}
	}

	/** Helper interface so we can expose internals of signal-isms to others. */
	sealed trait Registration {
		def remove(): Unit
	}
    /** Register a signal handler that can be removed later. 
     * NOTE: Does not stack with other signal handlers!!!!
     */
    def register(handler: () => Unit, signal: String = INT): Registration =
    	// TODO - Maybe we can just ignore things if not is-supported.
    	if(supported(signal)) {
	    	import sun.misc.{Signal,SignalHandler}
	    	val intSignal = new Signal(signal)
	    	val newHandler = new SignalHandler {
				def handle(sig: Signal) { handler() }
			}
			val oldHandler = Signal.handle(intSignal, newHandler)
			object unregisterNewHandler extends Registration {
				override def remove(): Unit = {
					Signal.handle(intSignal, oldHandler)
				}
			}
			unregisterNewHandler
		} else {
			// TODO - Maybe we should just throw an exception if we don't support signals...
			object NullUnregisterNewHandler extends Registration {
				override def remove(): Unit = ()
			}
			NullUnregisterNewHandler
		}


	def supported(signal: String): Boolean =
		try
		{
			val signals = new Signals0
			signals.supported(signal)
		}
		catch { case e: LinkageError => false }
}

// Must only be referenced using a
//   try { } catch { case e: LinkageError => ... }
// block to 
private final class Signals0
{
	def supported(signal: String): Boolean =
	{
			import sun.misc.Signal
		try { new Signal(signal); true }
		catch { case e: IllegalArgumentException => false }
	}

	// returns a LinkageError in `action` as Left(t) in order to avoid it being
	// incorrectly swallowed as missing Signal/SignalHandler
	def withHandler[T](signal: String, handler: () => Unit, action: () => T): Either[Throwable, T] =
	{
			import sun.misc.{Signal,SignalHandler}
		val intSignal = new Signal(signal)
		val newHandler = new SignalHandler {
			def handle(sig: Signal) { handler() }
		}	

		val oldHandler = Signal.handle(intSignal, newHandler)

		try Right(action())
		catch { case e: LinkageError => Left(e) }
		finally Signal.handle(intSignal, oldHandler)
	}
}