/* 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.data;

import java.util.TreeSet;
import java.util.Vector;
import java.util.regex.Pattern;

import de.rki.ng4.ipig.exceptions.FormatException;
import de.rki.ng4.ipig.mapping.Position;

/**
 * <p>Gene is a container for diverse informations of a gene, as they are provided by the USCS table browser.</p>
 * 
 * <p>The import of USCS annotations should be done with a {@link GeneSet}, which provides reading methods 
 * and creates an Gene object for any gene (resp. line) in an imported annotation file.</p>
 * 
 * <p>Please note: Coordinates (resp. positions) provided by the UCSC are zero-based and half-open.
 * This means gene positions are indexed like for example Java Arrays from 0 to length-1.</p>
 * 
 * <p>The member variables correspond to the USCS table schema for the track "UCSC Genes" and table "knownGene"
 * or the track "Ensembl Genes" with table "ensGene"
 * with e.g. clade "Mammal", genome "Human" and group "Genes and Gene Prediction Tracks" (
 * <a href="http://genome.ucsc.edu/cgi-bin/hgTables?org=Human&db=hg19&hgsid=205072007&hgta_doMainPage=1">
 * USCS Table Browser</a>).</p>
 *
 * @author Mathias Kuhring
 */

/**
 * <p>A Peptide is a container for diverse informations of a peptide from the input to be mapped.</p>
 * 
 * <p>The import of peptides should be done with a {@link PeptideSet}, as it provides reading methods for txt and mzIdentML files.
 * It will create an Peptide object for any imported peptide.</p>
 * 
 * 
 * 
 * @author Mathias Kuhring
 */
public class Peptide {
	// data and annotation from the ms-csv input, as far as needed (prot_acc corresponds to UniProtKB-ID/EntryName or IPI)
	// prot_acc	prot_desc	pep_query	pep_isunique	pep_exp_z	pep_score	pep_seq	pep_var_mod	pep_var_mod_pos
	private String prot_acc;
	private String prot_desc;
	private String pep_query;
	private String pep_isunique;
	private String pep_exp_z;
	private String pep_score;
	private String pep_seq;
	private String pep_var_mod;
	private String pep_var_mod_pos1;
	
	private Vector<String> rest;

	// the input row as string, used to simplify outputs
	private String row;

	// different protein ids, imported from uniprot idmapping file (UniProtKB-AC and RefSeq)
	private String uniqueIdentifier;
	private Vector<String> refSeq;
	private Vector<String> ensemble_TRS;

	private String geneSymbol;

	// positions to be calculated by the mapping
	private TreeSet<Position> positions;

	private Vector<String> geneNames;

	// some checkback values, if the peptide got protein ids, linked to a gene and mapped to this gene
	private boolean proteinAssigned;
	private boolean geneAssigned;
	private boolean geneMapped;

	private String method;
	//	private boolean validated;

	/**
	 * <p>Peptide is initiated by giving a String with following tab separated values:</p>
	 * 
	 * <p>prot_acc	prot_desc	pep_query	pep_isunique	pep_exp_z	pep_score	pep_seq	pep_var_mod	pep_var_mod_pos</p>SS
	 * 
	 * @param row One row of a peptide file describing one peptide
	 * @throws FormatException
	 */
	public Peptide(String row) throws FormatException{
		this.row = row.replace("\"", "");
		rest = new Vector<String>();
		refSeq = new Vector<String>();
		ensemble_TRS = new Vector<String>();
		geneNames = new Vector<String>();
		positions = new TreeSet<Position>(new Position.PositionComparator());
		parseCsvRow();
	}

