
# Generic class to download a genome
class DownloadGenome {
	string className		# This class' short name
	string genome			# Genome as appeard in SnpEff's config file (it usually includes relase / version number)
	string genomeNoVer		# Genome name without any version / release 
	string version			# Genom version
	string downloadDir		# Where to download the files
	string downloadDirOri	# Where to download the original files, if there is a transformation needed
	string dirDataGenome	# SnpEff's data directory for this genome
	string cdsUrl, fastaUrl, genesUrl, protUrl	# Source URLs
	string cdsLocal, fastaLocal, genesLocal, protLocal	# Local downlaod files
	string cdsData, fastaData, genesData, protData	# SnpEff 'data' files
	string fastaDataEq		# This genome fasta is equivalent, if present, create a link (typical is the major name of the genome without version, e.g. 'hg38')
	string genesExt			# Genes file extension ('gtf', 'gff', genBank', etc.)

	void DownloadGenome(string genome) {
		this.genome = genome
		dirDataGenome = "$dirData/$genome"

		# Get class (short) name to infer download dir (remove the 'Download' prefix to the class name)
		#className = type(this).toLower()
		#if( className.startsWith('download') ) className = className.substr('download'.length())

		downloadDir = "$dirDownload/$className/$genome"

		# Genome name without version number
		setGenomeAndVersion()
	}

	# If the referce FASTA file 'fastaDataEq' exists, create a link
	void checkRefrenceFasta() {
		if( !fastaDataEq.exists() || fastaData.exists() ) return
		log("Download '$genome': FASTA refernce file '$fastaDataEq' exists, linking")
		sys ln -s '$fastaDataEq' '$fastaData'
	}

	# Download and install all genomic files for 'genome'
	void download() {
		log("Download '$genome', using download dir '$downloadDir'")
		init()

		checkRefrenceFasta()

		# Download files (check if data is already downloaded)
		fastaLocal = downloadFastaCheck()
		genesLocal = downloadGenesCheck()
		cdsLocal = downloadCdsFastaCheck()
		protLocal = downloadProteinFastaCheck()
		other := downloadOther()
		wait

		# Process files
		preProcess()
		if( !genesLocal.isEmpty() ) genesLocal = processGenes(genesLocal)
		if( !fastaLocal.isEmpty() ) fastaLocal = processFasta(fastaLocal)
		if( !cdsLocal.isEmpty() )	cdsLocal = processCds(cdsLocal)
		if( !protLocal.isEmpty() )	protLocal = processProt(protLocal)

		# Install files
		install(genesLocal, dirDataGenome + "/genes.$genesExt\.gz")
		install(fastaLocal, fastaData)
		install(cdsLocal, cdsData)
		install(protLocal, protData)
		for(string f: other) {
			install(f, dirDataGenome + '/' + f.baseName())
		}
	}
	
	# Download (and build) 'cds' FASTA file to local download dir
	string downloadCdsFasta() {
		return downloadFile(cdsUrl, cdsLocal, "CDS FASTA", false)
	}

	# Download (and build) 'cds' FASTA file to local download dir
	# Returns: Path to file
	string downloadCdsFastaCheck() {
		if( cdsData.exists() ) {
			log("Download CDS FASTA: Data file '$cdsData' already exists, skipping")
			return ''
		}
		if( cdsLocal.exists() ) {
			log("Download CDS FASTA: Data file '$cdsLocal' already exists, skipping")
			return cdsLocal
		}
		return downloadCdsFasta()
	}

	# Download (and build) reference genome FASTA file to local download dir
	string downloadFasta() {
		return downloadFile(fastaUrl, fastaLocal, "FASTA reference", false)
	}

	# Download (and build) reference genome FASTA file to local download dir
	# Returns: Path to fasta file or empty if no download is needed
	string downloadFastaCheck() {
		if( fastaData.exists() ) {
			log("Download Reference FASTA: Data file '$fastaData' already exists, skipping")
			return ''
		}
		if( fastaLocal.exists() ) {
			log("Download Reference FASTA: Data file '$fastaLocal' already exists, skipping")
			return fastaLocal
		}
		return downloadFasta()
	}

