/*  
 *  src/org/thalia/bio/entity/factory/OntologyTabFactory.java
 * 
 *  $Author: veyrieras $, $Date: 2006-10-17 15:41:55 $, $Version$
 *
 *  MetaQTL : a Java package for QTL Meta-Analysis
 * 
 *  Copyright (C) 2003-2006  Jean-Baptiste Veyrieras, INRA, France.
 *
 *  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 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * 
 */
package org.metaqtl.bio.entity.factory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;

import org.metaqtl.bio.IBioEntity;
import org.metaqtl.bio.IOntologyTermProperties;
import org.metaqtl.bio.entity.Ontology;
import org.metaqtl.bio.entity.OntologyTerm;

/**
 * A factory to deal with {@link Ontology}.
 */
public class OntologyTabFactory extends BioEntityFactory {
	
	
	public final static String[] HEADER = {"Term_id", "Term_name", "Parent_id", "Synonyms", "Definition"};
	
	
	public IBioEntity load(InputStream inputStream) throws IOException {
		InputStreamReader reader = new InputStreamReader(inputStream);
		return load(reader);
	}

	/**
	 * 
	 * <ul>
	 * 	<li> Term_id        : the term identifient. </li> 
	 *  <li> Term_name      : the term name </li>
	 *  <li> Parent_id      : the term parent id </li>
	 *  <li> Synonyms       : the synonyms </li>
	 *  <li> Definition     : the definition of the term</li>
	 * </ul>
	 * 
	 */
	public IBioEntity load(Reader reader) throws IOException {
		BufferedReader buffer;
		String line,sbuff;
		String[] fields;
		StringTokenizer token;
		OntologyTerm root = null;
    	Hashtable terms = null;
		
		Ontology ontology = new Ontology();
        
        buffer = new BufferedReader(reader);
        
        try {
        	
        	int i,idx,cnt;
        	int[] hidx; // header name indices 
        	ArrayList children;
        	
        	line = buffer.readLine();
        	token = new StringTokenizer(line, "\t");
    		/* Check if the header is valid, i.e if there are four
    		 * columns */
        	
    		if (token.countTokens() == HEADER.length) {
    			
    			hidx = new int[HEADER.length];
    			idx  = 0;
    			while(token.hasMoreTokens()) {
    				for(i=0;i<HEADER.length;i++) {
    					if (HEADER[i].equalsIgnoreCase(token.nextToken())) {
    						hidx[i]=idx;
    						idx++;
    					}
    				}
    			}
    			if (idx != HEADER.length)
    				throw new IOException("Bad header column names");
    		} else {
    			throw new IOException("Bad header format");
    		}
    		
    		terms  = new Hashtable();
    		fields = new String[HEADER.length];
    		cnt=1;
    		
    		while ((line = buffer.readLine()) != null) {
    			
    			token = new StringTokenizer(line, "\t", true);
    			
    			if (token.countTokens() >= HEADER.length) {
    				for(i=0;i<HEADER.length;i++) fields[i]="";
    				idx = 0;
    				while(token.hasMoreTokens()) {
    					sbuff = token.nextToken();
    					if (!sbuff.equals("\t")){
	    					fields[idx++] = sbuff;
	    				}
    				}
    				if (!fields[hidx[2]].equals("")) {
	    				if (!terms.containsKey(fields[hidx[2]])) {
	    					children = new ArrayList();
	    					terms.put(fields[hidx[2]], children);
	    				} else {
	    					children = (ArrayList) terms.get(fields[hidx[2]]); 
	    				}
    				 } else children = null;
    				
    				OntologyTerm term = new OntologyTerm();
    				
    				term.setID(Long.parseLong(fields[hidx[0]]));
    				term.setName(fields[hidx[1]]);
    				
    				Properties prop = term.getProperties();
    				
    				prop.setProperty(IOntologyTermProperties.SYNONYMS, fields[hidx[3]]);
    				prop.setProperty(IOntologyTermProperties.DEFINITION, fields[hidx[4]]);
    				
    				term.setProperties(prop);
    				
    				if (!fields[hidx[2]].equals("")) {
    					children.add(term);
    				} else {
    					root = term;
    				}
    				
    			} else 
    				throw new IOException("Bad line format at " + cnt);
    			cnt++;
    		}
    		
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		if (terms != null) {
			
			ontology = new Ontology();
			
			OntologyTerm[] tree = buildTree(root, terms);
			
			root.setChildren(tree);
			
			ontology.setRoot(root);
			
		}
		
        
        return ontology;
		
	}

	/* (non-Javadoc)
	 * @see org.thalia.bio.extend.entity.factory.BioEntityFactory#unload(org.thalia.bio.entity.IBioEntity, java.io.Writer)
	 */
	public void unload(IBioEntity entity, Writer writer) throws IOException {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see org.thalia.bio.extend.entity.factory.BioEntityFactory#unload(org.thalia.bio.entity.IBioEntity, java.io.OutputStream)
	 */
	public void unload(IBioEntity entity, OutputStream stream) throws IOException {
		// TODO Auto-generated method stub
		
	}
	/**
	 * 
	 * @param term
	 * @param termTable
	 * @return
	 */
	private OntologyTerm[] buildTree(OntologyTerm term, Hashtable termTable) {
		ArrayList childrenList;
		OntologyTerm[] children = null;
		long id;
		
		id = term.getID();
		
		if (!termTable.containsKey(Long.toString(id))) return null;
		
		childrenList = (ArrayList) termTable.get(Long.toString(id));

		children = new OntologyTerm[childrenList.size()];
		
		childrenList.toArray(children);
		
		for(int i=0;i<children.length;i++) {
			children[i].setParentTerm(term);
			children[i].setChildren(buildTree(children[i], termTable));
		}
		
		return children;
	}
	
}
