package artificialFastqGenerator;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * An instance of this class can be used to create new Nucleobase and Read objects.
 * 
 * Copyright (C) 2012 The Institute of Cancer Research (ICR).
 *
 * This file is part of ArtificialFastqGenerator v1.0.0.
 * 
 * ArtificialFastqGenerator 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 3 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 Public License along with this
 * program. If not, see <http://www.gnu.org/licenses/>
 * 
 * Authour's contact email: Matthew.Frampton@icr.ac.uk
 */

public class NucleobaseAndReadFactory {

	private long numNucleobasesCreated;
	private long numReadsCreated;
	private int xCoordinate;
	private int yCoordinate;
	private int readLength;
	private QualityScoreAndErrorGenerator qualityScoreAndErrorGenerator;
	
	private Logger logger;
	
	/**
	 * Initialises a new NucleobaseAndReadFactory object.
	 * 
	 * @param startingXCoord - the starting X coordinate.
	 * @param startingYCoord - the starting Y coordinate.
	 * @param readLength - the lenght of each read.
	 * @param useRealQualityScores - true if using quality scores from an existing fastq file, else false.
	 * @param simulateErrorInRead - true if simulating error in reads, else false.
	 * @param fastq1ForQualityScoresPath - the 1st fastq file whose quality scores are used.
	 * @param fastq2ForQualityScoresPath - the 2nd fastq file whose quality scores are used.
	 */
	
	public NucleobaseAndReadFactory(int startingXCoord, int startingYCoord, int readLength, boolean useRealQualityScores,
			boolean simulateErrorInRead, String fastq1ForQualityScoresPath, String fastq2ForQualityScoresPath) {
		
		numNucleobasesCreated = 0;
		numReadsCreated = 0;
		xCoordinate = startingXCoord;
		yCoordinate = startingYCoord;
		this.readLength = readLength; 
		qualityScoreAndErrorGenerator = new QualityScoreAndErrorGenerator(readLength,useRealQualityScores,
				simulateErrorInRead,fastq1ForQualityScoresPath,fastq2ForQualityScoresPath);
		
		logger = Main.logger;
	}
	
	/**
	 * Update the x and y coordinates.
	 */
	
	private void updateXAndYCoords() {
		
		if (xCoordinate == 20100) {
			xCoordinate = 1000;
			yCoordinate = yCoordinate + 1;
		} else {
			xCoordinate = xCoordinate + 1;}
		
	}
	
	/**
	 * Create a Nucleobase object.
	 * 
	 * @param genotype - the nucleobase's genotype.
	 * @return newNucleobase - a new nucleobase object.
	 */
	
	public Nucleobase createNucleobase(char genotype) {
		
		numNucleobasesCreated = numNucleobasesCreated + 1;
		Nucleobase newNucleobase = new Nucleobase(genotype,numNucleobasesCreated,0);
		return newNucleobase;
		
	}
	
	/**
	 * Create a pair of reads.
	 * 
	 * @param leftEndSequence - the sequence of nucleobases for the left read.
	 * @param rightEndSequence - the sequence of nucleobases for the right read.
	 */
	
	public void createPairedEndReads(ArrayList<Nucleobase> leftEndSequence, ArrayList<Nucleobase> rightEndSequence) {

		updateXAndYCoords();	
		Read leftRead = createRead(leftEndSequence, null, true);
		Read rightRead = createRead(rightEndSequence, leftRead, false);
		leftRead.setPairedEndRead(rightRead);
	
	}
	
	/**
	 * Create a new Read object. 
	 * 
	 * @param nucleobases - the sequence of nucleobases for the read.
	 * @param pairedEndRead - the other read with which this read forms a pair.
	 * @return newRead - a new Read object.
	 */
	
	private Read createRead(ArrayList<Nucleobase> nucleobases, Read pairedEndRead, boolean isLeft) {
		
		numReadsCreated = numReadsCreated + 1;
		ArrayList<char[]> qualityScoresAndGenotypesRead = qualityScoreAndErrorGenerator.getQualityScoresAndGenotypesRead(isLeft, nucleobases);
		char[] encodedQualityScores = qualityScoresAndGenotypesRead.get(0);
		char[] genotypesThatAreRead = qualityScoresAndGenotypesRead.get(1);
		Read newRead = new Read(nucleobases,numReadsCreated,pairedEndRead,isLeft,encodedQualityScores,genotypesThatAreRead,xCoordinate,yCoordinate);
		return newRead;
		
	}
	
	/**
	 * Get the number of nucleobases that have been created.
	 * 
	 * @return numNucleobasesCreated - the number of Nucleobase objects that have been created so far.
	 */
	
	public long getNumNucleobasesCreated() {
		return numNucleobasesCreated;
	}
	
	/**
	 * Get the number of reads that have been created.
	 * 
	 * @return numReadsCreated - the number of Read objects that have been created so far.
	 */
	
	public long getNumReadsCreated() {
		return numReadsCreated;
	}
	
	/**
	 * Get the number of base call errors that have been simulated.
	 * 
	 * @return numBaseCallErrors - the number of base call errors that have been simulated.
	 */
	
	public long getNumBaseCallErrors() {
		return qualityScoreAndErrorGenerator.getNumBaseCallErrors();
	}
	
	/**
	 * Get the number of ACGT nucleobases that have been called.
	 * 
	 * @return numACGTBasesCalled - the number of ACGT nucleobases that have been called.
	 */
	
	public long getNumACGTBasesCalled() {
		return qualityScoreAndErrorGenerator.getNumACGTBasesCalled();
	}
	
	/**
	 * Log the overall error statistics.
	 */
	
	public void logOverallErrorStats() {
		
		long totalBaseCalls = numReadsCreated * (long) readLength;
		logger.log(Level.INFO,"# reads=" + numReadsCreated);
		logger.log(Level.INFO,"# nucleobase calls=" + totalBaseCalls);
		logger.log(Level.INFO,"# ACGT nucleobase calls=" + qualityScoreAndErrorGenerator.getNumACGTBasesCalled());
		logger.log(Level.INFO,"# ACGT call errors=" + qualityScoreAndErrorGenerator.getNumBaseCallErrors());
	}

	/**
	 * Reset fields ready to process a new chromosome. 
	 */
	
	public void resetFieldsForNewChromosome() {
		
		numNucleobasesCreated = 0;
		numReadsCreated = 0;
		qualityScoreAndErrorGenerator.resetNumBaseCallErrors();
	}
	
}
