/*  
 *  src/org/metaqtl/main/MetaMain.java
 * 
 *  $Author: veyrieras $, $Date: 2006-10-17 15:41:54 $, $Version$
 *
 *  MetaQTL : a Java package for QTL Meta-Analysis
 * 
 *  Copyright (C) 2003-2006  Jean-Baptiste Veyrieras, INRA, France.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * 
 */
package org.metaqtl.main;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;

import javax.imageio.ImageIO;

import org.metaqtl.ChromCluster;
import org.metaqtl.Chromosome;
import org.metaqtl.IMetaQtlConstants;
import org.metaqtl.MetaDico;
import org.metaqtl.MetaQtlAnalysis;
import org.metaqtl.MetaQtlData;
import org.metaqtl.MetaQtlModel;
import org.metaqtl.Qtl;
import org.metaqtl.QtlPartition;
import org.metaqtl.adapter.QtlPartitionAdapter;
import org.metaqtl.bio.IBioGenome;
import org.metaqtl.bio.IBioLGroup;
import org.metaqtl.bio.IBioLocus;
import org.metaqtl.bio.IBioOntology;
import org.metaqtl.bio.entity.factory.BioEntityFactory;
import org.metaqtl.bio.entity.factory.MapChartFactory;
import org.metaqtl.bio.entity.factory.MapMakerFactory;
import org.metaqtl.bio.entity.factory.MapTabFactory;
import org.metaqtl.bio.entity.factory.OntologyTabFactory;
import org.metaqtl.bio.entity.factory.XmlBioEntityFactory;
import org.metaqtl.bio.entity.factory.XmlGeneticMapFactory;
import org.metaqtl.bio.entity.factory.XmlOntologyFactory;
import org.metaqtl.factory.DubiousLocusFactory;
import org.metaqtl.factory.MetaDicoFactory;
import org.metaqtl.factory.MetaGraphParFactory;
import org.metaqtl.factory.MetaQtlAnalysisFactory;
import org.metaqtl.factory.MetaQtlModelFactory;
import org.metaqtl.graph.ChromGraph;
import org.metaqtl.graph.GraphFactory;

/**
 * 
 */
public abstract class MetaMain {
	
	protected CmdLineParser parser;
	
	private CmdLineParser.Option averbose;
	
	private CmdLineParser.Option ahelp;
	
	protected Boolean verbose;
	
	protected Boolean help;
	
	public abstract void printUsage();
	
	public abstract void printHelp();
	
	public void initCmdLineParser() {
		parser		   = new CmdLineParser();
		// Add default option
		averbose  = parser.addBooleanOption('v', "verbose");
	    ahelp	  = parser.addBooleanOption('h', "help");
	}
	
	public void parseCmdLine(String[] args) {
		
		if (args.length == 0) {
			printUsage();
			System.exit(1);
		}
		try {
	        parser.parse(args);
	    }
	    catch (CmdLineParser.OptionException e) {
	        System.err.println(e.getMessage());
	        printUsage();
	        System.exit(1);
	    }
	    verbose  =  (Boolean)parser.getOptionValue(averbose, new Boolean(false));
	    help     =  (Boolean)parser.getOptionValue(ahelp, new Boolean(false));
	    if (help.booleanValue()) {
	    	printHelp();
	    	System.exit(0);
	    }
	}
	
	public static void generalHelp() {
		System.out.println("-v, --verbose : verbose mode");
        System.out.println("-h, --help    : display help");
	}

	/**
	 * @return
	 */
	public static String generalUsage() {
		return "[{-v, --verbose}]] [{-h, --help}]]";
	}
	
