package sbt

	import complete.Parser
	import Def.{Initialize, ScopedKey}
	import std.TaskExtra.{task => mktask, _}
	import Task._
	import Types._

/** Parses input and produces a task to run.  Constructed using the companion object. */
final class InputTask[T] private(val parser: State => Parser[Task[T]])
{
	def mapTask[S](f: Task[T] => Task[S]): InputTask[S] =
		new InputTask[S](s => parser(s) map f)

	def partialInput(in: String): InputTask[T] =
		new InputTask[T](s => Parser(parser(s))(in) )

	def fullInput(in: String): InputTask[T] = new InputTask[T]( s =>
		Parser.parse(in, parser(s)) match {
			case Right(v) => Parser.success(v)
			case Left(msg) =>
				val indented = msg.lines.map("   " + _).mkString("\n")
				Parser.failure(s"Invalid programmatic input:\n$indented")
		}
	)
}

object InputTask
{
	implicit class InitializeInput[T](i: Initialize[InputTask[T]]) {
		def partialInput(in: String): Initialize[InputTask[T]] = i(_ partialInput in)
		def fullInput(in: String): Initialize[InputTask[T]] = i(_ fullInput in)

			import std.FullInstance._
		def toTask(in: String): Initialize[Task[T]] = flatten(
			(Def.stateKey zipWith i) ( (sTask, it) =>
				sTask map ( s =>
					Parser.parse(in, it.parser(s)) match {
						case Right(t) => Def.value(t)
						case Left(msg) =>
							val indented = msg.lines.map("   " + _).mkString("\n")
							sys.error(s"Invalid programmatic input:\n$indented")
					}
				)
			)
		)
	}

	implicit def inputTaskParsed[T](in: InputTask[T]): std.ParserInputTask[T] = ???
	implicit def inputTaskInitParsed[T](in: Initialize[InputTask[T]]): std.ParserInputTask[T] = ???

	def make[T](p: State => Parser[Task[T]]): InputTask[T] = new InputTask[T](p)

	def static[T](p: Parser[Task[T]]): InputTask[T] = free(_ => p)

	def static[I,T](p: Parser[I])(c: I => Task[T]): InputTask[T] = static(p map c)

	def free[T](p: State => Parser[Task[T]]): InputTask[T] = make(p)

	def free[I,T](p: State => Parser[I])(c: I => Task[T]): InputTask[T] = free(s => p(s) map c)

	def separate[I,T](p: State => Parser[I])(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] =
		separate(Def value p)(action)

	def separate[I,T](p: Initialize[State => Parser[I]])(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] =
		p.zipWith(action)((parser, act) => free(parser)(act))

	/** Constructs an InputTask that accepts no user input. */
	def createFree[T](action: Initialize[Task[T]]): Initialize[InputTask[T]] =
		action { tsk => free(emptyParser)( const(tsk) ) }

	/** Constructs an InputTask from:
	*  a) a Parser constructed using other Settings, but not Tasks
	*  b) a dynamically constructed Task that uses Settings, Tasks, and the result of parsing. */
	def createDyn[I,T](p: Initialize[State => Parser[I]])(action: Initialize[Task[I => Initialize[Task[T]]]]): Initialize[InputTask[T]] =
		separate(p)(std.FullInstance.flattenFun[I,T](action))

	/** A dummy parser that consumes no input and produces nothing useful (unit).*/
	def emptyParser: State => Parser[Unit] = Types.const(complete.DefaultParsers.success(()))

	/** Implementation detail that is public because it is used by a macro.*/
	def parserAsInput[T](p: Parser[T]): Initialize[State => Parser[T]] = Def.valueStrict(Types.const(p))

	/** Implementation detail that is public because it is used y a macro.*/
	def initParserAsInput[T](i: Initialize[Parser[T]]): Initialize[State => Parser[T]] = i(Types.const)



	@deprecated("Use another InputTask constructor or the `Def.inputTask` macro.", "0.13.0")
	def apply[I,T](p: Initialize[State => Parser[I]])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] =
	{
		val dummyKey = localKey[Task[I]]
		val (marker, dummy) = dummyTask[I]
		val it = action(TaskKey(dummyKey)) mapConstant subResultForDummy(dummyKey, dummy)
		val act = it { tsk => (value: I) => subForDummy(marker, value, tsk) }
		separate(p)(act)
	}

	@deprecated("Use another InputTask constructor or the `Def.inputTask` macro.", "0.13.0")
	def apply[I,T](p: State => Parser[I])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] =
		apply(Def.value(p))(action)

	/** The proper solution is to have a Manifest context bound and accept slight source incompatibility,
	* The affected InputTask construction methods are all deprecated and so it is better to keep complete
	* compatibility.  Because the AttributeKey is local, it uses object equality and the manifest is not used. */
	private[this] def localKey[T]: AttributeKey[T] = AttributeKey.local[Unit].asInstanceOf[AttributeKey[T]]

	private[this] def subResultForDummy[I](dummyKey: AttributeKey[Task[I]], dummyTask: Task[I]) = 
		new (ScopedKey ~> Option) { def apply[T](sk: ScopedKey[T]) =
			if(sk.key eq dummyKey) {
				// sk.key: AttributeKey[T], dummy.key: AttributeKey[Task[I]]
				// (sk.key eq dummy.key) ==> T == Task[I] because AttributeKey is invariant
				Some(dummyTask.asInstanceOf[T])
			} else
				None
		}

	private[this] def dummyTask[I]: (AttributeKey[Option[I]], Task[I]) =
	{
		val key = localKey[Option[I]]
		val f: () => I = () => sys.error(s"Internal sbt error: InputTask stub was not substituted properly.")
		val t: Task[I] = Task(Info[I]().set(key, None), Pure(f, false))
		(key, t)
	}
	private[this] def subForDummy[I, T](marker: AttributeKey[Option[I]], value: I, task: Task[T]): Task[T] =
	{
		val seen = new java.util.IdentityHashMap[Task[_], Task[_]]
		lazy val f: Task ~> Task = new (Task ~> Task) { def apply[T](t: Task[T]): Task[T] =
		{
			val t0 = seen.get(t)
			if(t0 == null) {
				val newAction = 
					if(t.info.get(marker).isDefined)
						Pure(() => value.asInstanceOf[T], inline = true)
					else
						t.work.mapTask(f)
				val newTask = Task(t.info, newAction)
				seen.put(t, newTask)
				newTask
			} else
				t0.asInstanceOf[Task[T]]
		}}
		f(task)
	}
}