/**************************************************************************
/* OligoArray -- OligoArray: Genome-scale oligonucleotide design for microarrays
/*
/* Copyright (C) 2001 Jean Marie ROUILLARD (jean-marie.rouillard@cgm.cnrs-gif.fr)
/*
/* If you use this program, please cite Rouillard et al. 2002, Bioinformatics, 
/* Vol 18 pp
/*
/* This program is free software; you can redistribute it and/or modify
/* it under the terms of the GNU Library 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 Library General Public License for more details.
/*
/* You should have received a copy of the GNU Library General Public License
/* along with this program; see the file COPYING.LIB.  If not, write to 
/* the Free Software Foundation Inc., 59 Temple Place - Suite 330, 
/* Boston, MA  02111-1307 USA
/**************************************************************************/


import java.net.Socket;

import java.io.PrintStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.FileReader;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.File;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Arrays;
import java.util.Set;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;




public class OligoArray 
    extends JFrame
    implements ActionListener, DocumentListener, Runnable {

    private static OligoArray oligoArray;
    private Thread runSearch;
    static private JFrame myFrame;
    static private int oligoLength, distance, maxTm, tm, deltaTm, mxNbOligo;
    static private String linker5prime, linker3prime, listProhibited, seqFile, blastDB, saveAs, infoText, hostName, POST;
    static private BufferedReader readBuff;
    //static private PrintWriter logFile;
    static private PrintWriter skipped;
    static private PrintWriter oligoDb;
    static private boolean again, useGUI;
    static private JButton seq, blast, save, run, abort, exit;
    static private JTextField tefLength, tefDistance, tefTmRange, tefPlusMinus, tefTmStruct, tefTag5, tefTag3, tefProhi, tefSeq, tefBlast, tefSave, tefMxNbOligo;
    static private TextArea infoArea;





    public static void main (String argv[]) throws java.io.IOException {

	again = true;
	useGUI = false;
	oligoLength = 50;
	distance = 1000;
	tm = 88;
	deltaTm = 5;
	maxTm = 63;
	mxNbOligo = 1;
	listProhibited = "";
	linker5prime = "";
	linker3prime = "";
	seqFile = "";
	blastDB = "";
	saveAs = " ";
	infoText = "OligoArray 1.0\n\nGenome-Scale Oligonucleotide design for microarrays\n\nCopyright : Jean-Marie Rouillard (2001)\n\n\n\n";
	
	if (argv.length == 0) {

	    useGUI = true;
	    oligoArray = new OligoArray();
	    oligoArray.init();
	}
	else if (argv.length == 12) {

	    seqFile = argv[0];
	    if(argv[1].indexOf("nsq") != -1) {
		blastDB = argv[1].substring(0, argv[1].lastIndexOf("."));
	    }
	    else {
		blastDB = argv[1];
	    }
	    saveAs = argv[2]  ;
	    oligoLength = new Integer(argv[3]).intValue();
	    distance = new Integer(argv[4]).intValue();
	    tm = new Integer(argv[5]).intValue();
	    deltaTm = new Integer(argv[6]).intValue();
	    maxTm = new Integer(argv[7]).intValue();
	    mxNbOligo = new Integer(argv[8]).intValue();
	    listProhibited = argv[9];
	    linker5prime = argv[10];
	    linker3prime = argv[11];
	    displayInfos(infoText);
	    runOligoArray();
	}
	else {
	    System.out.println("Syntax should be java OligoArray to use the GUI\n or java OligoArray seqFile blastDB saveAs oligoLength distance TmRange TmRange(+/-) maxTm mxNbOligo listProhibited linker5prime linker3prime");
	}
    }



    private static void runOligoArray () throws java.io.IOException {

	//logFile = new PrintWriter(new FileOutputStream("SearchOligo.log"));
	//logFile.println("orf\tdistance\tlimitSpeci used\tlimitTmStruct used\toligo Tm");
	skipped = new PrintWriter(new FileOutputStream("RejectedSeq.fas"));
	oligoDb = new PrintWriter(new FileOutputStream(saveAs));
	readBuff = new BufferedReader (new FileReader(seqFile));

	if (! isBlastDbOK()) {
	    
	    JOptionPane.showMessageDialog(myFrame, "Sorry, but the Blast database you have chosen is not valid!","Error", JOptionPane.ERROR_MESSAGE);
	}
	else {

	    try {

		getCorrectMfoldURL();

		String line, orf, sequence, fastaSequence;
		boolean oligoFound;
		
		line = readBuff.readLine();
		
		while (line != null & again) {
		    orf = line.substring(1);
		    displayInfos("Start "+orf);
		    line = readBuff.readLine();
		    sequence = "";
		    while (line != null && line.indexOf('>') == -1) {
			sequence = sequence.concat(line);
			line = readBuff.readLine();
		    }
		    
		    fastaSequence = ">" + orf + "\n" + sequence;
		    
		    //We search for no more than 14 nt perfect matches but we can tolerate some few 15 nt perfect matches
		    if (analizeOrf(fastaSequence, 15, maxTm) || analizeOrf(fastaSequence, 16, maxTm)){
		    }
		    else if (makeFamily(fastaSequence, 15, maxTm)) {
		    }
		    else {
			skipped.println(fastaSequence); 
			displayInfos("No oligo for sequence "+orf+".\nThis sequence is saved in RejectedSeq.fas.");
		    }
		}
		displayInfos("All sequences from the input file have been processed\n\nGood Bye !");
		readBuff.close();
		skipped.close();
		//logFile.close();
		oligoDb.close();
	    }
	    catch (Exception e) {
		displayInfos(e.toString());
	    }
	}
    }





    private static boolean makeFamily (String fastaSequence,int limitSpeci, int limitTm) throws java.io.IOException {

	String orf = fastaSequence.substring(1, fastaSequence.indexOf("\n"));
	String sequence = fastaSequence.substring(fastaSequence.indexOf("\n") + 1).toUpperCase();;
	String line, oligoSequence, cousin, nbCousin, sister, nbSister, cluster;
	int length = sequence.length();
	int begin = length - oligoLength;
	int counter = 1;
	int nbOfCousin;
	boolean oligoFound = false;
	boolean rejectedByStructure = false;
	Hashtable cousin_listBegin = new Hashtable();
	Hashtable begin_listSister = new Hashtable();
	Hashtable begin_nbSister = new Hashtable();
	Hashtable nbCousin_listCousin = new Hashtable();
	Hashtable structureTm_position = new Hashtable();
	Vector listBegin, listCousin;
	StringTokenizer st;
	int currentTm = 0;
	double structureTm;


	while (begin >= 0 & length - begin < distance) {

	    oligoSequence = ">" + orf + "-" + counter + "\n" + sequence.substring(begin, begin + oligoLength);
	    currentTm = oligoTm(oligoSequence);
	    
	    if (checkProhibited(oligoSequence, listProhibited) &&
		tm - deltaTm <= currentTm  & currentTm <= tm + deltaTm) {

		cluster = (makeCluster(oligoSequence, 15));
		st = new StringTokenizer(cluster, ";");

		cousin = st.nextToken();
		nbCousin = st.nextToken();
		if (st.hasMoreTokens()) {
		    sister = st.nextToken();
		    nbSister =  st.nextToken();
		}
		else {
		    sister = null;
		    nbSister = null;
		}




		
		if (cousin_listBegin.containsKey(cousin)) {
		    listBegin = (Vector)cousin_listBegin.get(cousin);
		    listBegin.add(new Integer(begin));
		    cousin_listBegin.put(cousin, listBegin);
		}
		else {
		    listBegin = new Vector();
		    listBegin.add(new Integer(begin));
		    cousin_listBegin.put(cousin, listBegin);
		}



		if (sister != null) {
		    begin_listSister.put(new Integer(begin), sister);
		    begin_nbSister.put(new Integer(begin), new Integer(nbSister));
		}
		else {
		    begin_listSister.put(new Integer(begin), orf);
		    begin_nbSister.put(new Integer(begin), new Integer(1));
		}



		if (nbCousin_listCousin.containsKey(nbCousin)) {
		    listCousin = (Vector)nbCousin_listCousin.get(new Integer(nbCousin));
		    listCousin.add(cousin);
		    nbCousin_listCousin.put(new Integer(nbCousin), listCousin);
		}
		else {
		    listCousin = new Vector();
		    listCousin.add(cousin);
		    nbCousin_listCousin.put(new Integer(nbCousin), listCousin);
		}
	    }
	    begin = begin - 10;
	    counter++;
	}


	//just a way to sort the list of nbCousin (int) from the smaller to the larger
	Set mySet = nbCousin_listCousin.keySet();
	Integer myArray[] = new Integer[mySet.size()];
	myArray = (Integer[])mySet.toArray(myArray);
	Arrays.sort(myArray);
	int countOligo = 0;

	    
	for (int i = 0; i < myArray.length; i++) {
	    
	    nbOfCousin = myArray[i].intValue();
	    
	    if (nbOfCousin > 0) {
		
		listCousin= (Vector)nbCousin_listCousin.get(new Integer(nbOfCousin));
		for (int k = 0; k < listCousin.size(); k++) {
			
		    cousin = (String)listCousin.elementAt(k);
		    listBegin = (Vector)cousin_listBegin.get(cousin);
		    
		    for (int j = 0; j < listBegin.size(); j++) {
						    
			if (begin_nbSister.containsKey((Integer)listBegin.elementAt(j))) {
			    
			    begin = ((Integer)listBegin.elementAt(j)).intValue();
			    oligoSequence = ">" + orf + "\n" + sequence.substring(begin, begin + oligoLength);
			    currentTm = oligoTm(oligoSequence);
			    structureTm = checkStructure(addLinker(oligoSequence, linker5prime, linker3prime));

			    if (structureTm < limitTm) {
				
				sister = ((String)begin_listSister.get((Integer)listBegin.elementAt(j)));
				oligoDb.print(">" + orf + "\t" + (length - begin) + "\t" +  
					      currentTm + "\t" + structureTm + "\t" + sister + "\t");
				st = new StringTokenizer(cousin);
				while (st.hasMoreTokens()) {
				    String aCousin = st.nextToken();
				    if (sister.indexOf(aCousin) == -1) {
					oligoDb.print(aCousin + " ");
				    }
				}
				oligoDb.println("\n" + sequence.substring(begin, begin + oligoLength));
				oligoDb.flush();			
				displayInfos("Done (non spec.)");
				//logFile.println(orf + "\t" + (length - begin) + "\t" + limitSpeci + "\t" + limitTm + "\t" + currentTm);
				//logFile.flush();
				countOligo++;
				oligoFound = true;
				rejectedByStructure = false;
				if(countOligo == mxNbOligo) {
				    i = myArray.length;
				    j = listBegin.size();
				    k = listCousin.size();
				}
			    }
			    else {
				rejectedByStructure = true;
				if (!structureTm_position.contains(new Double(structureTm))) {
				    structureTm_position.put(new Double(structureTm), new Integer(j));
				}
			    }
			}
		    }
		    if (rejectedByStructure) {

			//just a way to sort the list of structureTm (Double) from the smaller to the larger
			Set tmSet = structureTm_position.keySet();
			Double tmArray[] = new Double[tmSet.size()];
			tmArray = (Double[])tmSet.toArray(tmArray);
			Arrays.sort(tmArray);
			
			if (tmArray.length > 0 && tmArray[0].doubleValue() < maxTm) {
			    
			    int j = ((Integer)structureTm_position.get(tmArray[0])).intValue();
			    currentTm = oligoTm(">" + orf + "\n" + sequence.substring(begin, begin + oligoLength));
			    structureTm = tmArray[0].doubleValue();
			    sister = ((String)begin_listSister.get((Integer)listBegin.elementAt(j)));
			    oligoDb.print(">" + orf + "\t" + (length - begin) + "\t" +  
					  currentTm + "\t" + structureTm + "\t" + sister + "\t");
			    st = new StringTokenizer(cousin);
			    while (st.hasMoreTokens()) {
				String aCousin = st.nextToken();
				if (sister.indexOf(aCousin) == -1) {
				    oligoDb.print(aCousin + " ");
				}
			    }
			    oligoDb.println("\n" + sequence.substring(begin, begin + oligoLength));
			    oligoDb.flush();
			    displayInfos("Done (non spec.)");
			    //logFile.println(orf + "\t" + (length - begin) + "\t" + limitSpeci + "\t" + limitTm + "\t" + currentTm);
			    //logFile.flush();
			    oligoFound = true;
			    countOligo++;
			    if(countOligo == mxNbOligo) {
				i = myArray.length;
				j = listBegin.size();
				k = listCousin.size();
			    }
			}
		    }
		}
		
	    }
	}
	return oligoFound;
    }







    private static String makeCluster (String fastaSequence, int limitSpeci) throws java.io.IOException {

	String callAndArgs = "blastall -W 7 -F F -S 1 -e 100 -p blastn -d " + blastDB;
	fastaSequence = fastaSequence + "\n\n";
	
	Runtime rt = Runtime.getRuntime();
	Process child;
	OutputStream pipeTo;
	BufferedReader pipeFrom;
	String orf = fastaSequence.substring(1, fastaSequence.indexOf("-"));
	String orfSister;
	Vector vecSister = new Vector();
	Vector vecCousin = new Vector();
	int size, percent;
	boolean saveIt = true;
	String cluster[];
	
	
	child = rt.exec(callAndArgs);
	pipeTo = child.getOutputStream();
	pipeFrom = new BufferedReader(new InputStreamReader(child.getInputStream()));
	
	pipeTo.write(fastaSequence.getBytes(), 0, fastaSequence.getBytes().length);
	pipeTo.close();
	
	
	String line = pipeFrom.readLine();
	
	
	while (line != null && line.indexOf(">") == -1) {
	    line = pipeFrom.readLine();
	}

  
	while (line != null && line.indexOf("Lambda") != 0) {
 
	    orfSister = line.substring(line.indexOf(">") + 1).trim();
	    line = pipeFrom.readLine();
	    while (line != null && line.indexOf(">") == -1 && line.indexOf("Lambda") != 0) {
	   
		if (line.indexOf("Identities") == 1) {
		    size = new Integer(line.substring(line.indexOf("/") + 1, 
						      line.indexOf("(") - 1)).intValue();
		    percent = new Integer(line.substring(line.indexOf("(") + 1, 
							 line.indexOf(")") - 1)).intValue();

		    if (size >= oligoLength - 5 && percent >= 90 ) {
			vecSister.add(orfSister);	    
		    }

		    //Sister orf are included in the cousin list
		    if (size >= 51 && percent >= 50) {
			   vecCousin.add(orfSister);	
		    }
		    else if (size >= 36 && size < 51 && percent >= 60) {
			vecCousin.add(orfSister);		    
		    }
		    else if (size >= limitSpeci && size < 36 && percent >= 70) {
			vecCousin.add(orfSister);
		    }
		}
		line = pipeFrom.readLine();
		
	    }
	}
	pipeFrom.close();
	child.destroy();


	//Convert to String[] to be abble to sort elements
	cluster = new String[vecCousin.size()];
	cluster = (String[])vecCousin.toArray(cluster);
	Arrays.sort(cluster);
	
	//It will be easier to handle a String instead of a String[]
	String cousin = "";
	for (int i=0; i<cluster.length; i++) {
	    cousin = cousin.concat(cluster[i] + " ");
	}

	//We concatenate the cousin with its size for convenience
	cousin = cousin + ";" + cluster.length;
	
	String sister = "";
	if (vecSister.size() > 0) {
	    //Convert to String[] to be abble to sort elements
	    cluster = new String[vecSister.size()];
	    cluster = (String[])vecSister.toArray(cluster);
	    Arrays.sort(cluster);
	    
	    //It will be easier to handle a String instead of a String[]
	    sister = "";
	    for (int i=0; i<cluster.length; i++) {
		sister = sister.concat(cluster[i] + " ");
	    }

	    //We concatenate the sister with its size for convenience
	    sister = sister + ";" +  cluster.length;
	}

	//return the list of cousins, their number, the list of sisters and their number. Everything semi-colon separated.
	return cousin + ";" + sister;
    }






    private static boolean analizeOrf (String fastaSequence, int limitSpeci, int limitTm) throws java.io.IOException { 

	String orf = fastaSequence.substring(1, fastaSequence.indexOf("\n"));
	String sequence = fastaSequence.substring(fastaSequence.indexOf("\n") + 1).toUpperCase();;
	String line, oligoSequence;
	int length = sequence.length();
	int begin = length - oligoLength;
	int counter = 1;
	boolean oligoFound = false;
	boolean rejectedByStructure = false;
	int currentTm, countOligo = 0;
	double structureTm;
	Hashtable structureTm_position = new Hashtable();

	while (begin >= 0 & length - begin < distance) {
	    oligoSequence = ">" + orf + "-" + counter + "\n" + sequence.substring(begin, begin + oligoLength);
	    currentTm = oligoTm(oligoSequence);
	    oligoSequence = addLinker(oligoSequence, linker5prime, linker3prime);
	  
	    if (checkProhibited(oligoSequence, listProhibited) &&
		tm - deltaTm <= currentTm  & currentTm <= tm + deltaTm &&
		checkSpecificity(oligoSequence, limitSpeci)) {
		structureTm = checkStructure(oligoSequence);

		if (structureTm < limitTm) {

		    oligoDb.println(">" + orf + "\t" + (length - begin) + "\t"+ 
				    currentTm + "\t" + structureTm + "\t" +orf + "\tnull\n" +
				    sequence.substring(begin, begin + oligoLength));
		    oligoDb.flush();
		    displayInfos("Done (spec.)");
		    //logFile.println(orf + "\t" + (length - begin) + "\t" + limitSpeci + "\t" + limitTm + "\t" + currentTm);
		    //logFile.flush();
		    countOligo++;
		    if(countOligo == mxNbOligo) {
			begin = -1;
		    }
		    oligoFound = true;
		    rejectedByStructure = false;
		    break;
		}
		else {
		    rejectedByStructure = true;
		    if (!structureTm_position.contains(new Double(structureTm))) {
			structureTm_position.put(new Double(structureTm), new Integer(begin));
		    }
		}
	    }
	    begin = begin - 10;
	    counter++;
	}

	//we can accept few oligo with a structure with Tm up to maxTm
	if (rejectedByStructure) {
	    
	    //just a way to sort the list of structureTm (Double) from the smaller to the larger
	    Set mySet = structureTm_position.keySet();
	    Double myArray[] = new Double[mySet.size()];
	    myArray = (Double[])mySet.toArray(myArray);
	    Arrays.sort(myArray);
	    
	    for (int i = 0; i < mxNbOligo; i++) {

		if (myArray.length > 0 && myArray[0].doubleValue() < maxTm) {

		    begin = ((Integer)structureTm_position.get(myArray[0])).intValue();
		    currentTm = oligoTm(">" + orf + "\n" + sequence.substring(begin, begin + oligoLength));
		    structureTm = myArray[0].doubleValue();
		    oligoDb.println(">" + orf + "\t" + (length - begin) + "\t"+ 
				    currentTm + "\t" + structureTm + "\t" + orf + "\tnull\n" +
				    sequence.substring(begin, begin + oligoLength));
		    oligoDb.flush();
		    displayInfos("Done (spec.)");
		    //logFile.println(orf + "\t" + (length - begin) + "\t" + limitSpeci + "\t" + limitTm + "\t" + currentTm);
		    //logFile.flush();
		    oligoFound = true;
		}
	    }
	}
	return oligoFound;
    }



    private static double checkStructure (String fastaSequence) throws java.io.IOException {
		
	final int port = 80;
	final String type = "Content-type: application/x-www-form-urlencoded";
	final String fieldLength = "Content-length: ";

	String sequence = fastaSequence.substring(fastaSequence.indexOf("\n") + 1);
	String aRequest = "SEQ_NAME=test&SEQUENCE=" + sequence + "%0D%0A%3B&NA=DNA&NA_CONC=1.0&MG_CONC=0.0&UNITS=M&T=50";
		
	double tmStruct = 0;
	String fileName = "";
	boolean structComputed = false;

	try {	
	    Socket myConnec = new Socket(hostName, port);
	    
	    BufferedReader fromServer = new BufferedReader (new InputStreamReader(myConnec.getInputStream()));
	    PrintStream toServer = new PrintStream (myConnec.getOutputStream());
	    
	    String requestLength = (new Integer (aRequest.length())).toString();
	    

	    toServer.println(POST + "\n" +
			     type + "\n" +
			     fieldLength + requestLength +
			     "\n\n" +
			     aRequest);
	    
	    
	    String line;
	    while (true) {
		
		line = fromServer.readLine();
		
		if (line == null) {
		    break;
		}
		if (line.indexOf("T<SUB>m</SUB> &nbsp; = &nbsp; ") != -1 ) {

		    structComputed = true;
		    if (line.substring(line.indexOf("T<SUB>m</SUB> &nbsp; = &nbsp; ") + 29 ).indexOf("*") == -1) {
			tmStruct = new Double(line.substring(line.indexOf("T<SUB>m</SUB> &nbsp; = &nbsp; ") + 29 )).doubleValue();
		    }
		    else {
			tmStruct = 0;
		    }
		}
	    }

	    //Sometimes, I loss the connection, so I have to comput again the structure...
	    if (!structComputed) {
		tmStruct = checkStructure (fastaSequence);
	    }
	}
	catch (Exception e) {
	    displayInfos("There is a problem of network. Cannot reach the Mfold server");
	    tmStruct = checkStructure (fastaSequence);
	}
	return tmStruct;
    }


    private static boolean checkSpecificity(String fastaSequence, int limitSpec) throws java.io.IOException {
	
	String callAndArgs = "blastall -W 7 -F F -S 1 -e 100 -p blastn -d " + blastDB;
	fastaSequence = fastaSequence + "\n\n";

	Runtime rt = Runtime.getRuntime();
	Process child;
	OutputStream pipeTo;
	BufferedReader pipeFrom;
	String orf = fastaSequence.substring(1, fastaSequence.indexOf("-"));

	int size, percent;
	boolean saveIt = true;
	
	    
	child = rt.exec(callAndArgs);
	pipeTo = child.getOutputStream();
	pipeFrom = new BufferedReader(new InputStreamReader(child.getInputStream()));
	
	pipeTo.write(fastaSequence.getBytes(), 0, fastaSequence.getBytes().length);
	pipeTo.close();
	
	
	String line = pipeFrom.readLine();
	
	
	while (line != null && line.indexOf(">") == -1) {
	    line = pipeFrom.readLine();
	}

    again:
	while (line != null) {
	
	    //Skipp self matching
	    if (line.indexOf(">" + orf) != -1) {

		line = pipeFrom.readLine();
		while (line != null && line.indexOf(">") == -1) {
		    line = pipeFrom.readLine();
		}
	    }
	    else {
		line = pipeFrom.readLine();
		while (line != null && line.indexOf(">") == -1) {
		    if (line.indexOf("Identities") == 1) {
			size = new Integer(line.substring(line.indexOf("/") + 1, 
							  line.indexOf("(") - 1)).intValue();
			percent = new Integer(line.substring(line.indexOf("(") + 1, 
							     line.indexOf(")") - 1)).intValue();


			if (size >= 51 && percent >= 50) {
			    pipeFrom.close();
			    child.destroy();
			    saveIt = false;
			    break again;			    
			}
			else if (size >= 36 && size < 51 && percent >= 60) {
			    pipeFrom.close();
			    child.destroy();
			    saveIt = false;
			    break again;					    
			}
			else if (size >= limitSpec && size < 36 && percent >= 70) {
			    pipeFrom.close();
			    child.destroy();
			    saveIt = false;
			    break again;		    
			}
		    }
		    line = pipeFrom.readLine();
		}
	    }
	}
	pipeFrom.close();
	child.destroy();
	
	return saveIt;
    }

    private static void  getCorrectMfoldURL () {

	final int port = 80;
	hostName = "www.bioinfo.rpi.edu";
	POST = "POST /applications/mfold/old/cgi-bin/rouillard2.cgi HTTP/1.0";
	final String type = "Content-type: application/x-www-form-urlencoded";
	final String fieldLength = "Content-length: ";

	String sequence = "ATCGTCGTACGTGCGTACGTGCAGTCATGCGTACGACTG";
	String aRequest = "SEQ_NAME=test&SEQUENCE="+sequence+"%0D%0A%3B&NA=DNA&NA_CONC=1.0&MG_CONC=0.0&UNITS=M&T=50";
		
	double tmStruct = 0;
	String fileName = "";
	boolean structComputed = false;

	try {	
	    Socket myConnec = new Socket(hostName, port);
	    
	    BufferedReader fromServer = new BufferedReader (new InputStreamReader(myConnec.getInputStream()));
	    PrintStream toServer = new PrintStream (myConnec.getOutputStream());
	    
	    String requestLength = (new Integer (aRequest.length())).toString();
	    
	    toServer.println(POST + "\n" +
			     type + "\n" +
			     fieldLength + requestLength +
			     "\n\n" +
			     aRequest);
	    
	    
	    String line = fromServer.readLine();
	    while (line !=null) {
		//In case of the document as moved, here is what we expect :
		//The document has moved <A HREF="http://www.bioinfo.rpi.edu/applications/mfold/old/cgi-bin/rouillard2.cgi">here</A>.<P>  
		if (line.indexOf("rouillard2.cgi") != -1) {
		    hostName = line.substring(line.indexOf("http://") + 7 ,
					      line.indexOf("/",line.indexOf("http://") + 7));
		    POST = "POST " + 
			line.substring(line.indexOf("/",line.indexOf("http://") + 7),
				       line.indexOf("rouillard2.cgi") + 14) + 
			" HTTP/1.0";

		}
		line = fromServer.readLine();
	    }

	}
	catch (Exception e) {
	    displayInfos("There is a problem of network. Cannot reach the Mfold server");
	}

    }

    private static boolean isBlastDbOK() throws java.io.IOException {
	
	boolean result = false;

	displayInfos("Start Blast database testing. If this test take more than few\nsecondes and nothing appears on this windows, the Blast\nprogram is probably not properly installed.");
	String callAndArgs = "blastall -W 7 -F F -S 1 -e 100 -p blastn -d " + blastDB;
	
	Runtime rt = Runtime.getRuntime();
	Process child;
	OutputStream pipeTo;
	BufferedReader pipeFrom;
	String seqtest = ">name\nATGGGTATGCCATTGCATTACGGCATCGCGCCTAGCGCGCTACGGCATCGAGCGGTGTGTGGCTTTGCGAGCGGCGCATCGG\n\n";

	    
	child = rt.exec(callAndArgs);
	pipeTo = child.getOutputStream();
	pipeFrom = new BufferedReader(new InputStreamReader(child.getInputStream()));
	
	pipeTo.write(seqtest.getBytes(), 0, seqtest.getBytes().length);
	pipeTo.close();
	displayInfos("\nCommand sent to Blast. Waiting for reply. If the test stop here,\nthis means that Java cannot communicate with OligoArray.");
	
	String line = pipeFrom.readLine();
	
	
	while (line != null && line.indexOf(">") == -1) {
	    if (line.indexOf("Database:") == 0) {
		displayInfos("\n" + line);
		result = true;
	    }
	    line = pipeFrom.readLine();
	}

	pipeFrom.close();
	child.destroy();

	if(result) {
	    displayInfos("\nThe Blast database was succesfully tested and is valid\nOligoArray will now process your sequences.\n\n\n");
	}
	else {
	    displayInfos("\nThe Blast database was succesfully tested but is not valid\nPlease choose a valid database.\n\n\n");
	}
	return result;
    }



    private static boolean checkProhibited (String fastaSequence, String list) {
	boolean accept = true;
	String sequence = fastaSequence.substring(fastaSequence.indexOf("\n") + 1);
	StringTokenizer st = new StringTokenizer(list, ";");

	while (st.hasMoreTokens()) {
	    if (sequence.indexOf(st.nextToken()) != -1) {
		accept = false;
	    }
	}

	return accept;
    }




    private static String addLinker (String fastaSequence, String before, String after) {
	
	String sequence = fastaSequence.substring(fastaSequence.indexOf("\n") + 1);
	return fastaSequence.substring(0, fastaSequence.indexOf("\n") + 1) + before + sequence + after;
    }



    private static boolean verifiedSeq(String seq, String valid) {
	boolean verified = true;
	boolean charVerified;
	char[] seqArray = seq.toUpperCase().toCharArray();
	char[] validArray = valid.toUpperCase().toCharArray();

	for (int i = 0; i < seqArray.length & verified; i++) {

	    charVerified = false;
	    for (int j = 0; j < validArray.length & ! charVerified; j++) {
		charVerified = charVerified || (seqArray[i] == validArray[j]);
	    }
	    verified = verified & charVerified;
	}
	return verified;
    }



    
    private static int oligoTm (String fastaSequence) {
	//This Tm is computed using the nearest-neighbor model. cf J. SantaLucia, 1998, PNAS, vol 95, pp 1460-1465 for details and data
	//Data are for a sodium concentration of 1M
	//Even if I do not use it, I compute dG. All parameters are here if someone want to work with deltaG instead of Tm.
	//I choose to use a DNA concentration of 10E-6 M
	
	char seqArray[] = fastaSequence.substring(fastaSequence.indexOf("\n") +1).toCharArray();
	char n1, n2;
	int l = seqArray.length;
	double dG = 0;
	double dH = 0;
	double dS = 0;
	double concDNA = 0.000001;

	final double dGAA = -1;
	final double dHAA = -7.9;
	final double dSAA = -22.2;
    
	final double dGAT = -0.88;
	final double dHAT = -7.2;
	final double dSAT = -20.4;
    
	final double dGAC = -1.44;
	final double dHAC = -8.4;
	final double dSAC = -22.4;
    
	final double dGAG = -1.28;
	final double dHAG = -7.8;
	final double dSAG = -21;
    
    
	final double dGTA = -0.58;
	final double dHTA = -7.2;
	final double dSTA = -21.3;
    
	final double dGTT = -1;
	final double dHTT = -7.9;
	final double dSTT = -22.2;
    
	final double dGTC = -1.3;
	final double dHTC = -8.2;
	final double dSTC = -22.2;
    
	final double dGTG = -1.45;
	final double dHTG = -8.5;
	final double dSTG = -22.7;
    
    
	final double dGCA = -1.45;
	final double dHCA = -8.5;
	final double dSCA = -22.7;
    
	final double dGCT = -1.28;
	final double dHCT = -7.8;
	final double dSCT = -21;
    
	final double dGCC = -1.84;
	final double dHCC = -8;
	final double dSCC = -19.9;
    
	final double dGCG = -2.17;
	final double dHCG = -10.6;
	final double dSCG = -27.2;
    
    
	final double dGGA = -1.3;
	final double dHGA = -8.2;
	final double dSGA = -22.2;
    
	final double dGGT = -1.44;
	final double dHGT = -8.4;
	final double dSGT = -22.4;
    
	final double dGGC = -2.24;
	final double dHGC = -9.8;
	final double dSGC = -24.4;
    
	final double dGGG = -1.84;
	final double dHGG = -8;
	final double dSGG = -19.9;
    
    
	final double dGInitGC = 0.98;
	final double dHInitGC = 0.1;
	final double dSInitGC = -2.8;
    
	final double dGInitAT = 1.03;
	final double dHInitAT = 2.3;
	final double dSInitAT = 4.1;

	for (int i = 0; i < l - 1; i++) {
	    n1 = seqArray[i];
	    n2 = seqArray[i+1];
	    if (n1 == 'A' | n1 == 'a') {
		if (n2 == 'A' | n2 == 'a') {
		    dG = dG + dGAA;
		    dH = dH + dHAA;
		    dS = dS + dSAA;
		}
		else if (n2 == 'T' | n2 == 't') {
		    dG = dG + dGAT;
		    dH = dH + dHAT;
		    dS = dS + dSAT;
		}
		else if (n2 == 'C' | n2 == 'c') {
		    dG = dG + dGAC;
		    dH = dH + dHAC;
		    dS = dS + dSAC;
		}		
		else if (n2 == 'G' | n2 == 'g') {
		    dG = dG + dGAG;
		    dH = dH + dHAG;
		    dS = dS + dSAG;
		}
	    }
	    else if (n1 == 'T' | n1 == 't') {
		if (n2 == 'A' | n2 == 'a') {
		    dG = dG + dGTA;
		    dH = dH + dHTA;
		    dS = dS + dSTA;
		}
		else if (n2 == 'T' | n2 == 't') {
		    dG = dG + dGTT;
		    dH = dH + dHTT;
		    dS = dS + dSTT;
		}
		else if (n2 == 'C' | n2 == 'c') {
		    dG = dG + dGTC;
		    dH = dH + dHTC;
		    dS = dS + dSTC;
		}		
		else if (n2 == 'G' | n2 == 'g') {
		    dG = dG + dGTG;
		    dH = dH + dHTG;
		    dS = dS + dSTG;
		}
	    }
	    else if (n1 == 'C' | n1 == 'c') {
		if (n2 == 'A'| n2 == 'a') {
		    dG = dG + dGCA;
		    dH = dH + dHCA;
		    dS = dS + dSCA;
		}
		else if (n2 == 'T' | n2 == 't') {
		    dG = dG + dGCT;
		    dH = dH + dHCT;
		    dS = dS + dSCT;
		}
		else if (n2 == 'C' | n2 == 'c') {
		    dG = dG + dGCC;
		    dH = dH + dHCC;
		    dS = dS + dSCC;
		}		
		else if (n2 == 'G' | n2 == 'g') {
		    dG = dG + dGCG;
		    dH = dH + dHCG;
		    dS = dS + dSCG;
		}
	    }
	    else if (n1 == 'G' | n1 == 'g') {
		if (n2 == 'A' | n2 == 'a') {
		    dG = dG + dGGA;
		    dH = dH + dHGA;
		    dS = dS + dSGA;
		}
		else if (n2 == 'T' | n2 == 't') {
		    dG = dG + dGGT;
		    dH = dH + dHGT;
		    dS = dS + dSGT;
		}
		else if (n2 == 'C' | n2 == 'c') {
		    dG = dG + dGGC;
		    dH = dH + dHGC;
		    dS = dS + dSGC;
		}		
		else if (n2 == 'G' | n2 == 'g') {
		    dG = dG + dGGG;
		    dH = dH + dHGG;
		    dS = dS + dSGG;
		}
	    }
	}
	n1 = seqArray[0];
	if ((n1 == 'A' | n1 == 'a') || (n1 == 'T' | n1 == 't')) {
	    
	    dG = dG + dGInitAT;
	    dH = dH + dHInitAT;
	    dS = dS + dSInitAT;
	}
	else if ((n1 == 'C' | n1 == 'c') || (n1 == 'G' | n1 == 'g')) {
	
	    dG = dG + dGInitGC;
	    dH = dH + dHInitGC;
	    dS = dS + dSInitGC;
	}
	n1 = seqArray[l - 1];
	if ((n1 == 'A' | n1 == 'a') || (n1 == 'T' | n1 == 't')) {
	    
	    dG = dG + dGInitAT;
	    dH = dH + dHInitAT;
	    dS = dS + dSInitAT;
	}
	else if ((n1 == 'C' | n1 == 'c') || (n1 == 'G' | n1 == 'g')) {

	    dG = dG + dGInitGC;
	    dH = dH + dHInitGC;
	    dS = dS + dSInitGC;
	}
	
	//cf J. SantaLucia, 1998, PNAS, vol 95, pp 1460-1465 for details on the formula below
	return new Double(((1000*dH) / (dS+(1.9872*Math.log(concDNA/4)))) - 273.15).intValue();
    }


    //A method to display some informations to user
    private static void displayInfos (String info) {

	if (useGUI) {
	    infoArea.append(info+"\n");
	}
	else {
	 
	    System.out.println(info);
	}
    }







    /////////////////////////////////////////////////////////////////////////////////////
    //                                                                                 //
    //              Below this point are all methods need for the GUI                  //
    //                                                                                 //
    /////////////////////////////////////////////////////////////////////////////////////




    public void init () {

	    try {
		UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
	    } 
	    catch (Exception e) { }

	    //The main frame that will contain specialized panels
	    myFrame = new JFrame("OligoArray 1.0");
	    myFrame.addWindowListener(new WindowAdapter() {
		    public void windowClosing(WindowEvent e) {
			System.exit(0);
		    }
		});

	    GridBagLayout gridbag = new GridBagLayout();
	    GridBagConstraints c = new GridBagConstraints();
	    Font myFont = new Font(null, Font.BOLD, 18);





	    // A panel devoted to general control
	    JPanel controlPanel = new JPanel();
	    controlPanel.setBorder(BorderFactory.createLineBorder(Color.black));
	    controlPanel.setLayout(gridbag);


	    //title line
	    JLabel labtitleFile = new JLabel("File data");
	    labtitleFile.setForeground(Color.black);
	    labtitleFile.setFont(myFont);
	    makeConstraints(c, 0, 0, 3, 1, "NONE","CENTER");
	    c.insets = new Insets(10,10,70,10);
	    gridbag.setConstraints(labtitleFile, c);
	    controlPanel.add(labtitleFile);



	    //Select where is the sequence file to process
	    seq = new JButton("Select Sequence file");
	    seq.addActionListener(this);
	    makeConstraints(c, 0, 1, 3, 1, "NONE","CENTER");
	    c.insets = new Insets(10,10,10,10);
	    gridbag.setConstraints(seq, c);
	    controlPanel.add(seq);


	    tefSeq = new JTextField( 20);
	    tefSeq.setEditable(false);
	    makeConstraints (c, 0, 2, 3, 1, "NONE","CENTER");
	    c.insets = new Insets(0,10,20,10);
	    gridbag.setConstraints(tefSeq, c); 
	    controlPanel.add(tefSeq);




	    //Select the blast database to use
	    blast = new JButton("Select Blast database (.nsq)");
	    blast.addActionListener(this);
	    makeConstraints(c, 0, 3, 3, 1, "NONE","CENTER");
	    c.insets = new Insets(10,10,10,10);
	    gridbag.setConstraints(blast, c);
	    controlPanel.add(blast);


	    tefBlast = new JTextField( 20);
	    tefBlast.setEditable(false);
	    makeConstraints (c, 0, 4, 3, 1, "NONE","CENTER");
	    c.insets = new Insets(0,10,20,10);
	    gridbag.setConstraints(tefBlast, c); 
	    controlPanel.add(tefBlast);



	    //Select the file to save results
	    save = new JButton("Save As :");
	    save.addActionListener(this);
	    makeConstraints(c, 0, 5, 3, 1, "NONE","CENTER");
	    c.insets = new Insets(10,10,10,10);
	    gridbag.setConstraints(save, c);
	    controlPanel.add(save);


	    tefSave = new JTextField( 20);
	    tefSave.setEditable(false);
	    makeConstraints (c, 0, 6, 3, 1, "NONE","CENTER");
	    c.insets = new Insets(0,10,30,10);
	    gridbag.setConstraints(tefSave, c); 
	    controlPanel.add(tefSave);




	    run = new JButton("Run");
	    run.addActionListener(this);
	    makeConstraints(c, 0, 7, 1, 1, "NONE","CENTER");
	    c.insets = new Insets(30,10,10,20);
	    gridbag.setConstraints(run, c);
	    controlPanel.add(run);



	    abort = new JButton("Cancel");
	    abort.addActionListener(this);
	    makeConstraints(c, 1, 7, 1, 1, "NONE","CENTER");
	    c.insets = new Insets(30,10,10,20);
	    gridbag.setConstraints(abort, c);
	    controlPanel.add(abort);


	    exit = new JButton("Exit");
	    exit.addActionListener(this);
	    makeConstraints(c, 2, 7, 1, 1, "NONE","CENTER");
	    c.insets = new Insets(30,10,10,20);
	    gridbag.setConstraints(exit, c);
	    controlPanel.add(exit);





	    


	    //A panel devoted to set oligonucleotide parameters.
	    JPanel oligoPanel = new JPanel();
	    oligoPanel.setBorder(BorderFactory.createLineBorder(Color.black));
	    oligoPanel.setLayout(gridbag);

	    //title line
	    JLabel labtitleOligo = new JLabel("Oligonucleotide data");
	    labtitleOligo.setForeground(Color.black);
	    labtitleOligo.setFont(myFont);
	    makeConstraints(c, 0, 0, 5, 1, "NONE","CENTER");
	    c.insets = new Insets(10,10,40,10);
	    gridbag.setConstraints(labtitleOligo, c);
	    oligoPanel.add(labtitleOligo);


	    //oligo length
	    JLabel labLength = new JLabel("Oligonucleotide length :");
	    labLength.setForeground(Color.black);
	    makeConstraints (c, 0, 1, 1, 1, "HORIZONTAL","EAST");
	    c.insets = new Insets(0,10,10,5);
	    gridbag.setConstraints(labLength, c);
	    oligoPanel.add(labLength);

	    tefLength = new JTextField(new Integer(oligoLength).toString(), 3);
	    tefLength.getDocument().addDocumentListener(this);
	    tefLength.getDocument().putProperty("name", "tefLength");
	    makeConstraints (c, 1, 1, 1, 1, "NONE","CENTER");
	    c.insets = new Insets(0,0,10,0);
	    gridbag.setConstraints(tefLength, c); 
	    oligoPanel.add(tefLength);

	    JLabel labNt1 = new JLabel("nt");
	    labNt1.setForeground(Color.black);
	    makeConstraints (c, 2, 1, 3, 1, "NONE","WEST");
	    c.insets = new Insets(0,5,10,5);
	    gridbag.setConstraints(labNt1, c); 
	    oligoPanel.add(labNt1);





	    //Distance between 5' oligo and stop codon
	    JLabel labDistance = new JLabel("Distance 5'- Stop :");
	    labDistance.setForeground(Color.black);
	    makeConstraints (c, 0, 2, 1, 1, "NONE","EAST"); 
	    c.insets = new Insets(0,10,10,5);
	    gridbag.setConstraints(labDistance, c);
	    oligoPanel.add(labDistance);

	    tefDistance = new JTextField(new Integer(distance).toString(), 3);
 	    tefDistance.getDocument().addDocumentListener(this);
	    tefDistance.getDocument().putProperty("name", "tefDistance");
	    makeConstraints (c, 1, 2, 1, 1, "NONE","CENTER");
	    c.insets = new Insets(0,0,10,0);
	    gridbag.setConstraints(tefDistance, c); 
	    oligoPanel.add(tefDistance);

	    JLabel labNt2 = new JLabel("nt");
	    labNt2.setForeground(Color.black);
	    makeConstraints (c, 2, 2, 3, 1, "HORIZONTAL","WEST");
	    c.insets = new Insets(0,5,10,5);
	    gridbag.setConstraints(labNt2, c); 
	    oligoPanel.add(labNt2);



	    //Tm range
	    JLabel labTmRange = new JLabel("Tm range :");
	    labTmRange.setForeground(Color.black);
	    makeConstraints (c, 0, 3, 1, 1, "NONE","EAST");
	    c.insets = new Insets(0,5,10,5); 
	    gridbag.setConstraints(labTmRange, c);
	    oligoPanel.add(labTmRange);

	    tefTmRange = new JTextField(new Integer(tm).toString(), 2);
	    tefTmRange.getDocument().addDocumentListener(this);
	    tefTmRange.getDocument().putProperty("name", "tefTmRange");
	    makeConstraints (c, 1, 3, 1, 1, "NONE","CENTER");
	    c.insets = new Insets(0,0,10,0);
	    gridbag.setConstraints(tefTmRange, c); 
	    oligoPanel.add(tefTmRange);

	    JLabel labPlusMinus = new JLabel("+/-");
	    labPlusMinus.setForeground(Color.black);
	    makeConstraints (c, 2, 3, 1, 1, "NONE","CENTER"); 
	    c.insets = new Insets(0,0,10,5);
	    gridbag.setConstraints(labPlusMinus, c);
	    oligoPanel.add(labPlusMinus);

	    tefPlusMinus = new JTextField(new Integer(deltaTm).toString(), 2);
	    tefPlusMinus.getDocument().addDocumentListener(this);
	    tefPlusMinus.getDocument().putProperty("name", "tefPlusMinus");
	    makeConstraints (c, 3, 3, 1, 1, "NONE","CENTER");
	    c.insets = new Insets(0,0,10,0);
	    gridbag.setConstraints(tefPlusMinus, c); 
	    oligoPanel.add(tefPlusMinus);

	    JLabel labDegrees1 = new JLabel("C");
	    labDegrees1.setForeground(Color.black);
	    makeConstraints (c, 4, 3, 1, 1, "NONE","WEST");
	    c.insets = new Insets(0,5,10,5);
	    gridbag.setConstraints(labDegrees1, c); 
	    oligoPanel.add(labDegrees1);




	    //Maximum Tm admited for secondary structure
	    JLabel labTmStruct = new JLabel("Max. Tm for structure :");
	    labTmStruct.setForeground(Color.black);
	    makeConstraints (c, 0, 4, 1, 1, "NONE","EAST"); 
	    c.insets = new Insets(0,10,10,5);
	    gridbag.setConstraints(labTmStruct, c);
	    oligoPanel.add(labTmStruct);

 	    tefTmStruct = new JTextField(new Integer(maxTm).toString(), 2);
	    tefTmStruct.getDocument().addDocumentListener(this);
	    tefTmStruct.getDocument().putProperty("name", "tefTmStruct");
	    makeConstraints (c, 1, 4, 1, 1, "NONE","CENTER");
	    c.insets = new Insets(0,0,10,0);
	    gridbag.setConstraints(tefTmStruct, c); 
	    oligoPanel.add(tefTmStruct);

	    JLabel labDegrees2 = new JLabel("C");
	    labDegrees2.setForeground(Color.black);
	    makeConstraints (c, 2, 4, 3, 1, "NONE","WEST"); 
	    c.insets = new Insets(0,5,10,5);
	    gridbag.setConstraints(labDegrees2, c);
	    oligoPanel.add(labDegrees2);




	    //Maximum number of oligo per imput sequence
	    JLabel labMxNbOligo = new JLabel("Max. number of oligo :");
	    labMxNbOligo.setForeground(Color.black);
	    makeConstraints (c, 0, 5, 1, 1, "NONE","EAST"); 
	    c.insets = new Insets(0,10,10,5);
	    gridbag.setConstraints(labMxNbOligo, c);
	    oligoPanel.add(labMxNbOligo);

 	    tefMxNbOligo = new JTextField(new Integer(mxNbOligo).toString(), 2);
	    tefMxNbOligo.getDocument().addDocumentListener(this);
	    tefMxNbOligo.getDocument().putProperty("name", "tefMxNbOligo");
	    makeConstraints (c, 1, 5, 1, 1, "NONE","CENTER");
	    c.insets = new Insets(0,0,10,0);
	    gridbag.setConstraints(tefMxNbOligo, c); 
	    oligoPanel.add(tefMxNbOligo);




	    //Add a tag in 5' of oligo
	    JLabel labTag5 = new JLabel("5' Tag :");
	    labTag5.setForeground(Color.black);
	    makeConstraints (c, 0, 6, 5, 1, "NONE","WEST"); 
	    c.insets = new Insets(20,10,10,5);
	    gridbag.setConstraints(labTag5, c);
	    oligoPanel.add(labTag5);

	    tefTag5 = new JTextField(20);
	    tefTag5.getDocument().addDocumentListener(this);
	    tefTag5.getDocument().putProperty("name", "tefTag5");
	    makeConstraints (c, 0, 7, 5, 1, "NONE","CENTER");
	    c.insets = new Insets(0,10,10,5);
	    gridbag.setConstraints(tefTag5, c); 
	    oligoPanel.add(tefTag5);




	    //Add  a tag in 3' of oligo
	    JLabel labTag3 = new JLabel("3' Tag :");
	    labTag3.setForeground(Color.black);
	    makeConstraints (c, 0, 8, 5, 1, "NONE","WEST");
	    c.insets = new Insets(20,10,10,5); 
	    gridbag.setConstraints(labTag3, c);
	    oligoPanel.add(labTag3);

	    tefTag3 = new JTextField(20);
	    tefTag3.getDocument().addDocumentListener(this);
	    tefTag3.getDocument().putProperty("name", "tefTag3");
	    makeConstraints (c, 0, 9, 5, 1, "NONE","CENTER");
	    c.insets = new Insets(0,10,10,5);
	    gridbag.setConstraints(tefTag3, c); 
	    oligoPanel.add(tefTag3);




	    //A list of sequence to avoid in oligo
	    JLabel labProhi = new JLabel("Prohibited sequences :");
	    labProhi.setForeground(Color.black);
	    makeConstraints (c, 0, 10, 5, 1, "HORIZONTAL","WEST"); 
	    c.insets = new Insets(20,10,10,5); 
	    gridbag.setConstraints(labProhi, c);
	    oligoPanel.add(labProhi);


	    tefProhi = new JTextField(20);
	    tefProhi.getDocument().addDocumentListener(this);
	    tefProhi.getDocument().putProperty("name", "tefProhi");
	    makeConstraints (c, 0, 11, 5, 1, "NONE","CENTER");
	    c.insets = new Insets(0,10,10,5);
	    gridbag.setConstraints(tefProhi, c); 
	    oligoPanel.add(tefProhi);






	    //A panel to display informations to user
	    JPanel infoPanel = new JPanel();
	    infoPanel.setBorder(BorderFactory.createLineBorder(Color.black));
	    infoPanel.setLayout(gridbag);


	    //A title for this panel
	    JLabel labtitleInfo = new JLabel("Status");
	    labtitleInfo.setForeground(Color.black);
	    labtitleInfo.setFont(myFont);
	    makeConstraints(c, 0, 0, 1, 1, "NONE","NORTH");
	    c.insets = new Insets(10,10,10,10);
	    gridbag.setConstraints(labtitleInfo, c);
	    infoPanel.add(labtitleInfo);


	    //A windows to display informations
	    infoArea = new TextArea(infoText,25,30);
	    infoArea.setEditable(false);
	    infoArea.setBackground(Color.white);
	    makeConstraints (c, 0, 1, 1, 1, "HORIZONTAL","SOUTH");
	    c.insets = new Insets(0,5,20,1);
	    gridbag.setConstraints(infoArea, c); 
	    infoPanel.add(infoArea);


	    //Final packaging
	    myFrame.getContentPane().add(controlPanel, BorderLayout.WEST);
	    myFrame.getContentPane().add(oligoPanel, BorderLayout.CENTER);
	    myFrame.getContentPane().add(infoPanel, BorderLayout.EAST);
	    myFrame.setBackground(Color.lightGray);
	    myFrame.setSize(myFrame.getPreferredSize());
	    myFrame.setResizable(true);
	    myFrame.pack();
	    myFrame.setVisible(true);
    }



    /**Just a method to set all parameters I use for GridBagConstraints*/
    protected void makeConstraints (GridBagConstraints gbc, 
					   int gx, int gy, int gw, 
					   int gh, 
					   String fill, String anc) {
	gbc.gridx = gx;
	gbc.gridy = gy;
	gbc.gridwidth = gw;
	gbc.gridheight = gh;


	if (fill.equals("NONE")) {
	    gbc.fill = GridBagConstraints.NONE;
	}
	else if (fill.equals("HORIZONTAL")) {
	    gbc.fill = GridBagConstraints.HORIZONTAL;
	}
	else if (fill.equals("VERTICAL")) {
	    gbc.fill = GridBagConstraints.VERTICAL;
	}
	else if (fill.equals("BOTH")) {
	    gbc.fill = GridBagConstraints.BOTH;
	}
	if (anc.equals("CENTER")) {
	    gbc.anchor = GridBagConstraints.CENTER;
	}
	else if (anc.equals("NORTH")) {
	    gbc.anchor = GridBagConstraints.NORTH;
	}
	else if (anc.equals("NORTHEAST")) {
	    gbc.anchor = GridBagConstraints.NORTHEAST;
	}
	else if (anc.equals("EAST")) {
	    gbc.anchor = GridBagConstraints.EAST;
	}
	else if (anc.equals("SOUTHEAST")) {
	    gbc.anchor = GridBagConstraints.SOUTHEAST;
	}
	else if (anc.equals("SOUTH")) {
	    gbc.anchor = GridBagConstraints.SOUTH;
	}
	else if (anc.equals("SOUTHWEST")) {
	    gbc.anchor = GridBagConstraints.SOUTHWEST;
	}
	else if (anc.equals("WEST")) {
	    gbc.anchor = GridBagConstraints.WEST;
	}
	else if (anc.equals("NORTHWEST")) {
	    gbc.anchor = GridBagConstraints.NORTHWEST;
	}
    }

    /**Some methods to capture and process text modification in the textField from the user*/
    public void insertUpdate(DocumentEvent e) {
	textChanged(e);
    }
    public void removeUpdate(DocumentEvent e) {
	textChanged(e);
    }
    public void changedUpdate(DocumentEvent e) {
	textChanged(e);
    }



    public void textChanged(DocumentEvent evt) {
 	Document whatsup = evt.getDocument();

	//User changes the oligo length
	if (whatsup.getProperty("name").equals("tefLength")) {

	    if (! tefLength.getText().equals("")) {
		
		try {
		    oligoLength = new Integer(tefLength.getText()).intValue();
		}
		catch (Exception excpt) {
		    JOptionPane.showMessageDialog(myFrame, "Please enter only an integer number","Error", JOptionPane.ERROR_MESSAGE); 
		}
	    }
	}
	    
	
	//User changes the distance
	else  if (whatsup.getProperty("name").equals("tefDistance")) {

	    if (! tefDistance.getText().equals("")) {
		
		try {
		    distance = new Integer(tefDistance.getText()).intValue();
		}
		catch (Exception excpt) {
		    JOptionPane.showMessageDialog(myFrame, "Please enter only an integer number","Error", JOptionPane.ERROR_MESSAGE);
		}
	    }
	}
	
	//User changes the tm for oligo
	else if (whatsup.getProperty("name").equals("tefTmRange")) {
	    if (! tefTmRange.getText().equals("")) {
		
		try {
		    tm = new Integer(tefTmRange.getText()).intValue();
		}
		catch (Exception excpt) {
		    JOptionPane.showMessageDialog(myFrame, "Please enter only an integer number","Error", JOptionPane.ERROR_MESSAGE); 
		}
	    }
	}
	else if (whatsup.getProperty("name").equals("tefPlusMinus")) {
	    if (! tefPlusMinus.getText().equals("")) {
		
		try {
		    deltaTm = new Integer(tefPlusMinus.getText()).intValue();
		}
		catch (Exception excpt) {
		    JOptionPane.showMessageDialog(myFrame, "Please enter only an integer number","Error", JOptionPane.ERROR_MESSAGE);
		}
	    }
	}
	
	
	//User changes the max Tm for structure
	else if (whatsup.getProperty("name").equals("tefTmStruct")) {
	    if (! tefTmStruct.getText().equals("")) {
		
		try {
		    maxTm = new Integer(tefTmStruct.getText()).intValue();
		}
		catch (Exception excpt) {
		    JOptionPane.showMessageDialog(myFrame, "Please enter only an integer number","Error", JOptionPane.ERROR_MESSAGE);
		}
	    }
	}
	
	//User changes the max nb of oligos per gene
	else if (whatsup.getProperty("name").equals("tefMxNbOligo")) {
	    if (! tefMxNbOligo.getText().equals("")) {
		
		try {
		    mxNbOligo = new Integer(tefMxNbOligo.getText()).intValue();
		}
		catch (Exception excpt) {
		    JOptionPane.showMessageDialog(myFrame, "Please enter only an integer number","Error", JOptionPane.ERROR_MESSAGE);
		}
	    }
	}
	//User chooses a 5' tag
	else if (whatsup.getProperty("name").equals("tefTag5")) {
	    if (! tefTag5.getText().equals("")) {
		
		try {
		    linker5prime = tefTag5.getText();
		    if (! verifiedSeq(linker5prime, "gatc")) {
			JOptionPane.showMessageDialog(myFrame, "Please enter only G, A, T or C","Error", JOptionPane.ERROR_MESSAGE);
		    }
		}
		catch (Exception excpt) {
		}
	    }
	}
	
	//User chooses a 3' tag
	else if (whatsup.getProperty("name").equals("tefTag3")) {
	    if (! tefTag3.getText().equals("")) {
		
		try {
		    linker3prime = tefTag3.getText();
		    if (! verifiedSeq(linker3prime, "gatc")) {
			JOptionPane.showMessageDialog(myFrame, "Please enter only G, A, T or C","Error", JOptionPane.ERROR_MESSAGE);
		    }
		}
		catch (Exception excpt) {
		}
	    }
	}
	    
	//User chooses a list of prohibited sequence, semi-colon separated
	else if (whatsup.getProperty("name").equals("tefProhi")) {
	    if (! tefProhi.getText().equals("")) {
		
		try {
		    listProhibited = tefProhi.getText();
		    if (! verifiedSeq(listProhibited, "gatc;")) {
			JOptionPane.showMessageDialog(myFrame, "Please enter only G, A, T, C or ; to separate sequences","Error", JOptionPane.ERROR_MESSAGE);
		    }
		}
		catch (Exception excpt) {
		}
	    }
	}
    }







    /**Users like to click on button. Please to consider its choice*/
    public void actionPerformed (ActionEvent evt) {
 	Object src = evt.getSource();

	if (src instanceof JButton) {
	    
	    if (src == seq) {
		
		FileDialog fd = new FileDialog(myFrame, "Select Sequence file", FileDialog.LOAD);
		fd.show();
		seqFile = fd.getDirectory() + fd.getFile();
		tefSeq.setText(seqFile);
	    }
	    
	    if (src == blast) {
		
		FileDialog fd = new FileDialog(myFrame, "Select Blast database", FileDialog.LOAD);
		fd.show();
		blastDB = fd.getDirectory() + fd.getFile();
		tefBlast.setText(blastDB);
		blastDB = blastDB.substring(0, blastDB.lastIndexOf("."));
	    }
	    
	    if (src == save) {
		FileDialog fd = new FileDialog(myFrame, "Save As", FileDialog.SAVE);
		fd.show();
		saveAs = fd.getDirectory() + fd.getFile();
		tefSave.setText(saveAs);
	    }
	    
	    if (src == run) {
		again = true;
		start();
	    }
	    
	    if (src == abort) {
		again = false;
		seq. setEnabled(true);
		blast.setEnabled(true);
		save.setEnabled(true);
		run.setEnabled(true);
		exit.setEnabled(true);
		tefLength.setEditable(true);
		tefDistance.setEditable(true);
		tefTmRange.setEditable(true);
		tefPlusMinus.setEditable(true);
		tefTmStruct.setEditable(true);
		tefMxNbOligo.setEditable(true);
		tefTag5.setEditable(true);
		tefTag3.setEditable(true);
		tefProhi.setEditable(true);
		displayInfos("Process cancelled by user");
	    }

	    if (src == exit) {
		myFrame.dispose();
		System.exit(0);	
	    }
	}
    }


    public void start () {

	runSearch = new Thread(this);
	runSearch.start();
    }

    public void stop () {
	//Don't stop directly this thread but use the again parameter to stop and close all open streams
    }

    public void run () {
	
	try {
	    seq.setEnabled(false);
	    blast.setEnabled(false);
	    save.setEnabled(false);
	    run.setEnabled(false);
	    exit.setEnabled(false);
	    tefLength.setEditable(false);
	    tefDistance.setEditable(false);
	    tefTmRange.setEditable(false);
	    tefPlusMinus.setEditable(false);
	    tefTmStruct.setEditable(false);
	    tefMxNbOligo.setEditable(false);
	    tefTag5.setEditable(false);
	    tefTag3.setEditable(false);
	    tefProhi.setEditable(false);

	    runOligoArray();

	    again = false;
	    seq. setEnabled(true);
	    blast.setEnabled(true);
	    save.setEnabled(true);
	    run.setEnabled(true);
	    exit.setEnabled(true);
	    tefLength.setEditable(true);
	    tefDistance.setEditable(true);
	    tefTmRange.setEditable(true);
	    tefPlusMinus.setEditable(true);
	    tefTmStruct.setEditable(true);
	    tefTag5.setEditable(true);
	    tefTag3.setEditable(true);
	    tefProhi.setEditable(true);
	}
	catch (Exception e) {
	    System.err.println(e);
	}
    }
}