/* BEGIN_HEADER                                              Java TreeView
 *
 * $Author: rqluk $
 * $RCSfile: ChromosomeLoci.java,v $
 * $Revision: 1.1 $
 * $Date: 2006-08-16 19:13:49 $
 * $Name:  $
 *
 * This file is part of Java TreeView
 * Copyright (C) 2001-2014 Alok Saldanha, All Rights Reserved. Modifications Copyright (C) Lawrence Berkeley Lab.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * END_HEADER 
 */
package edu.stanford.genetics.treeview.plugin.karyoview;

/**
* this class encapsulates the position of things. No expression data or headers or other such nonsense allowed.
*/
class ChromosomeLoci {
  
	  /**
  * Adds an element. Multiple elements with the same cdtIndex are not allowed, so don't do it.
  */
  public void addLocus(ChromosomeLocus l) {
	loci[l.getCdtIndex()] = l;
	setStructValid(false);
  }
  
  /**
  * finds the actual position of the i'th element.
  */
  public double getPosition(int chromosome, int arm, int index) 
  throws ArrayIndexOutOfBoundsException {
	if (isStructValid() == false) buildTree();
	ChromosomeLocus temp = chromosomes[chromosome - 1].getLocus(arm, index);
	if (temp == null) {
	  throw new ArrayIndexOutOfBoundsException();
	} else {
	  return temp.getPosition();
	}
  }
  
  /**
  * Given a location, performs a binary search to find the closest locus.
  */
  public ChromosomeLocus getClosestLocus(int chromosome, int arm, double position) {
	  return chromosomes[chromosome - 1].getClosestLocus(arm, position);
  }
  
  public ChromosomeLocus getLocus(int i) {
	return loci[i];
  }
  
  
  /**
  * returns the maximum distance from centromere in Loci
  */
  public double getMaxPosition(int chromosome, int arm) {
	if (isStructValid() == false) buildTree();
	return chromosomes[chromosome - 1].getMaxPosition(arm);
  }

  /**
  * returns the maximum distance from centromere in Loci
  */
  public double getMaxPosition() {
	if (isStructValid() == false) buildTree();
	double maxPos = -1.0;
	for (int i = 0; i < chromosomes.length; i++) {
		double thisMax = chromosomes[i].getMaxPosition();
		if (thisMax > maxPos)  maxPos = thisMax;
	}
	return maxPos;
  }

  /**
  * returns largest chromosome number in loci.
  */
  public int getMaxChromosome() {
	if (isStructValid() == false) buildTree();
	return chromosomes.length;
  }
  
  private ChromosomeLocus [] loci;
  /**
  * This is just an array of all the chromosomes...
  */
  private Chromosome [] chromosomes;
  private boolean structValid;
  
  private boolean isStructValid() {
	return structValid;
  }
  private void setStructValid(boolean b) {
	structValid = b;
  }
  
  /**
  * usually know how many elements we'll need...
  */
  ChromosomeLoci(int n) {
	loci = new ChromosomeLocus[n];
	structValid = false;
  }
  
  /**
  * internal method to build fast datastructure
  */
  private void buildTree() {
	if (isStructValid() == true) return;
	
	allocateDataStructure();
	loadDataStructure();
	setStructValid(true);
  }
  
  /**
  * loads loci into allocated data structure.
  */
  private void loadDataStructure() {
	// going to insertion sort, since I'm lazy.
	for (int i = 0; i < loci.length; i++) {
		ChromosomeLocus locus = loci[i];
	  if (locus.getChromosome() > 0) {
		chromosomes[locus.getChromosome()-1].insertLocus(locus);
	  }
	}
  }
  
  /**
  * this routine allocates the proper space for chromosomes.
  * it must be called immediately before loadDataStructure();
  */
  private void allocateDataStructure() {
	// find max chromosome...
	int maxChr = -1;
	for (int i = 0; i < loci.length; i++) {
	  if (loci[i] == null) continue;
	  if (loci[i].getChromosome() > maxChr) maxChr = loci[i].getChromosome();
	}
	
	//  counts of arms
	int [] leftArmCount = new int [maxChr];
	int []rightArmCount = new int [maxChr];
	int []circularArmCount = new int [maxChr];
	for (int i = 0; i <maxChr;i++) {
	  leftArmCount[i] = 0;
	  rightArmCount[i] = 0;
	  circularArmCount[i] = 0;
	}
	for (int i = 0; i < loci.length; i++) {
	  int chr = loci[i].getChromosome();
	  int arm = loci[i].getArm();
	  if (arm == ChromosomeLocus.LEFT)  leftArmCount[chr -1]++;
	  if (arm == ChromosomeLocus.RIGHT) rightArmCount[chr-1]++;
	  if (arm == ChromosomeLocus.CIRCULAR) circularArmCount[chr-1]++;
	}
	
	chromosomes = new Chromosome [maxChr];
	for (int i = 0; i < maxChr; i++) {
		if (circularArmCount[i] != 0) {
			chromosomes[i] = new CircularChromosome(circularArmCount[i]);
		} else {
			chromosomes[i] = new LinearChromosome(leftArmCount[i], rightArmCount[i]);
		}
	}
  } // end allocateDataStructure
 
}


