/*  
 *  src/org/thalia/bio/entity/factory/MapMakerFactory.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.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.text.NumberFormat;
import java.util.Hashtable;

import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;
import org.metaqtl.bio.IBioEntity;
import org.metaqtl.bio.IBioLGroup;
import org.metaqtl.bio.IBioLocus;
import org.metaqtl.bio.entity.GeneticMap;
import org.metaqtl.bio.entity.LGroup;
import org.metaqtl.bio.entity.Marker;
import org.metaqtl.bio.util.StaticLocusPosition;

/**
 * @author Jean-Baptiste
 * 
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
public class MapMakerFactory extends BioEntityFactory  {

    /*
     * (non-Javadoc)
     * 
     * @see org.thalia.core.runtime.ITiedFactory#load(java.io.Reader)
     */
    public IBioEntity load(Reader reader) throws IOException {
        GeneticMap map = new GeneticMap();
        // Declare a LinkageGroup to fill map
        IBioLGroup group = null;
        // variable to convert distance
        boolean first = true;
        double distance = 0;
        // Initialize regular expression
        RE rChr = null;
        RE rName = null;
        RE rMarker = null;
        RE rLastMarker = null;
        RE rChrMercator = null;
        try {
            rChr = new RE("^\\*\\s*(.+?)\\s*$");
            rChrMercator = new RE("^\\chromosome\\s*(.+?)\\s*$");

            rName = new RE("^mapname=(.+)$");
            //rMarker = new RE("^\\s+\\*\\s+(\\S+)\\s+(\\S+)\\s+cM");
            rMarker = new RE("^\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+\\cM*");

            rLastMarker = new RE("^\\s+\\S*\\s+(\\S+)\\s+\\-+");
        } catch (RESyntaxException re) {
            System.out.println(re);
        }
        //
        // Read file
        //
        String line;
        BufferedReader red = new BufferedReader(reader);
        try {
            while ((line = red.readLine()) != null) {
                if (rName.match(line)) {
                    map.setName(rName.getParen(1));

                }
                if ((rChr.match(line)) || rChrMercator.match(line)) {

                    group = new LGroup(rChr.getParen(1), null);
                }
                if (rMarker.match(line)) {
                    Marker mrk = new Marker();
                    mrk.setGroup(group);
                    mrk.setName(rMarker.getParen(2));
                    String distTemp = rMarker.getParen(3);
                    //distTemp = distTemp.replace('.',',');

                    if (first) {
                        mrk.setPosition(new StaticLocusPosition(0));
                        distance = Double.parseDouble(distTemp);
                        first = false;
                    } else {
                        NumberFormat form = NumberFormat.getInstance();
                        form.setMaximumFractionDigits(2);
                        String temp = form.format(distance);
                        temp = temp.replace(',', '.');
                        double doudou = Double.parseDouble(temp);
                        mrk.setPosition(new StaticLocusPosition(doudou));
                        distance += Double.parseDouble(distTemp);

                    }
                    group.addLocus(mrk);
                }
                if (rLastMarker.match(line)) {
                    NumberFormat form = NumberFormat.getInstance();

                    Marker mrk = new Marker();
                    mrk.setGroup(group);
                    mrk.setName(rLastMarker.getParen(1));
                    String temp = form.format(distance);
                    temp = temp.replace(',', '.');
                    double dist = Double.parseDouble(temp);
                    mrk.setPosition(new StaticLocusPosition(dist));
                    group.addLocus(mrk);
                    map.addGroup(group);
                    distance = 0;
                    first = true;
                }
            }
        } catch (NumberFormatException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return map;
    }

    public IBioEntity load(InputStream stream) throws IOException {
        InputStreamReader r = new InputStreamReader(stream);
        return load(r);
    }

    public void unload(IBioEntity obj, Writer w) throws IOException {
        GeneticMap map = (GeneticMap) obj;
        PrintWriter writer = new PrintWriter(w);

        writer.println("mapname=" + map.getName());
        // Now, prepare formatting for each linkage group
        IBioLGroup[] groups = map.groups();
        for (int i = 0; i < groups.length; i++) {
            int maxName = 0;
            int maxDist = 0;
            double lastDist = 0;
            NumberFormat form = NumberFormat.getInstance();
            form.setMaximumFractionDigits(1);

            Hashtable pairwiseDistance = new Hashtable(groups[i]
                    .getLocusNumber() - 1);
            IBioLocus[] markers = (IBioLocus[]) groups[i].loci();
            sortPosition(markers);
            for (int j = 0; j < markers.length; j++) {
                IBioLocus mrk = markers[j];
                if (mrk.getName().length() > maxName) {
                    maxName = mrk.getName().length();
                }
                if (j < markers.length - 1) {
                    String pwdist = form.format(markers[j + 1].getPosition()
                            .absolute()
                            - mrk.getPosition().absolute());
                    pairwiseDistance.put(mrk.getName(), pwdist);
                    lastDist = mrk.getPosition().absolute();
                    if (pwdist.length() > maxDist) {
                        maxDist = pwdist.length();
                    }
                } else {
                    lastDist = mrk.getPosition().absolute();
                }
            }
            //
            // Write
            //

            writer.println("*" + groups[i].getName());
            writer.println(">map");
            writer
                    .println("===========================================================================");
            writer.println("Map:");
            writer.write("  Markers");
            writer.write(_print_space(3 + maxName + 6
                    - new String("Markers").length()));
            writer.write("Distance\n");
            for (int j = 0; j < markers.length; j++) {
                writer.write("   " + (j + 1) + "  " + markers[j].getName());
                String distance = (String) pairwiseDistance.get(markers[j]
                        .getName());
                if (distance != null)
                    distance = distance.replace(',', '.');

                if (j < markers.length - 1) {
                    writer.write(_print_space(7 + maxName
                            - markers[j].getName().length() + maxDist
                            - distance.length()));
                    writer.write(distance + " cM\n");
                } else {
                    writer.write(_print_space(5 + maxName
                            - markers[j].getName().length()));
                    writer.write("----------\n");
                }
            }
            writer.write(_print_space(3 + maxName + 6));
            String position = form.format(lastDist);
            position = position.replace(',', '.');
            writer.write(position + " cM " + markers.length
                    + " markers   log-likelihood=\n");
            writer
                    .println("===========================================================================");
        }
        try {
            w.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * @param i
     * @return
     */
    private String _print_space(int nb) {
        String toto = "";
        for (int i = 0; i < nb; i++)
            // TODO Auto-generated method stub
            toto += " ";

        return toto;
    }

    /**
     * @param markers
     * @return
     */
    private void sortPosition(IBioLocus[] markers) {
        int min;
        IBioLocus t;
        int N = markers.length;

        for (int i = 0; i < N - 1; ++i) {
            min = i;
            for (int j = i + 1; j < N; ++j)
                if (markers[j].getPosition().absolute() < markers[min]
                        .getPosition().absolute())
                    min = j;
            t = markers[min];
            markers[min] = markers[i];
            markers[i] = t;

        }
    }

	/* (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 {
		unload(entity, new OutputStreamWriter(stream));		
	}

}