/*
 * Copyright 2009 Lari Natri <lari.natri@gmail.com>
 *
 *  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/>.
 */

package compid.sequences;

/**
 *
 * @author Lari Natri
 */
import java.util.HashMap;

public class SimilarityMatrix
{

    private final Sequence components;
    private final double[][] matrix;
    private final double openGapScore;
    private final double extendGapScore;
    private GapScorer gapFunction;
    private final HashMap<Component, Integer> indexes;
    
    public SimilarityMatrix(Sequence componentList, double[][] scores,
            double openGapScore, GapScorer gapCalculator) throws Exception
    {
        this(componentList,scores,gapCalculator.getGapScore(0),
                gapCalculator.getGapScore(1));
        this.gapFunction = gapCalculator;
    }
    
    public SimilarityMatrix(Sequence componentList, double[][] scores,
            double linearGapScore) throws Exception    
    {
        this(componentList, scores, linearGapScore, linearGapScore);
    }

    public SimilarityMatrix(Sequence componentList, double[][] scores,
            double openGapScore, double extendGapScore) throws Exception
    {
        components = componentList;

        int compCount = components.getLength();
        if (compCount < 1 || scores.length != compCount) {
            throw new Exception("");
        }
        matrix = new double[compCount][compCount];
        for (int i = 0; i < compCount; ++i) {
            if (scores[i].length != compCount) {
                throw new Exception("");
            }
            for (int j = 0; j < compCount; ++j) {
                matrix[i][j] = scores[i][j];
            }
        }

        this.openGapScore = openGapScore;
        this.extendGapScore = extendGapScore;
        this.gapFunction = null;

        indexes = new HashMap<Component, Integer>(compCount,1);
        Component[] componentArray = components.getAsArray();
        for (int i = 0; i < compCount; ++i) {
            indexes.put(componentArray[i], i);
        }

        if (compCount != indexes.size()) {
            throw new Exception("");
        }
    }

    public final Component[] getComponents()
    {
        return components.getAsArray();
    }
    

    public final double[][] getScores()
    {
        double[][] result = new double[matrix.length][matrix.length];
        for (int i = 0; i < matrix.length; ++i) {
            for (int j = 0; j < matrix.length; ++j) {
                result[i][j] = matrix[i][j];
            }
        }
        return result;
    }
    
    public final double getOpenGapScore()
    {
        if (this.gapFunction!=null) return this.gapFunction.getGapScore(0);
        return this.openGapScore;
    }

    public final double getGapScore(int previousGaps) {
        if (this.gapFunction!=null) {
            return this.gapFunction.getGapScore(previousGaps);
        }
        else if (previousGaps<1) return this.openGapScore;
        return this.extendGapScore;
    }

    public final double getSimilarityScore(Component scA,
            Component scB) throws Exception
    {
        Integer indexA = indexes.get(scA);
        Integer indexB = indexes.get(scB);
        if (indexA == null || indexB == null) {
            throw new Exception("");
        }
        return matrix[indexA][indexB];
    }

    public final boolean isCompatible(Component sc)
    {
        if (sc == null || indexes.containsKey(sc) == false) {
            return false;
        } else {
            return true;
        }
    }

    public final boolean isCompatible(Sequence seq)
    {
        if (seq == null || seq.getLength() < 1) {
            return false;
        }

        Component[] seqArr = seq.getAsArray();
        for (int i = 0; i < seqArr.length; ++i) {
            if (this.isCompatible(seqArr[i]) == false) {
                return false;
            }
        }
        return true;
    }
}
