#include "distance_matrix.h"
#include <stdio.h>
#include <string.h>

DistanceMatrix::DistanceMatrix(Sequences* seq)
{
    this->seq = seq;
    this->sequenceNumber = seq->getSequenceNumber();
    this->distanceMatrix = new double[this->sequenceNumber * this->sequenceNumber];
}

DistanceMatrix::~DistanceMatrix()
{
    delete[] this->distanceMatrix;
}

void DistanceMatrix::copyDistanceMatrix(DistanceMatrix* sourceDM)
{
    memcpy(this->distanceMatrix, sourceDM->distanceMatrix,
           sizeof(double)*this->sequenceNumber*this->sequenceNumber);
}

// identityMatrix.y / identityMatrix.x == 1 <==> 100% identity
// identityMatrix.y / identityMatrix.x == 0 <==>   0% identity
void DistanceMatrix::initDistanceMatrixWindow(int winX, int winY, int winSize, short2* identityMatrix)
{
    if (winX == winY)
    {
        for (int j=0; (j<winSize) && (winY*winSize + j < sequenceNumber); j++) //Y
            for (int i=0; i<j; i++) //X
            {
                //                   Y in global matrix                        X in global matrix
                this->distanceMatrix[(winY*winSize + j)*this->sequenceNumber + (winX*winSize + i)] =
                        1.0 - ((double)identityMatrix[j*winSize + i].y)/
                              ((double)identityMatrix[j*winSize + i].x);
            }
    }
    else
    {
        for (int j=0; (j<winSize) && (winY*winSize + j < sequenceNumber); j++) //Y
            for (int i=0; (i<winSize) && (winX*winSize + i < sequenceNumber); i++) //X
            {
                //                   Y in global matrix                        X in global matrix
                this->distanceMatrix[(winY*winSize + j)*this->sequenceNumber + (winX*winSize + i)] =
                        1.0 - ((double)identityMatrix[j*winSize + i].y)/
                              ((double)identityMatrix[j*winSize + i].x);
            }
    }
}

double DistanceMatrix::getElement(int x, int y)
{
    if(x==y)
        return 0;

    if(x>y) // above diagonal
        return this->distanceMatrix[x*sequenceNumber + y];
    else
        return this->distanceMatrix[y*sequenceNumber + x];
}

void DistanceMatrix::setElement(int x, int y, double value)
{
    if(x>y) // above diagonal
        this->distanceMatrix[x*sequenceNumber + y] = value;
    else
        this->distanceMatrix[y*sequenceNumber + x] = value;
}

void DistanceMatrix::saveToFile(char* filename)
{
    FILE* file = fopen(filename, "w");

    for(int j=0; j<sequenceNumber; j++) // Y
        for(int i=0; i<j; i++) // X
            fprintf(file, "%5d%5d%10.6f\n", i, j, getElement(i,j));

    fclose(file);
}

void DistanceMatrix::saveToPhylipFile(char* filename)
{
    FILE* file = fopen(filename, "w");

    fprintf(file, "%d\n", sequenceNumber);

    for(int j=0; j<sequenceNumber; j++) // Y
    {
        fprintf(file, "%s", seq->getSeqName(j));

        for(int i=0; i<j; i++) // X
            fprintf(file, "\t%.4f", getElement(i,j));

        fprintf(file, "\n");
    }

    fclose(file);
}

