/* Copyright (c) 2012,
 * Mathias Kuhring, KuhringM@rki.de, Robert Koch-Institut, Germany, 
 * All rights reserved. For details, please note the license.txt.
 */

package de.rki.ng4.ipig;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import de.rki.ng4.ipig.data.Exporter;
import de.rki.ng4.ipig.data.GeneSet;
import de.rki.ng4.ipig.data.PeptideSet;
import de.rki.ng4.ipig.exceptions.ExitException;
import de.rki.ng4.ipig.gui.Downloader;
import de.rki.ng4.ipig.gui.GeneControlGui;
import de.rki.ng4.ipig.gui.IpigGui;
import de.rki.ng4.ipig.mapping.Mapper;
import de.rki.ng4.ipig.mapping.Validator;
import de.rki.ng4.ipig.tools.Configurator;
import de.rki.ng4.ipig.tools.Info;
import de.rki.ng4.ipig.tools.Logger;

/**
 * Program entry point, parsing of parameters and loading of configuration file
 * 
 * @author Mathias
 *
 */
public class Ipig {

	private static boolean idmapfile1 = true;
	private static boolean idmapfile2 = true; 

	public static void main (String[] args){
		try {
			// parse cmd input arguments (incl. loading config) and init logging with a new file
			parseArguments(args);

			// check program start
			if (Configurator.getSysProperty("gui", "false").matches("true"))
				IpigGui.main(null);
			else if (Configurator.getSysProperty("control","false").matches("true")){
				new GeneControl().run();
			}
			else if (Configurator.getSysProperty("controlgui", "false").matches("true")){
				GeneControlGui.main(null);
			}
			else if (Configurator.getSysProperty("downloader","false").matches("true")){
				Downloader.main(null);
			}
			else{
				run();
			}
		} 
		catch (ExitException e) {
			System.out.println("\n" + e.getMessage());
		}
		finally{
			System.gc();
		}
	}

	/**
	 * Starts the command-line version of the ipig
	 * 
	 * @throws ExitException
	 */
	public static void run() throws ExitException{
		if (Configurator.getProperty("infomode", "true").matches("false"))
			Info.setInfomode(false);
		else
			Info.setInfomode(true);
		
		checkFiles();

		// init a log file
		Logger.init(Configurator.getSysProperty("msPepSetName", "ipig")+".log", false);

		// init a Exporter
		// this is done already, because is checks some parameters
		Exporter expo = new Exporter();

		// outputPath preparation
		String outputPath = new File(Configurator.getProperty("outputPath","")).getAbsolutePath() + "/";

		Info info = new Info("start ipig");
		System.out.println();

		// read peptides
		PeptideSet mspeps = new PeptideSet(Configurator.getSysProperty("msPepFile"));
		// try some id mappings
		if (idmapfile1)	mspeps.readProteinIds(Configurator.getProperty("protMapFile"));
		if (idmapfile2) mspeps.readFasta(Configurator.getProperty("protSeqFile"));

		Set<String> filter = mspeps.getCommonProts("id").keySet();
		// read genes with filter and do annotation mapping
		GeneSet ucscgenes = new GeneSet(Configurator.getProperty("geneAnnoFile"),filter);
		ucscgenes.readAaSequences(Configurator.getProperty("geneAaSeqFile"));
		Mapper.annotationMapping(mspeps, ucscgenes);
		ucscgenes.removeUnused();
		ucscgenes.deleteAaSequences();

		// read all genes and do alternative mapping
		GeneSet allgenes = new GeneSet(Configurator.getProperty("geneAnnoFile"));
		allgenes.readAaSequences(Configurator.getProperty("geneAaSeqFile"));
		Mapper.alternativeMapping(mspeps, allgenes);
		allgenes.removeUnused();
		allgenes.deleteAaSequences();

		mspeps.sortPeptides();
		if (mspeps.getUnmapped().size() == mspeps.size()){
			throw new ExitException("no peptide mapped");
		}

		// write the output files
		mspeps.writeMapped(outputPath, Configurator.getSysProperty("msPepSetName"));
		mspeps.writeUnmapped(outputPath, Configurator.getSysProperty("msPepSetName"));
		expo.bed(mspeps, outputPath, Configurator.getSysProperty("msPepSetName"));
		expo.gff3(mspeps, outputPath, Configurator.getSysProperty("msPepSetName"));

		//*** This part is just a validation for development testing
		if (Configurator.getProperty("validation", "false").matches("true")){
			ucscgenes.addGenes(allgenes.getAll());
			allgenes = null;
			System.gc();

			int number = 100000;
			GeneSet genespart = new GeneSet(ucscgenes.getRandomSubset(number));
			genespart.readNaSequences(Configurator.getProperty("chromPath"));

			Validator.validate(mspeps, genespart);
		}
		//***

		info.stop();
	}