	/**
	 * @param genome
	 * @param format
	 * @param outFile
	 */
	protected static void writeMap(IBioGenome genome, String format, String outFile) throws IOException {
		FileOutputStream  stream 	   = new FileOutputStream(outFile);
		if (format.equals("tab")) {
			 MapTabFactory factory  = new MapTabFactory();
			 factory.unload(genome, stream);
		} else if (format.equals("mch")) {
			MapChartFactory factory  = new MapChartFactory();
			factory.unload(genome, stream);
		} else if (format.equals("mmk")) {
			MapMakerFactory factory  = new MapMakerFactory();
			factory.unload(genome, stream);
		}
		stream.close();
	}
	/**
	 * @param ap
	 * @param outFile
	 */
	protected static void writeMap(IBioGenome map, String outFile) throws IOException {
		FileOutputStream  stream 	   = new FileOutputStream(outFile);
		XmlBioEntityFactory xmlFactory = new  XmlGeneticMapFactory();
		xmlFactory.unload(map, stream);
		stream.close();
	}
	/**
	 * @param cluFile
	 * @return
	 */
	protected static MetaQtlModel getCluster(String cluFile) throws IOException {
		if (cluFile == null) return null;
		FileReader r = new FileReader(cluFile);
		MetaQtlModel cluster = MetaQtlModelFactory.read(r);
		r.close();
		return cluster;
	}

	/**
	 * @param resFile
	 * @return
	 * @throws I
	 */
	protected static MetaQtlAnalysis getResult(String resFile) throws IOException {
		if (resFile == null) return null;
		FileReader r = new FileReader(resFile);
		MetaQtlAnalysis result = MetaQtlAnalysisFactory.read(r);
		r.close();
		return result;
	}

	/**
	 * 
	 * @param mapFile
	 * @return
	 * @throws IOException
	 */
	protected static IBioGenome getMap(String mapFile) throws IOException  {
		if (mapFile == null) return null;
		
		FileReader reader = new FileReader(mapFile);
		XmlBioEntityFactory xmlFactory = new  XmlGeneticMapFactory();
		IBioGenome map = (IBioGenome) xmlFactory.load(reader);
		reader.close();
		
		return map;
	}
	
	/**
	 * @param dubFile
	 * @return
	 * @throws IOException
	 */
	protected static IBioLocus[] getDubious(String dubFile) throws IOException {
		if (dubFile == null) return null;
		FileReader reader = new FileReader(dubFile);
		IBioLocus[] dubious = DubiousLocusFactory.read(reader);
		reader.close();
		return dubious;
	}
	/**
	 * 
	 * @param dicoFile
	 * @return
	 * @throws IOException
	 */
	protected static MetaDico getDico(String dicoFile) throws IOException {
		if (dicoFile == null) return null;
		FileReader reader = new FileReader(dicoFile);
		MetaDico dico = MetaDicoFactory.read(reader);
		reader.close();
		return dico;
	}
	/**
	 * @param qtlDir
	 * @return
	 * @throws IOException
	 */
	protected static IBioGenome[] getMaps(String qtlDir) throws IOException  {
		if (qtlDir == null) return null;
		
		File dir = new File(qtlDir);
		File[] files = null;
		
		if (dir.isDirectory()) {
			files = dir.listFiles();
		} else {
			files = new File[1];
			files[0] = new File(qtlDir);
		}
			
		ArrayList mapList = new ArrayList();
		XmlBioEntityFactory xmlFactory = new  XmlGeneticMapFactory();
			
		for(int i=0;i<files.length;i++) {
			if (files[i].getName().endsWith("xml")) {
				FileReader reader = new FileReader(files[i]);
				IBioGenome qtl    = (IBioGenome) xmlFactory.load(reader);
				if (qtl != null)
					mapList.add(qtl);
				reader.close();
			}
		}
			
		if (mapList.size() > 0) {
			
			IBioGenome[] qtls = new IBioGenome[mapList.size()];
			mapList.toArray(qtls);
			
			return qtls;
			
		} else 
			return null;
	}
	
	
	/**
	 * @param qtlF
	 * @return
	 * @throws IOException
	 */
	protected static IBioOntology getOntology(String ontoF, char format) throws IOException {
		if (ontoF == null) return null;
		BioEntityFactory factory = null;
		IBioOntology ontology = null;
		FileReader r = new FileReader(ontoF);
		if (format == 'x') {
			factory = new  XmlOntologyFactory();
		} else if (format == 't') {
			factory = new  OntologyTabFactory();
		} else return null;
		ontology = (IBioOntology) factory.load(r);
		r.close();
		return ontology;
	}

