package org.baderlab.brain;

import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: moyez
 * Date: Sep 25, 2007
 * Time: 1:46:16 PM
 * To change this template use File | Settings | File Templates.
 */

/**
 * This class extends DistanceMatrix to include another matrix that stores, in each cell of the matrix,
 * the relative position of two protein profiles that have been aligned.
 */
public class AlignmentMatrix extends DistanceMatrix {
    private int[] alignmentPositionMatrix = null;
    private int matrixDim = 0;

    /**
     * Initializes a distance matrix of the given dimension
     *
     * @param matrixDim The dimension of the matrix
     */
    public AlignmentMatrix(int matrixDim) {
        super(matrixDim);
        this.matrixDim = matrixDim;
//        alignmentPositionMatrix = new int[(matrixDim * matrixDim - 1) / 2];
        alignmentPositionMatrix = new int[matrixDim * matrixDim];
    }

    /**
     * Put a value in the matrix at position (i,j)
     */
    public void setAlignmentValue(int i, int j, int value) {
//        if (j >= i) {
//            //don't store the diagonal or upper triangle
//            return;
//        }
        //calculate the position in the array given the lower triangular matrix coordinates
//        alignmentPositionMatrix[((i * (i - 1)) / 2) + j] = value;
        alignmentPositionMatrix[(i * matrixDim) + j] =  value;
    }

    /**
     * Get a value from the matrix at position (i,j)
     */
    public int getAlignmentValue(int i, int j) {
        if (i == j) {
            //return 0 for the diagonal
            return 0;
        }
//        if (j > i) {
//            //convert to lower triangle - the matrix is assumed to be symmetric
//            int oldj = j;
//            j = i;
//            i = oldj;
//        }
        //calculate the position in the array given the lower triangular matrix coordinates
//        return (alignmentPositionMatrix[((i * (i - 1)) / 2) + j]);
        return alignmentPositionMatrix[(i * matrixDim) + j];
    }


    /**
     * Calculates the pairwise alignment matrix based on ProteinProfileDistance.calculateOptimalAlignedPosition
     *
     * TODO: Allow generic alignment methods to be used.
     *       This would resemble DistanceMatrix.calcDistances which applies a given DistanceMetric to calculate
     *       the pairwise distances. Similarly, an extendible "AlignmentMetric" or "AlignmentMethod"
     *       would be provided and applied here to each p00rofile pair and the alignment stored in the matrix.
     *       Currently, the alignment stored in the matrix takes the form of an integer representing the relative
     *       positional offset of two aligned profiles. See ProteinProfileDistance.calculateOptimalAlignedPosition
     *       for more information on this metric.
     *
     * @param proteinProfileList     The list of objects to use to calculate an NxN distance matrix
     */
    public void calcAlignment(List proteinProfileList, String grouping) {
        //calculate the lower triangle of the distance matrix
        for (int i = 0; i < proteinProfileList.size(); i++) {
            ProteinProfile profile1 = (ProteinProfile) proteinProfileList.get(i);
            //for (int j = 0; j < i; j++) {
            for (int j = 0; j < proteinProfileList.size(); j++) {
                ProteinProfile profile2 = (ProteinProfile) proteinProfileList.get(j);
                this.setAlignmentValue(i, j, ProteinProfileDistance.calculateOptimalAlignedPosition(profile1, profile2, grouping));
            }
        }
    }

    /**
     * Returns an exact copy of this AlignmentMatrix object
     */
    public AlignmentMatrix copyAlignment() {
        AlignmentMatrix dm = new AlignmentMatrix(this.getMatrixDimension());
        System.arraycopy(this.alignmentPositionMatrix, 0, dm.alignmentPositionMatrix, 0, this.alignmentPositionMatrix.length);
        dm.setLabels(this.getLabels());
        return dm;
    }

    /**
     * Return the string representation of this matrix
     */
    public String toString() {
        StringBuffer sb = new StringBuffer();
        String lineSep = System.getProperty("line.separator");
        //header
        for (int i = 0; i < this.getLabels().size(); i++) {
            sb.append("\t" + (String) this.getLabels().get(i));
        }
        sb.append(lineSep);
        //labels rows
        for (int i = 0; i < this.getMatrixDimension(); i++) {
            sb.append(this.getLabels().get(i) + "\t");
            //for (int j = 0; j < i; j++) {
            for (int j = 0; j < this.getMatrixDimension(); j++) {
                sb.append(this.getValue(i, j) + "[" + this.getAlignmentValue(i, j) + "]");
//                if (j < i) {
//                    //don't output a tab for the last one
                    sb.append("\t");
//                }
            }
            sb.append(lineSep);
        }
        return (sb.toString());
    }

}
