/*
 * The Gemma project
 * 
 * Copyright (c) 2007 University of British Columbia
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package ubic.gemma.datastructure.matrix;

import java.io.IOException;
import java.io.Writer;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import ubic.gemma.model.expression.bioAssay.BioAssay;
import ubic.gemma.model.expression.designElement.CompositeSequence;
import ubic.gemma.model.genome.Gene;
import ubic.gemma.model.genome.biosequence.BioSequence;
import ubic.gemma.util.DateUtil;

/**
 * @author pavlidis
 * @version $Id$
 */
public class MatrixWriter<T> {

    private static NumberFormat nf = NumberFormat.getInstance();
    static {
        nf.setMaximumFractionDigits( 4 );
    }

    public void write( Writer writer, ExpressionDataMatrix<T> matrix, Map<Long, Collection<Gene>> geneAnnotations,
            boolean writeHeader ) throws IOException {
        this.write( writer, matrix, geneAnnotations, writeHeader, true );
    }

    /**
     * @param writer
     * @param matrix
     * @return
     */
    public void write( Writer writer, ExpressionDataMatrix<T> matrix, Map<Long, Collection<Gene>> geneAnnotations,
            boolean writeHeader, boolean writeSequence ) throws IOException {
        int columns = matrix.columns();
        int rows = matrix.rows();

        StringBuffer buf = new StringBuffer();
        if ( writeHeader ) {
            writeHeader( matrix, geneAnnotations, writeSequence, columns, buf );
        }

        for ( int j = 0; j < rows; j++ ) {
            CompositeSequence probeForRow = ( CompositeSequence ) matrix.getDesignElementForRow( j );
            buf.append( probeForRow.getName() );
            if ( writeSequence ) {
                BioSequence biologicalCharacteristic = probeForRow.getBiologicalCharacteristic();
                if ( biologicalCharacteristic != null ) buf.append( "\t" + biologicalCharacteristic.getName() );
            }

            buf.append( "\t" );
            addGeneInfo( buf, probeForRow, geneAnnotations );

            for ( int i = 0; i < columns; i++ ) {
                T val = matrix.get( j, i );
                buf.append( "\t" + val );
            }

            buf.append( "\n" );

        }
        writer.write( buf.toString() );
    }

    /**
     * @param matrix
     * @param geneAnnotations
     * @param writeSequence
     * @param columns
     * @param buf
     */
    private void writeHeader( ExpressionDataMatrix<T> matrix, Map<Long, Collection<Gene>> geneAnnotations,
            boolean writeSequence, int columns, StringBuffer buf ) {
        buf.append( "# Expression data file generated by Gemma\n" );
        buf.append( "# Generated " + DateUtil.convertDateToString( new Date() ) + "\n" );
        buf.append( "# If you use this file for your research, please cite the Gemma web site.\n" );
        buf.append( "Probe" );
        if ( writeSequence ) buf.append( "\tSequence" );
        if ( geneAnnotations != null && !geneAnnotations.isEmpty() ) {
            buf.append( "\tGeneSymbol\tGeneName" );
        }
        for ( int i = 0; i < columns; i++ ) {
            buf.append( "\t" + matrix.getBioMaterialForColumn( i ).getName() + ":" );
            for ( Object ba : matrix.getBioAssaysForColumn( i ) ) {
                buf.append( ( ( BioAssay ) ba ).getName() + "," );
            }
        }
        buf.append( "\n" );
    }

    /**
     * @param buf
     * @param probe The probe to add genes for.
     * @param geneAnnotations Map of composite sequence ids to genes. If null, nothing will be added to the text. If
     *        there are no genes for the probe, then blanks will be added.
     */
    private void addGeneInfo( StringBuffer buf, CompositeSequence probe, Map<Long, Collection<Gene>> geneAnnotations ) {
        if ( geneAnnotations == null || geneAnnotations.isEmpty() ) return;
        Collection<Gene> genes = geneAnnotations.get( probe.getId() );
        if ( genes != null ) {
            List<String> gs = new ArrayList<String>();
            List<String> gn = new ArrayList<String>();
            for ( Gene gene : genes ) {
                gs.add( gene.getOfficialSymbol() );
                gn.add( gene.getOfficialName() );
            }

            buf.append( StringUtils.join( gs.toArray(), '|' ) );
            buf.append( "\t" );
            buf.append( StringUtils.join( gn.toArray(), '|' ) );
        } else {
            buf.append( "\t\t" );
        }
    }

    /**
     * @param writer
     * @param matrix
     * @param writeHeader
     * @throws IOException
     */
    public void writeJSON( Writer writer, ExpressionDataMatrix<T> matrix, boolean writeHeader ) throws IOException {
        int columns = matrix.columns();
        int rows = matrix.rows();

        StringBuffer buf = new StringBuffer();

        buf.append( "{ 'numRows' : " + matrix.rows() + ", 'rows': " );

        buf.append( "[" );

        for ( int j = 0; j < rows; j++ ) {

            if ( j > 0 ) buf.append( "," );
            buf.append( "{" );
            buf.append( "'id' : \"" + matrix.getDesignElementForRow( j ).getName() + "\"" );
            BioSequence biologicalCharacteristic = ( ( CompositeSequence ) matrix.getDesignElementForRow( j ) )
                    .getBiologicalCharacteristic();
            if ( biologicalCharacteristic != null )
                buf.append( ", 'sequence' : \"" + biologicalCharacteristic.getName() + "\"" );

            buf.append( ", 'data' : [" );
            for ( int i = 0; i < columns; i++ ) {
                T val = matrix.get( j, i );
                if ( i > 0 ) buf.append( "," );
                buf.append( val );
            }

            buf.append( "]}\n" );

        }
        buf.append( "\n]}" );
        writer.write( buf.toString() );
    }

}

@@ -846,16 +846,43 @@
 l.Date;%0A
+import java.util.Iterator;%0A
 import j
@@ -3914,19 +3914,31 @@
 r ( 
-Object ba :
+Iterator%3CBioAssay%3E it =
  mat
@@ -3971,79 +3971,218 @@
  i )
- ) %7B%0A                buf.append( ( ( BioAssay ) ba ).getName() + %22,%22 );
+.iterator(); it.hasNext(); ) %7B%0A                BioAssay ba = it.next();%0A                buf.append( ba.getName() );%0A                if ( it.hasNext() ) %7B%0A                    buf.append( %22,%22 );%0A                %7D
 %0A   

remove trailing commas from header