	// parse the command-line parameters and load the config file
	private static void parseArguments(String[] args) throws ExitException{
		switch (args.length){
		case 0:
			help();
			break;
		case 1:
			if (args[0].matches("-g") || args[0].matches("-gui")){
				try {
					loadConf("ipig.conf");
				}catch(ExitException e){	 // conf file not absolutely necessary for gui
					System.out.print("no configuration loaded");
				}
				Configurator.setSysProperty("gui", "true");
			}
			else if (args[0].matches("-c") || args[0].matches("-control")){
				loadConf("gc.conf");
				Configurator.setSysProperty("control", "true");
			}
			else if (args[0].matches("-cg") || args[0].matches("-controlgui")){
				try {
					loadConf("gc.conf");
				}catch(ExitException e){	// conf file not absolutely necessary for gui
					System.out.print("no configuration loaded");
				}  
				Configurator.setSysProperty("controlgui", "true");
			}
			else if (args[0].matches("-d") || args[0].matches("-downloader")){
				try {
					loadConf("dl.conf");
				}catch(ExitException e){	// conf file not absolutely necessary for gui
					System.out.print("no configuration loaded");
				}  
				Configurator.setSysProperty("downloader", "true");
			}
			else if (args[0].matches("-h") || args[0].matches("-help"))
				help();
			else if (args[0].startsWith("-")){
				throw new ExitException("error: unknown paramerer (" + args[0] + ")");
			}
			else{
				File pepfile = new File(args[0]);
				checkFile(pepfile);
				loadConf("ipig.conf");
				Configurator.setSysProperty("msPepFile", pepfile.getAbsolutePath());
				Configurator.setSysProperty("msPepSetName", pepfile.getName().substring(0,pepfile.getName().lastIndexOf(".")));
			}
			break;
		case 2:
			loadConf(args[1]);
			if (args[0].matches("-g") || args[0].matches("-gui"))
				Configurator.setSysProperty("gui", "true");
			else if (args[0].matches("-c") || args[0].matches("-control"))
				Configurator.setSysProperty("control", "true");
			else if (args[0].matches("-cg") || args[0].matches("-controlgui"))
				Configurator.setSysProperty("controlgui", "true");
			else if (args[0].matches("-d") || args[0].matches("-downloader")){
				Configurator.setSysProperty("downloader", "true");
			}
			else if (args[0].matches("-h") || args[0].matches("-help"))
				help();
			else if (args[0].startsWith("-")){
				throw new ExitException("error: wrong paramerer (" + args[0] + ")");
			}
			else{
				File pepfile = new File(args[0]);
				checkFile(pepfile);
				Configurator.setSysProperty("msPepFile", pepfile.getAbsolutePath());
				Configurator.setSysProperty("msPepSetName", pepfile.getName().substring(0,pepfile.getName().lastIndexOf(".")));
			}
			break;
		default:
			throw new ExitException("error: wrong number of parameters");
		}
	}

	// loads a config file
	private static void loadConf(String filename) throws ExitException{
		File conffile = new File(filename);
		checkFile(conffile);
		Configurator.loadProperties(conffile.getAbsolutePath());
	}

	// prints the help file
	private static void help() throws ExitException{
		try {
			String help = "/help/ipig_help.txt";
			InputStream is = Ipig.class.getResourceAsStream(help);
			BufferedReader helpbf = new BufferedReader(new InputStreamReader(is));
			while (helpbf.ready()){
				System.out.println(helpbf.readLine());
			}
			is.close();
		} catch (IOException e) {
			System.out.println("error: " + e.getLocalizedMessage());
		}
		throw new ExitException("");
	}

	// checks if all necessary files/paths are indicated and if they exist
	private static void checkFiles() throws ExitException{
		StringBuffer message = new StringBuffer();
		boolean failed = false;
		Properties props = Configurator.getProperties();
		if (!props.containsKey("msPepFile")){
			failed = true;
			message.append("no ms peptide input file specified");
		}
		if (!props.containsKey("geneAnnoFile")){
			if (failed) message.append("\n");
			failed = true;
			message.append("no gene annotation file specified");
		}
		if (!props.containsKey("geneAaSeqFile")){
			if (failed) message.append("\n");
			failed = true;
			message.append("no gene aa-sequence file specified");
		}
		if (!props.containsKey("protMapFile")){
			idmapfile1 = false;
			System.out.println("no id mapping file specified\t -> continue without");
		}
		else if(props.getProperty("protMapFile").matches("")){
			Configurator.removeProperty("protMapFile");
			idmapfile1 = false;
			System.out.println("no id mapping file specified\t -> continue without");
		}
		if (!props.containsKey("protSeqFile")){
			idmapfile2 = false;
			System.out.println("no protein fasta file specified\t -> continue without");
		}
		else if(props.getProperty("protSeqFile").matches("")){
			Configurator.removeProperty("protSeqFile");
			idmapfile2 = false;
			System.out.println("no protein fasta file specified\t -> continue without");
		}
		if (props.getProperty("validation", "false").matches("true") && !props.containsKey("chromPath")){
			if (failed) message.append("\n");
			failed = true;
			message.append("no chromosome path specified");
		}
		if (props.containsKey("outputPath") && props.getProperty("outputPath").matches("")){
			Configurator.removeProperty("outputPath");
		}
		if (failed) throw new ExitException(message.toString());

		for (Entry<Object, Object> entry : Configurator.getProperties().entrySet()){
			if (((String) entry.getKey()).contains("File")){
				File test = new File((String) entry.getValue());
				checkFile(test);
			}
			if (((String) entry.getKey()).contains("Path")){
				File test = new File((String) entry.getValue());
				if (!test.isDirectory()){
					throw new ExitException("error: can't find path (" + test.getAbsolutePath() + ")");
				}
			}
		}
	}

	// a simple file check
	private static void checkFile(File test) throws ExitException{
		if (!test.isFile()){
			throw new ExitException("error: can't find file (" + test.getAbsolutePath() + ")");
		}
	}
}