	/**
	 * @param string
	 * @param version
	 */
	public static void printLicense(String program, String version) {
		System.out.println(program + " " + version);
		System.out.println("Copyright (C) 2005  Jean-Baptiste Veyrieras (INRA)");
		System.out.println(program+" comes with ABSOLUTELY NO WARRANTY.");
		System.out.println("This is free software, and you are welcome");
		System.out.println("to redistribute it under certain conditions;");
	}
	
	public static QtlPartition getQtlPartition(Chromosome chromosome, IBioOntology ontology) {
	 	if (chromosome == null) return null;
	 	if (chromosome.getQtlNumber() == 0) return null; 
	 	chromosome.computeQtlSD(IMetaQtlConstants.CI_MODE_AVAILABLE);
		MetaQtlData qtlData      = new MetaQtlData(chromosome.getQtls());
		if (ontology == null) qtlData.doTraitGroupClustering();
		else qtlData.doTraitOntologyClustering(ontology);
		qtlData.manageMissingData(IMetaQtlConstants.CI_MISS_IMPUTATION);
		return QtlPartitionAdapter.adapt(qtlData);	 	
	 }
	
	 public static QtlPartition getQtlPartition(Chromosome chrom, MetaQtlAnalysis analysis, String trait, int k, char tclust)
	 {
 		Qtl[] mqtls = analysis.getMetaQtl(chrom.name, trait, k);
 		
 		chrom.setMetaQtls(mqtls);
 		
 		QtlPartition qtlPart = QtlPartitionAdapter.adapt(analysis, chrom.name, trait, k);
 		
 		if (tclust == 'C') {
 			for(int i=0;i<qtlPart.nqtl;i++) {
 				int maxj = 0;
 				double max = 0.0;
	 			for(int j=0;j<qtlPart.np;j++) {
	 				if (qtlPart.proba[i][j] > max) {
	 					max = qtlPart.proba[i][j];
	 					maxj= j;
	 				}
	 			}
	 			for(int j=0;j<qtlPart.np;j++) {
	 				if (maxj==j) qtlPart.proba[i][j]=1.0;
					else qtlPart.proba[i][j]=0.0;
	 			}
 			}
 		}
 		return qtlPart;
	 }

	/**
	 * 
	 */
	public static void printImgFormat(int indent) {
		 // Get the format names
        String[] formatNames = ImageIO.getWriterFormatNames();
        Set set = new HashSet();
        for(int i=0; i<formatNames.length; i++) {
            String name = formatNames[i].toLowerCase();
            set.add(name);
        }
        Iterator iter = set.iterator();
        while(iter.hasNext()) {
        	for(int i=0;i<indent;i++) System.out.println(" ");
        	System.out.println("-" + iter.next());
        }
	} 
	/**
	 * @param parFile
	 */
	public static void setParameter(String parFile) throws IOException {
		if (parFile == null) return;
		FileReader r = new FileReader(parFile);
		MetaGraphParFactory.update(r);
		r.close();
	}
	/**
	 * 
	 * @param list
	 * @return
	 */
	public static String[] parseChromList(String list) {
		if (list==null) return null;
		// Do the string contains delimiters
		String[] chroms;
		if (list.indexOf(",")>0) {
			StringTokenizer tokenizer = new StringTokenizer(list, ",", false);
			chroms = new String[tokenizer.countTokens()];
			int i=0;
			while(tokenizer.hasMoreTokens()) chroms[i++] = tokenizer.nextToken();
		} else {
			chroms    = new String[1];
			chroms[0] = list;
		}
		return chroms;
	}
	
