/*  
 *  src/org/metaqtl/Qtl.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;

import java.io.Serializable;

import org.metaqtl.util.MappingFunction;
/**
 * A QTL (Quantitative Trait Loci)
 */
public class Qtl {
	/**
	 * The id of the Qtl
	 */
	public int id;
	/**
	 * The name of the QTL.
	 */
	public String name = null;
	/**
	 * The indice of the left flanking marker.  
	 */
	public int m1 = 0;	
	/**
	 * The indice of the right flanking marker.
	 */
	public int m2 = 0;
	/**
	 * The QTL most probable location on the chromosome.
	 */
	public double position = 0.0;
	/**
	 * The standard deviation of the QTL position.
	 */
	public double sd_position = 0.0;
	/**
	 * The recombination rate between the QTL position and
	 * the left flanking marker.
	 */
	public double c1 = 0.0; 
	/**
	 * The recombination rate between the QTL position and
	 * the right flanking marker.
	 */
	public double c2 = 0.0;
	/**
	 * The QTL trait data. 
	 */
	public QTLTrait trait = null;
	/**
	 * The QTL statistics
	 */
	public QTLStats stats = null;
	/**
	 * The QTL confidence interval.
	 */
	public QTLSupport ci = null;
	/**
	 * The QTL cross design.
	 */
	public QTLCross cross = null;
	/**
	 * The QTL projection properties.
	 */
	public QTLProj proj = null;
	/**
	 * The qtl status for meta-analysis
	 */
	private boolean ignore = false;
	/**
	 * This class stores the trait description of a QTL
	 */
	public class QTLTrait implements Serializable {
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		public QTLTrait() {
			name=unit=null;
		}
		
		/**
		 * The name of the trait.
		 */
		public String name;
		/**
		 * The unit of the trait.
		 */
		public String unit;
		/**
		 * The name of the group to which
		 * the trait belongs.
		 */
		public String group;
		/**
		 * @return
		 */
		public QTLTrait getCopy() {
			
			QTLTrait trait = new QTLTrait();
			
			if (name != null)
				trait.name  = new String(name);
			if (group != null)
				trait.group = new String(group);
			if (unit != null)
				trait.unit  = new String(unit);
			
			return trait;
		}
	}
	/**
	 * This class stores the usual statistics for the QTL.
	 */
	public class QTLStats  implements Serializable{
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		public QTLStats() {
			rsquare=additive=dominance=lod=0.0;
		}
		/**
		 * The r-square, i.e the proportion of variance 
		 * explained by the QTL. 
		 */
		public double rsquare;
		/**
		 * The additive effect of the QTL.
		 */
		public double additive;
		/**
		 * The dominance effect of the QTL.
		 */
		public double dominance;
		/**
		 * The lod score of the QTL.
		 */
		public double lod;
		/**
		 * @return
		 */
		public QTLStats getCopy() {
			QTLStats stats = new QTLStats();
			
			stats.additive  = additive;
			stats.dominance = dominance;
			stats.lod 		= lod;
			stats.rsquare	= rsquare;
			
			return stats;
		}
	}
	/**
	 * This class stores information
	 * on the QTL Confidence Interval
	 */
	public class QTLSupport  implements Serializable {
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		public QTLSupport() {
			from=to=ic1=ic2=lodDecrease=0.0;
		}
		/**
		 * The recombination rate between the QTL position
		 * and the left border of the IC.
		 */
		public double ic1;
		/**
		 * The recombination rate between the QTL position
		 * and the right border of the IC.
		 */
		public double ic2;
		/**
		 * The position from the first marker
		 * to the left border of the IC.
		 */
		public double from;
		/**
		 * The position from the first marker 
		 * to the right border of the IC.
		 */
		public double to;
		/**
		 * The value of the LOD decrease used to obtain the CI.
		 */
		public double lodDecrease;
		/**
		 * @return
		 */
		public QTLSupport getCopy() {
			QTLSupport copy = new QTLSupport();
			
			copy.from = from;
			copy.ic1  = ic1;
			copy.ic2  		 = ic2;
			copy.lodDecrease = lodDecrease;
			copy.to			 = to;
			
			return copy;
		}
	}
	/**
	 * This class stores the informations on the cross
	 * design used to detect the QTL.
	 */
	public class QTLCross  implements Serializable {
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		public QTLCross() {
			name=null;
			gen=type=size=0;
		}
		/**
		 * The name of the cross design.
		 */
		public String name;
		/**
		 * The type of the cross.
		 */
		public int type;
		/**
		 * The number of generations.
		 */
		public int gen;
		/**
		 * The number of individuals.
		 */
		public int size;
		/**
		 * @return
		 */
		public QTLCross getCopy() {
			QTLCross copy = new QTLCross();
			
			copy.gen = gen;
			copy.name = (name!=null) ? new String(name) : null;
			copy.size = size;
			copy.type = type;
			
			return copy;
		}
		
	}
	/**
	 * This class stores the QTL projection
	 * properties.
	 */
	public class QTLProj   implements Serializable {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		/**
		 * The name of the map from which
		 * this QTL comes.
		 */
		public String mapOrigin;
		/**
		 * The original indice of the left
		 * flanking marker.
		 */
		public int o_mrkIdxL;
		/**
		 * The original indice of the right 
		 * flanking marker.
		 */
		public int o_mrkIdxR;
		/**
		 * The skeleton indice of the left
		 * flanking marker.
		 */
		public int s_mrkIdxL;
		/**
		 * The skeleton indice of the right 
		 * flanking marker.
		 */
		public int s_mrkIdxR;
		/**
		 * The scale ratio between the original
		 * map and the map on which the QTL has
		 * been projected.
		 */
		public double mapScale;
		/**
		 * The cumulated scale weights of the common
		 * marke sequences between the original map
		 * and the map on which the QTL has been projected.
		 */
		public double mapWeight;
		/**
		 * The scale ratio between the original flanking
		 * marker interval and the one on the map used
		 * for projection. 
		 */
		public double intScale;
		/**
		 * True if the two maps share a common marker sequence
		 * around the QTL.
		 */
		public boolean shareFlanking;
		/**
		 * True if the flanking marker order are inversed between
		 * the skeleton and the original map.
		 */
		public boolean swap;
		