	/*
	 * Parsing the imported row by checking the format and split it.
	 * Taking only the variables needed for mapping, which are the proteins name (corr. UniProtKB-ID) and the peptides sequence.
	 * The Row is saved as String for later exports.
	 */
	private void parseCsvRow() throws FormatException{
		/* prot_acc	prot_desc	pep_query	pep_isunique	pep_exp_z	pep_score	pep_seq	pep_var_mod	pep_var_mod_pos
		 * ROA2_HUMAN	Heterogeneous nuclear ribonucleoproteins A2/B1 OS=Homo sapiens GN=HNRNPA2B1 PE=1 SV=2	252	1	2	140.52	NMGGPYGGGNYGPGGSGGSGGYGGR	Deamidated (NQ)	0.1000000000000000000000000.0
		 * CPSM_HUMAN	Carbamoyl-phosphate synthase [ammonia], mitochondrial OS=Homo sapiens GN=CPS1 PE=1 SV=2	99	1	2	5.51	ASRSFPFVSK	
		 */
		Pattern p = Pattern.compile("[\\w:\\.\\|]*\t.*\t\\d*\t\\d\t\\d\t(\\d|\\.)+\t[A-Z]+.*");
		if (!row.matches(p.pattern())){	
			throw new FormatException("peptide row incorrect");	
		}

		String[] splits = row.split("\t");

		prot_acc = splits[0];
		prot_desc = splits[1];
		pep_query = splits[2];
		pep_isunique = splits[3];
		pep_exp_z = splits[4];
		pep_score = splits[5];
		pep_seq = splits[6];
		pep_var_mod = "";
		pep_var_mod_pos1 = "";
		if (splits.length>7)
			pep_var_mod = splits[7];
		if (splits.length>8)
			pep_var_mod_pos1 = splits[8];
		if (splits.length>9)
			for (int i=9; i<splits.length; i++)
				rest.add(splits[i]);
	}
	
	/**
	 * <p>Extracts a gene name from the protein description.
	 * This is a public method as it has to be done from "outside" due to some input file properties.</p>
	 * 
	 * <p>For further details see source (!) of PeptideSet#readTxt(String).</p>
	 */
	public void extrGS(){
		String label1 = "Gene_Symbol=";
		String label2 = "GN=";
		int pos = prot_desc.indexOf(label1);
		if (pos >= 0){
			int from = pos + label1.length();
			int to = prot_desc.indexOf(" ", from);
			if(to>0){
				geneSymbol =  prot_desc.substring(from, to);
			}
			else{
				geneSymbol =  prot_desc.substring(from);
			}
		}
		pos = prot_desc.indexOf(label2);
		if (pos >= 0){
			int from = pos + label2.length();
			int to = prot_desc.indexOf(" ", from);
			if(to>0){
				geneSymbol =  prot_desc.substring(from, to);
			}
			else{
				geneSymbol =  prot_desc.substring(from);
			}
		}
	}

	/**
	 * Returns identifiers or references for the protein this peptide is part of.
	 * 
	 * @param ref Parameter to choose the protein identifier or identifier, "name" for the protein name (e.g. UniProtKB-ID), 
	 * "id" for protein ids (UniProtKB-AC and RefSeq as references to UCSC knownGenes and Ensemble_TRS as reference to EnsenbleGenes)
	 * @return Vector of identifiers
	 */
	public Vector<String> getProt(String ref){
		Vector<String> out = new Vector<String>();
		if (ref.matches("id")){
			out.add(prot_acc);
			out.add(uniqueIdentifier);
			out.addAll(refSeq);
			out.addAll(ensemble_TRS);
			return out;
		}
		if (ref.matches("name")){
			out.add(prot_acc);
			return out;
		}
		else
			return null;
	}

	/**
	 * Returns the protein accession (e.g. UniProtKB-ID or IPI).
	 * 
	 * @return The protein accession (UniProtKB-ID or IPI)
	 */
	public String getProt_acc(){
		return prot_acc;
	}

	/**
	 * Sets the protein accession.
	 * 
	 * @param prot_acc protein accession
	 */
	public void setProt_acc(String prot_acc){
		this.prot_acc = prot_acc;
	}

	/**
	 * Returns the peptide's aminoacid sequence.
	 * 
	 * @return The peptide's aminoacid sequence
	 */
	public String getSequence(){
		return pep_seq;
	}

	/**
	 * Sets this Peptide as mapped to a gene.
	 * 
	 * @param mapped set true if Peptide could be mapped to a gene
	 */
	public void setGeneMapped(boolean mapped){
		geneMapped = mapped;
	}

	/**
	 * Returns if this Peptide could be mapped to gene.
	 * 
	 * @return true if Peptide could be mapped, false else
	 */
	public boolean isGeneMapped(){
		return geneMapped;
	}

	/**
	 * Sets this Peptide as assigned to a protein.
	 * 
	 * @param proteinAssigned set true if Peptide is assigned to a protein
	 */
	public void setProteinAssigned(boolean proteinAssigned){
		this.proteinAssigned = proteinAssigned;
	}

	/**
	 * Returns if this Peptide is assigned to a protein.
	 * 
	 * @return true if Peptide is assigned, false else
	 */
	public boolean isProteinAssigned(){
		return proteinAssigned;
	}