	public static Chromosome[] getChromByNames(Chromosome[] chroms, String[] chromNames) {
		if (chromNames == null) return null;
		ArrayList chromList = new ArrayList();
		for(int i=0;i<chromNames.length;i++) {
			for(int j=0;j<chroms.length;j++) {
				if (chroms[j].getName().equals(chromNames[i])) {
					chromList.add(chroms[j]);
					break;
				}
			}
		}
		if (chromList.size() > 0) {
			Chromosome[] c = new Chromosome[chromList.size()];
			chromList.toArray(c);
			return c;
		}
		return null;
	}
	
	public static ChromCluster[] getClusterByNames(ChromCluster[] clusters, String[] chromNames) {
		if (chromNames == null) return null;
		ArrayList chromList = new ArrayList();
		for(int i=0;i<chromNames.length;i++) {
			for(int j=0;j<clusters.length;j++) {
				if (clusters[j].getName().equals(chromNames[i])) {
					chromList.add(clusters[j]);
					break;
				}
			}
		}
		if (chromList.size() > 0) {
			ChromCluster[] c = new ChromCluster[chromList.size()];
			chromList.toArray(c);
			return c;
		}
		return null;
	}
	/**
	 * 
	 * @param chroms
	 * @param withQtl
	 * @return
	 */
	public static ChromGraph[] getChromGraph(Chromosome[] chroms, boolean withQtl, int modeQtl, IBioOntology ontology, MetaQtlAnalysis treeAnalysis) {
		if (chroms==null) return null;
		Hashtable  qtlPartNames  = new Hashtable();
		ChromGraph[] chromGraphs = new ChromGraph[chroms.length];
		for(int i=0;i<chroms.length;i++) {
			chromGraphs[i] = new ChromGraph();
			chromGraphs[i].chromosome = chroms[i];
			if (withQtl) {
				chromGraphs[i].qtlPart = getQtlPartition(chroms[i], ontology);
				if (chromGraphs[i].qtlPart != null) {
					chromGraphs[i].qtlPart.mode = modeQtl;
					ArrayList pNames = chromGraphs[i].qtlPart.getPartNames();
					for(int j=0;j<pNames.size();j++) {
						qtlPartNames.put(pNames.get(j), new String(""));						
					}
					if (modeQtl==QtlPartition.HORIZONTAL_MODE && treeAnalysis!=null) {
						int cidx = treeAnalysis.getChromIdx(chroms[i].getName());
						chromGraphs[i].qtlPart.tree = treeAnalysis.getResult(chroms[i].getName()).getQtlTree();
						// Check Leaf order...
						int[] nidx = new int[treeAnalysis.qtlByChrom[cidx].length];
						for(int idx=0;idx<treeAnalysis.qtlByChrom[cidx].length;idx++) {
							for(int j=0;j<chromGraphs[i].qtlPart.qtlNames.length;j++) {
								if (treeAnalysis.qtlByChrom[cidx][idx].name.equals(chromGraphs[i].qtlPart.qtlNames[j])) {
									nidx[idx]=j;
									break;
								}
							}
						}
						chromGraphs[i].qtlPart.tree.updateLeafIdx(nidx);
					}
				}
			}
		}
		synchChromGraphs(chromGraphs, qtlPartNames.keySet());
		// Finally if a chrom is defined as a skeleton, move it
		// to the front
		for(int i=0;i<chromGraphs.length;i++) {
			if (chromGraphs[i].chromosome.skeleton) {
				for(int j=i-1;j>=0;j--) {
					ChromGraph tmp   = chromGraphs[j+1];
					chromGraphs[j+1] = chromGraphs[j];
					chromGraphs[j]   = tmp;
				}
				break;
			}
		}
		return chromGraphs;
	}
	/**
	 * 
	 * @param chromGraphs
	 * @param partNames
	 */
	public static void synchChromGraphs(ChromGraph[] chromGraphs, Set partNames) {
		if (partNames == null) return;
		if (chromGraphs == null) return;
		String[] pNames = new String[partNames.size()];
		Iterator iter = partNames.iterator();
		int i=0;
		while(iter.hasNext()) pNames[i++]=(String) iter.next();
		for(i=0;i<chromGraphs.length;i++) {
			if (chromGraphs[i].qtlPart!=null) {
				chromGraphs[i].qtlPart.rebuildPartition(pNames);
			}
		}	
	}
	