	# Download a file
	string downloadFile(string url, string localFile, string title, bool doWait) {
		log("Downloading '$genome' $title: URL '$url', file '$localFile'")
		if( url.isEmpty() ) {
			log("Downloading '$genome' $title: Empty URL, skipping")
			return ''
		}
		if( localFile.isEmpty() ) {
			log("Downloading '$genome' $title: Empty local file, skipping")
			return ''
		}
		if( localFile.exists() ) {
			println "Local file '$localFile' exists, skipping download"
			return localFile
		}

		if( wget(url, localFile, doWait, false, false) ) return localFile
		error "Could not download remote file '$url'";
	}

	# Download (and build) 'genes' file to local download dir
	string downloadGenes() {
		return downloadFile(genesUrl, genesLocal, "Genes", false)
	}

	# Download (and build) 'genes' file to local download dir
	# Returns: Path to file (GTF, GFF, RefSeq, GeneBank, etc.)
	string downloadGenesCheck() {
		if( genesData.exists() ) {
			log("Download Genes: Data file '$genesData' already exists, skipping")
			return ''
		}
		if( genesLocal.exists() ) {
			log("Download Genes: Data file '$genesLocal' already exists, skipping")
			return genesLocal
		}

		return downloadGenes()
	}

	# Download 'other' files
	# Returns: A list of all paths
	string[] downloadOther() {
		string[] files
		# Override this method if you need to download "other" files
		return files
	}

	# Download (and build) 'protein' FASTA file to local download dir
	string downloadProteinFasta() {
		return downloadFile(protUrl, protLocal, "Protein FASTA", false)
	}

	# Download (and build) 'protein' FASTA file to local download dir
	# Returns: Path to file
	string downloadProteinFastaCheck() {
		if( protData.exists() ) {
			log("Download Protein FASTA: Data file '$protData' already exists, skipping")
			return ''
		}
		if( protLocal.exists() ) {
			log("Download Protein FASTA: Data file '$protLocal' already exists, skipping")
			return protLocal
		}
		return downloadProteinFasta()
	}

	# Initialize default values
	void init() {
		cdsLocal = cdsUrl.isEmpty() ? '' : downloadDir + '/' + cdsUrl.baseName()
		fastaLocal = fastaUrl.isEmpty() ? '' : downloadDir + '/' + fastaUrl.baseName()
		genesLocal = genesUrl.isEmpty() ? '' : downloadDir + '/' + genesUrl.baseName()
		protLocal = protUrl.isEmpty() ? '' : downloadDir + '/' + protUrl.baseName()

		genesExt = genesExt.isEmpty() ? 'gtf' : genesExt

		if( genesData.isEmpty() )	genesData = dirDataGenome + "/genes.$genesExt\.gz"
		if( fastaData.isEmpty() )	fastaData = "$dirData/genomes/$genome\.fa.gz"
		if( fastaDataEq.isEmpty() )	fastaDataEq = "$dirData/genomes/$genomeNoVer\.fa.gz"
		if( cdsData.isEmpty() )	cdsData = dirDataGenome + "/cds.fa.gz"
		if( protData.isEmpty() )	protData = dirDataGenome + "/proteing.fa.gz"

		dirDataGenome.mkdir()
		downloadDir.mkdir()
		downloadDir.chdir()
	}
	
	# Install a file in 'dstDir' directory
	void install(string src, string dst) {
		if( src.isEmpty() ) return
		log("Installing file '$src' to '$dst'")
		dst.dirName().mkdir()
		task( dst <- src ) sys cp -vf '$src' '$dst'
	}

	# Pre-Process (default, do nothing)
	void preProcess()	return 

	# Process CDS file (default, do nothing)
	string processCds(string cds)	return cds

	# Process FASTA file (default, do nothing)
	string processFasta(string fa)	return fa

	# Process genes file (default, do nothing)
	string processGenes(string genes)	return genes

	# Process protein file (default, do nothing)
	string processProt(string prot)	return prot

	# Set genomeNoVer (i.e. genome name without version number) and version number
	# This is the name of the genome, with "major" number but no version (e.g. 'GRCh38' instead of 'GRCh38.103')
	void setGenomeAndVersion() {
		string[] gv

		if( genome.indexOf('.') > 0 ) gv = genome.split('\.')
		else if( genome.indexOf('_') > 0 ) gv = genome.split('_')

		if( gv.isEmpty() ) (genomeNoVer, version) = [genome, '']
		else (genomeNoVer, version) = gv

		log("Download genome '$genome': genomeNoVer '$genomeNoVer' version '$version'")
	}
}