	/**
	 * Returns a String representation of the peptide in the same format as the it was in the input.
	 * 
	 * @return Peptide's String representation
	 */
	public String getRow() {
		return prot_acc + "\t"  + prot_desc + "\t" + pep_query + "\t" + pep_isunique + "\t" + pep_exp_z + "\t" + 
				pep_score + "\t" + pep_seq + "\t" + pep_var_mod + "\t" + pep_var_mod_pos1;
	}

	/**
	 * Returns the peptide's score.
	 * @return peptide's score
	 */
	public String getPep_Score(){
		return pep_score;
	}

	/**
	 * Returns the peptide's charge.
	 * @return peptide's charge
	 */
	public String getPep_exp_z(){
		return pep_exp_z;
	}

	/**
	 * Sets the unique identifier (UniProtKB-AC)
	 * @param uniqueIdentifier usually the UniProtKB-AC
	 */
	public void setUniqueIdentifier(String uniqueIdentifier) {
		this.uniqueIdentifier = uniqueIdentifier;
	}

	/**
	 * Sets this Peptide as assigned to a gene.
	 * 
	 * @param geneAssigned set true if Peptide is assigned to a gene
	 */
	public void setGeneAssigned(boolean geneAssigned) {
		this.geneAssigned = geneAssigned;
	}
	
	/**
	 * Returns if this Peptide is assigned to a gene.
	 * 
	 * @return true if Peptide is assigned, false else
	 */
	public boolean isGeneAssigned() {
		return geneAssigned;
	}

	/**
	 * Returns the protein description.
	 * @return protein description
	 */
	public String getProt_desc() {
		return prot_desc;
	}

	/**
	 * Sets the protein description.
	 * @param prot_desc protein description
	 */
	public void setProt_desc(String prot_desc) {
		this.prot_desc = prot_desc;
	}

	/**
	 * Returns the gene name or gene symbol.
	 * @return
	 */
	public String getGN() {
		return geneSymbol;
	}

	/**
	 * Adds a RefSeq to a list.
	 * @param string one RefSeq
	 */
	public void addRefSeq(String string) {
		refSeq.add(string);
	}	

	/**
	 * Returns if the peptide is uniquely assigned to one protein during identification.
	 * @return "1" if unique, "0" else
	 */
	public String getPep_isunique(){
		return pep_isunique;
	}

	/**
	 * Set the match method, if the peptide could be matches to a gene.
	 * @param method method name
	 */
	public void setMatchMethod(String method) {
		this.method = method;
	}

	/**
	 * Returns the match method, if the peptide could be matches to a gene.
	 * @return method name
	 */
	public String getMatchMethod(){
		return method;
	}

	/**
	 * Returns the peptide's identification query value.
	 * @return query value
	 */
	public String getPep_query() {
		return pep_query;
	}

	/**
	 * <p>Adds a new {@Link Position} to a sorted Set.</p> 
	 * <p>Redundant Positions (same location; chromosome, strand, start and end positions) are rejected.</p>
	 * 
	 * @param pos  a new Position
	 * @return true if Position could be added, false if rejected
	 */
	public boolean addPosition(Position pos){
		return positions.add(pos);
	}

	/**
	 * Returns the Set of Positions.
	 * @return Set of Positions
	 */
	public TreeSet<Position> getPositions(){
		return positions;
	}

	/**
	 * Adds the gene the peptide could be assigned to.
	 * 
	 * @param name the gene's name
	 */
	public void addGene(String name) {
		geneNames.add(name);
	}

	/**
	 * Returns a Vector with all genes the peptide could be assigned to yet.
	 * @return Vector with gene names
	 */
	public Vector<String> getGenes(){
		return geneNames;
	}

	/**
	 * Adds an ensemble transcript name (Ensembl_TRS)
	 * @param ens Ensembl_TRS
	 */
	public void addEnsembl_TRS(String ens) {
		ensemble_TRS.add(ens);
	}

	/**
	 * Returns the peptide modifications.
	 * @return the pep_var_mod
	 */
	public String getPep_var_mod() {
		return pep_var_mod;
	}
	
	/**
	 * Returns a string representation of the peptide modification positions.
	 * @return the pep_var_mod_pos1
	 */
	public String getPep_var_mod_pos1() {
		return pep_var_mod_pos1;
	}
}