	public static void writeImg(ChromGraph[] chroms, File file, String imgFormat) throws IOException {
		BufferedImage bi = GraphFactory.getImage(chroms);
		if (bi != null)
			ImageIO.write(bi, imgFormat, file);
	}
	
	/**
	 * @param file
	 * @throws IOException
	 */
	public static void writePar(File file) throws IOException {
		FileOutputStream stream = new FileOutputStream(file);
		MetaGraphParFactory.write(stream);
		stream.close();
	}
	
	/**
	 * @param file
	 * @return
	 */
	public static Hashtable getMrkToRm(File file) throws IOException {
		int i=0;
		Hashtable  hash   = null;
		ArrayList  list   = null;
		ArrayList  tmp;
		FileReader reader = new FileReader(file);
		if (reader == null) return null;
		BufferedReader buffer = new BufferedReader(reader);
		String line,map,chrom,marker;
		while ((line = buffer.readLine()) != null) {
			i++;
			StringTokenizer token = new StringTokenizer(line, "\t");
			if (token.countTokens() >= 3) {
				map   = token.nextToken();
				chrom = token.nextToken();
				marker= token.nextToken();
				if (hash==null) hash = new Hashtable();
				list = (ArrayList) hash.get(map);
				if (list == null) list = new ArrayList();
				tmp = new ArrayList();tmp.add(chrom);tmp.add(marker);
				list.add(tmp);
				hash.put(map, list);
			} else {
				throw new IOException("[ ERROR ] Unexpected line format at line " + i +" in " + file.getName()); 
			}
		}
		reader.close();
		return hash;
	}
	
	/**
	 * @param maps
	 * @param mrkRmFile
	 */
	public static void removeLocus(IBioGenome[] maps, String mrkRmFile) throws IOException {
		if (maps == null || mrkRmFile == null) return;
		Hashtable hash = getMrkToRm(new File(mrkRmFile));
		if (hash == null) return;
		Enumeration iter = hash.keys();
		while(iter.hasMoreElements()) {
			String mapName = (String)iter.nextElement();
			ArrayList mrkLists = (ArrayList)hash.get(mapName);
			IBioGenome map = getMap(maps, mapName);
			if (map != null) {
				for(int i=0;i<mrkLists.size();i++) {
					String chromName = (String) ((ArrayList)mrkLists.get(i)).get(0);
					String mrkName = (String) ((ArrayList)mrkLists.get(i)).get(1);
					IBioLGroup chrom = map.getGroup(chromName);
					if (chrom != null) {
						chrom.removeLocus(mrkName);
					} else {
						System.err.println("[ WARNING ] Unable to find chromosome " + chromName + " : wrong name in " + mrkRmFile);
					}
				}
			} else {
				System.err.println("[ WARNING ] Unable to find map " + mapName + " : wrong name in " + mrkRmFile);
			}
		}
	}
	
	/**
	 * @param maps
	 * @param mapName
	 * @return
	 */
	public static IBioGenome getMap(IBioGenome[] maps, String mapName) {
		if (maps == null || mapName==null) return null;
		for(int i=0;i<maps.length;i++) {
			if (maps[i].getName().equals(mapName))
				return maps[i];
		}
		return null;
	}
	
	

}
