/* sbt -- Simple Build Tool
 * Copyright 2009 Mark Harrah
 */
package sbt.extract

import java.io.{ByteArrayOutputStream, File}
import FileUtilities.{classLocationFile, clean, createTemporaryDirectory, download, transferAndClose, unzip, write, zip}
import SelfExtractingProject.{flat, separator}

trait SelfExtractingProject extends Project
{
	protected def createSelfExtractingJar(actions: List[String], jvmOptions: List[String], projectZip: Path, outputJar: Path): Option[String] =
	{
		def jarForClass(name: String) = Path.fromFile(classLocationFile(Class.forName(name)))
		val loaderJar = jarForClass("xsbti.Launcher")
		val bytes = new ByteArrayOutputStream
		transferAndClose(this.getClass.getResourceAsStream("extract.location"), bytes, log) orElse
		{
			val extractorJarLocation = bytes.toString("UTF-8")
			createSelfExtractingJar(actions, jvmOptions, projectZip, loaderJar, extractorJarLocation, outputJar)
		}
	}
	private def createSelfExtractingJar(actions: List[String], jvmOptions: List[String], projectZip: Path, loaderJar: Path, extractorJarLocation: String, outputJar: Path): Option[String] =
	{
		val installContents = jvmOptions.mkString("\n") + separator + actions.mkString("\n")
		withTemporaryDirectory(log) { tmp =>
			val tmpPath = Path.fromFile(tmp)
			write(new File(tmp, "install"), installContents, log) orElse
			unzip(this.getClass.getResource(extractorJarLocation), tmpPath, log).left.toOption orElse
			Control.thread(compressLoader(loaderJar)) { compressedLoader =>
				zip( (tmpPath ###) :: flat(projectZip) :: compressedLoader :: Nil, outputJar, true, log)
			}
		}
	}
	private def withTemporaryDirectory(log: Logger)(f: File => Option[String]) =
	{
		Control.thread(createTemporaryDirectory(log)) { dir =>
			Control.trapUnitAndFinally("", log)
				{ f(dir) }
				{ clean(Path.fromFile(dir) :: Nil, true, log) }
		}
	}
	private def compressLoader(loaderJar: Path): Either[String, Path] =
	{
		val jarName = loaderJar.asFile.getName
		val dotIndex = jarName.lastIndexOf('.')
		val baseName =
			if(dotIndex > 0) jarName.substring(0, dotIndex)
			else jarName
		val packedName = baseName + ".pack"
		val packed = outputPath / packedName
		val packedAndGzip = (outputPath ###) / (packedName + ".gz")
		val result =
			Pack.pack(loaderJar, packed, log) orElse
			FileUtilities.gzip(packed, packedAndGzip, log)
		result.toLeft(packedAndGzip)
	}
}
trait BasicSelfExtractingProject extends BasicScalaProject with SelfExtractingProject
{
	def installActions: List[String] = update.name :: `package`.name :: Nil
	def jvmOptions: List[String] = Nil
	def selfExtractingJar: Path = outputPath / (artifactBaseName + "-setup.jar")
	
	lazy val installer = installerAction
	def installerAction = task { createSelfExtractingJar(installActions, jvmOptions, packageProjectZip, selfExtractingJar) } dependsOn packageProject
}

object SelfExtractingProject
{
	// keep this in sync with sbt.extract.Main.separator
	def separator = "===================="
	private def flat(p: Path) =
		p match
		{
			case rp: RelativePath => (rp.parentPath ###) / rp.component
			case _ => p
		}
}