//    <TMSEG: Prediction of Transmembrane Helices in Proteins.>
//    Copyright (C) 2014  Michael Bernhofer
//
//    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 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 General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
package predictors;
import io.ModelHandler;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import util.ErrorUtils;
import util.Globals;
import util.Mappings;
import weka.classifiers.Classifier;
import weka.classifiers.trees.RandomForest;
import weka.core.Attribute;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.SparseInstance;
import weka.core.converters.ArffSaver;
import data.Protein;
import data.Pssm;
/**
 * Class to predict transmembrane residues within a protein.
 */
public class HelixIndexer {
	
	
	public static final int 		indexNotTmh 	= 0;
	public static final int 		indexTmh 		= 1;
	public static final int 		indexSignal 	= 2;
	
	private ArrayList<Attribute> 	attributes 		= null;
	private Instances 				dataset 		= null;
	private Classifier 				classifier 		= null;
	private boolean 				isTrained 		= false;
	
	private double[] 				globalConsAa 	= null;
	private double[] 				globalNonConsAa = null;
	
	
	public HelixIndexer()
	{
		this.buildAttributes();
		this.initialize();
	}
	
	
	/**
	 * Initializes the attributes list for the Weka arff-format.
	 */
	private void buildAttributes()
	{
		this.attributes = new ArrayList<Attribute>();
		
		for (int i = 1; i <= ((2*Globals.INDEXER_WINDOW_SIZE)+1); ++i)
		{
			for (int j = 0; j < 21; ++j)
			{
				this.attributes.add(new Attribute(Mappings.intToAa(j)+"_"+i));
			}
		}
		
		this.attributes.add(new Attribute("conserved_hydrophobicity"));
		this.attributes.add(new Attribute("non-conserved_hydrophobicity"));
		
		this.attributes.add(new Attribute("conserved_hydrophobic"));
		this.attributes.add(new Attribute("non-conserved_hydrophobic"));
		
		this.attributes.add(new Attribute("conserved_pos_charged"));
		this.attributes.add(new Attribute("non-conserved_pos_charged"));
		
		this.attributes.add(new Attribute("conserved_neg_charged"));
		this.attributes.add(new Attribute("non-conserved_neg_charged"));
		
		this.attributes.add(new Attribute("conserved_polar"));
		this.attributes.add(new Attribute("non-conserved_polar"));
		
		ArrayList<String> lengths = new ArrayList<String>();
		
		lengths.add(String.valueOf("0"));
		lengths.add(String.valueOf("1"));
		lengths.add(String.valueOf("2"));
		lengths.add(String.valueOf("3"));
		lengths.add(String.valueOf("4"));
		
		this.attributes.add(new Attribute("n-term_distance", lengths));
		this.attributes.add(new Attribute("c-term_distance", lengths));
		this.attributes.add(new Attribute("global_length", lengths));
		
		for (int j = 0; j < 20; ++j)
		{
			this.attributes.add(new Attribute("global_conserved_"+Mappings.intToAa(j)));
			this.attributes.add(new Attribute("global_non-conserved_"+Mappings.intToAa(j)));
		}
		
		ArrayList<String> classes = new ArrayList<String>();
		
		classes.add(String.valueOf(HelixIndexer.indexNotTmh));
		classes.add(String.valueOf(HelixIndexer.indexTmh));
		classes.add(String.valueOf(HelixIndexer.indexSignal));
		
		this.attributes.add(new Attribute("class", classes));
	}
	
	
	/**
	 * Initializes the classifier and dataset.
	 */
	public void initialize()
	{
		this.isTrained 	= false;
		this.classifier = null;
		this.dataset 	= new Instances("HelixIndexer Model", this.attributes, 0);
		
		this.dataset.setClassIndex(this.attributes.size()-1);
	}
	
	
	/**
	 * Inputs a given list of proteins for the training data.
	 * 
	 * @param proteins
	 */
	public void input(List<Protein> proteins)
	{
		for (Protein protein : proteins)
		{
			this.input(protein);
		}
	}
	
	
	/**
	 * Inputs a given protein for the training data.
	 * 
	 * @param protein
	 */
	public void input(Protein protein)
	{
		if (protein == null) 				{return;}
		if (protein.getStructure() == null) {return;}
		if (protein.getPssm() == null) 		{return;}
		
		Pssm 	pssm 		= protein.getPssm();
		int 	length 		= pssm.getLength();
		char[] 	structure 	= protein.getStructure();
		
		if (pssm.getLength() != structure.length)
		{
			ErrorUtils.printError(HelixIndexer.class, "PSSM and structure annotation length do not match for " + protein.getName(), null);
			
			return;
		}
		
		this.globalComposition(pssm);
		
		for (int i = 0; i < length; ++i)
		{
			if (Mappings.ssToInt(structure[i]) != Mappings.indexUnknown)
			{
				this.addWindowToDatabase(pssm, i, structure);
			}
		}
	}
	
	
	/**
	 * Predicts transmembrane residues for a given list of proteins.
	 * 
	 * @param proteins
	 */
	public void predict(List<Protein> proteins)
	{
		for (Protein protein : proteins)
		{
			this.predict(protein);
		}
	}
	
	
	/**
	 * Predicts transmembrane residues for a given protein.
	 * 
	 * @param protein
	 */
	public void predict(Protein protein)
	{
		if (protein == null || protein.getPssm() == null) {return;}
		
		Pssm 		pssm 		= protein.getPssm();
		int 		length 		= pssm.getLength();
		int[] 		scoresSol 	= new int[length];


Answer: 		int[] 		scoresTmh 	= new int[length];