		public QTLProj() {
			mapScale=0.0;
			mapWeight=0.0;
			intScale=0.0;
			shareFlanking=false;
			swap=false;
			mapOrigin=null;
		}

		/**
		 * @return
		 */
		public QTLProj getCopy() {
			QTLProj copy = new QTLProj();
			
			copy.intScale = intScale;
			copy.mapScale = mapScale;
			copy.mapWeight = mapWeight;
			copy.o_mrkIdxL = o_mrkIdxL;
			copy.o_mrkIdxR = o_mrkIdxR;
			copy.s_mrkIdxL = s_mrkIdxL;
			copy.s_mrkIdxR = s_mrkIdxR;
			copy.shareFlanking = shareFlanking;
			copy.swap 		= swap;
			if (mapOrigin!=null)
				copy.mapOrigin  = new String(mapOrigin);
			
			return copy;
		}
		
	}
	/**
	 * Depending on the CI type this methods returns the  
	 * the standard deviation of the QTL position using the given
	 * mapping context.
	 * @param ci the confidence interval of the QTL.
	 * @param mappingFunction the mapping function.
	 * @param mappingUnit the mapping unit.
	 * @return the standard deviation of the QTL position.
	 */
	public static double getQTLSD(QTLSupport ci, int mappingFunction, int mappingUnit) {
		double d1,d2,sup, sd=0.0;
		if (ci==null) return 0.0;
		
		d1 = MappingFunction.distance(ci.ic1, mappingFunction, mappingUnit);
		d2 = MappingFunction.distance(ci.ic2, mappingFunction, mappingUnit);
		
		sup = d1+d2;
		
		if (ci.lodDecrease != 0.0) {
			if (ci.lodDecrease < 1.5)
				sd = sup/(2.0*1.6449); /* CI(90%) */
			else
				sd = sup/(2.0*1.96); /* CI(95%) */
		} else {
			sd = sup/(2.0*1.96); /* CI(95%) */
		}
		
		return sd;
	}
	/**
	 * Depending on the r-square parameter and the cross design
	 * this method returns the standard deviation of the QTL position
	 * for the given mapping context.
	 * 
	 * This method uses the formula introduced in Darvasi A. and Soller M.,
	 * A Simple Method to Calculate Resolving Power and Confidence Interval
	 * of QTL Map Location, 1997, Behavior Genetics 27,2:125-132.
	 * 
	 * For the moment the cross type is not taken into account.
	 * 
	 * @param rsquare 
	 * @return
	 */
	public static double getQTLSD(double rsquare, QTLCross cross, int mappingUnit) {
		double sd=0.0;
		
		if (cross == null) return 0.0;
		if (cross.size == 0.0) return 0.0;
		if (rsquare == 0.0) return 0.0;
		if (rsquare > 1.0) return 0.0;
		
		sd  = 530.0/(rsquare*cross.size); /* CI(95%) */
		sd /= (2.0*1.96);
		
		if (mappingUnit == IMetaQtlConstants.MAPPING_UNIT_M)
			sd /= 100.0;
		
		return sd;
	}
	/**
	 * This methods returns true if the QTL must be ignored.
	 * 
	 * @return true if the QTL must be ignored.
	 */
	public boolean isIgnore() {
		return ignore;
	}
	/**
	 * Set the status of the QTL.
	 * 
	 * @param ignore the status of the QTL.
	 */
	public void setIgnore(boolean ignore) {
		this.ignore = ignore;
	}
	/**
	 * Returns a copy of the QTL.
	 * 
	 * @return a copy of the QTL.
	 */
	public Qtl getCopy() {
		
		Qtl qtl = new Qtl();
		
		qtl.c1	= c1;
		qtl.c2	= c2;
		if (ci != null)
			qtl.ci  = ci.getCopy();
		if (cross != null)
			qtl.cross	= cross.getCopy();
		qtl.ignore  = ignore;
		qtl.m1		= m1;
		qtl.m2		= m2;
		qtl.name    = new String(name);
		qtl.position	= position;
		if (proj != null)
			qtl.proj		= proj.getCopy();
		qtl.sd_position = sd_position;
		if (stats != null)
			qtl.stats		= stats.getCopy();
		if (trait != null)
			qtl.trait		= trait.getCopy();
			
		
		return qtl;
	}
	/**
	 * Returns the name of the QTL.
	 * @return the name of the QTL.
	 */
	public String getName() {
		return name;
	}
}
