/*
 * #%L
 * gitools-core
 * %%
 * Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group
 * %%
 * 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/gpl-3.0.html>.
 * #L%
 */
package org.gitools.matrix.model.compressmatrix;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.Arrays;

/**
 * This class contains all the values of an uncompressed row
 */
public class NotCompressRow {

    private static final Logger log = LoggerFactory.getLogger(NotCompressRow.class);
    private int row;
    private int totalLayers;
    private double[][] values;
    private boolean[] indices;
    private CompressDimension columns;

    /**
     * @param columns The columns dimension
     */
    public NotCompressRow(int row, int totalLayers, CompressDimension columns) {
        this.row = row;
        this.totalLayers = totalLayers;
        this.values = new double[columns.size()][totalLayers];
        this.indices = new boolean[columns.size()];
        Arrays.fill(indices, false);
        this.columns = columns;
    }

    /**
     * Gets row position.
     *
     * @return the row
     */
    public int getRow() {
        return row;
    }

    /**
     * Append a new column fields.
     *
     * @param value the fields
     */
    public void append(String value) {
        int column = columns.indexOf(AbstractCompressor.parseField(value, 0));
        double[] cells = parseDoubles(value);
        indices[column] = true;
        values[column] = cells;
    }

    /**
     * Converts the content of the row into a sequence of [column position int],[values length int],[values byte buffer]
     *
     * @return
     * @throws java.io.IOException
     */
    public byte[] toByteArray() throws IOException {

        // Write the buffer
        ByteArrayOutputStream bytes = new ByteArrayOutputStream(indices.length * ((8 * totalLayers) + 4));
        DataOutputStream out = new DataOutputStream(bytes);

        for (int column = 0; column < indices.length; column++) {

            if (indices[column]) {
                // Column position
                out.writeInt(column);

                // Column values
                byte[] line = createColumnLine(values[column]);
                out.write(line);
            }
        }
        out.close();
        bytes.close();
        return bytes.toByteArray();
    }

    private double[] parseDoubles(String fields) {

        double values[] = new double[totalLayers];

        for (int i = 0; i < totalLayers; i++) {
            String value = AbstractCompressor.parseField(fields, i + 2);
            values[i] = Double.NaN;
            if (value != null) {
                if (!value.equals("-")) {
                    try {
                        values[i] = Double.parseDouble(value);
                    } catch (NumberFormatException e) {
                        log.error("Malformed number '" + value + "'", e);
                    }
                }
            }
        }

        return values;
    }

    /**
     * Returns a byte array with all the fields except the two first that are the row and the column.
     *
     * @param values
     * @return
     * @throws java.io.UnsupportedEncodingException
     */
    private byte[] createColumnLine(double[] values) throws UnsupportedEncodingException {
        byte[] bytes = new byte[8 * values.length];
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        for (double value : values) {
            buffer.putDouble(value);
        }
        return bytes;
    }
}
