#include "stdafx.h"
#include "AlignmentsQ.h"

const unsigned int CAlignmentsQ::NULL_RECORD = std::numeric_limits<unsigned int>::max();
const unsigned short CAlignmentsQ::NULL_EDIT_DIS = std::numeric_limits<unsigned short>::max();

CAlignmentsQ::CAlignmentsQ(unsigned int iMaxCapacity)
{
    this->initialization(iMaxCapacity);
}

CAlignmentsQ::CAlignmentsQ(char cFlag_of_Queue_All_Best_One, unsigned int iMaxCapacity)
{
    this->initialization(iMaxCapacity);
    this->cFlag_of_Queue_All_Best_One = cFlag_of_Queue_All_Best_One;
}

CAlignmentsQ::~CAlignmentsQ(void)
{
    delete [] this->aiHitIndex;
    this->aiHitIndex = NULL;
    delete [] this->asdiff;
    this->asdiff = NULL;
}

void CAlignmentsQ::setQueue_All_Best_OneFlag(char cFlag_of_Queue_All_Best_One)
{
    //Simply a public function used to set the flag option to queue the best set or all or one alignment
    this->cFlag_of_Queue_All_Best_One = cFlag_of_Queue_All_Best_One;
}

char CAlignmentsQ::returnQueue_All_Best_OneFlag()
{
    return(this->cFlag_of_Queue_All_Best_One);
}

inline void CAlignmentsQ::pushHits(unsigned int startindex, unsigned short diff)
{
    if (this->load < this->iMaxCapacity) {
        this->aiHitIndex[this->load] = startindex;
        this->asdiff[this->load] = diff;
        this->load++;
    } else { // if the buffer is overflow.
        // cout << "Alignment Queue overflow" << endl;
    }
}

// return true if there is a same hit
bool CAlignmentsQ::checkHits(unsigned int startindex)
{
    for (unsigned int i = 0; i < this->load && i < this->iMaxCapacity; i++) {
        // If the maping is in the record, return(true);
        if (this->aiHitIndex[i] == startindex) {
            return(true);
        }
    }
    return(false);
}

// replace the record with the largest diff
int CAlignmentsQ::replaceHits(unsigned int startindex, unsigned short diff)
{
    int candidateId = -1;
    unsigned short candidateDiff = this->MinDiff;
    for (unsigned int i = 0; i < this->load && i < this->iMaxCapacity; i++) {
        if (diff < this->asdiff[i]) {
            if (candidateDiff < asdiff[i]) {
                candidateId = i;
            }
        }
    }
    if (candidateId >= 0) { // replace the worse record
        this->aiHitIndex[candidateId] = startindex;
        this->asdiff[candidateId] = diff;
        return(candidateId);
    }
    return(-1);
}

unsigned int CAlignmentsQ::saveHits(unsigned int startindex, unsigned short diff)
{
    bool recordIsAsGoodOrBetter = (diff <= this->MinDiff);
    bool saveAllMapping = (this->cFlag_of_Queue_All_Best_One == 'A');
    if ( recordIsAsGoodOrBetter || saveAllMapping) {
        if (diff < this->MinDiff) { // If the new alignment is better
            this->MinDiff = diff;
            if (this->cFlag_of_Queue_All_Best_One == 'B') {
                this->load = 0; //NOT this->clearHits();
                this->ForwardAlignmentLoad = 0;
            }
            // Definition of non-ambiguous is having a unique best mapping.
            this->AmbiguousFlag = false;
        } else { // linear check if the record has occured. (Buttlenect?)
            if (checkHits(startindex)) {
                return(this->load);
            }
            // This is put at the end to avoid duplicate record
            if (diff == this->MinDiff) {
                this->AmbiguousFlag = true;
            }
        }
        //(3) save the alignment in the queue
        bool qIsFull = (this->load >= this->iMaxCapacity);
        if (saveAllMapping && qIsFull) {
            this->replaceHits(startindex, diff);
        } else if (!qIsFull) {
            this->pushHits(startindex, diff);
        }
    }
    return(load);
}

int CAlignmentsQ::initialization(unsigned int MAX_Q_CAPACITY)
{
    this->iMaxCapacity = MAX_Q_CAPACITY;
    this->aiHitIndex = new unsigned int [MAX_Q_CAPACITY + 1];
    this->asdiff = new unsigned short [MAX_Q_CAPACITY + 1];
    this->load = 0;
    this->clearHits();
    // The default setting is to queue the best set (could be more than one) alignment
    this->cFlag_of_Queue_All_Best_One = 'B';
    for (unsigned int i = 0; i < this->iMaxCapacity; i++) {
        this->aiHitIndex[i] = NULL_RECORD;
        this->asdiff[i] = NULL_EDIT_DIS;
    }
    this->qualityScores = NULL;
    this->readID = 0;
    this->tag[0] = '\0';
    return (0);

}

int CAlignmentsQ::clearHits()
{
    for (unsigned int i = 0; i < min(this->load, this->iMaxCapacity); i++) {
        this->aiHitIndex[i] = NULL_RECORD;
        this->asdiff[i] = NULL_EDIT_DIS;
    }
    /* Marked this to increase speed */
    this->load = 0;
    this->ForwardAlignmentLoad = 0;
    this->MinDiff = MAX_READ_LENGTH;
    this->AmbiguousFlag = false;
    this->reverseIsBetter = false;
    return(0);
}

int CAlignmentsQ::sortHitsByLocation()
{
    // Sort according to distance
    vector< pair <int, int> > v;
    for (unsigned int i = 0; i < this->ForwardAlignmentLoad; i++) {
        v.push_back(pair<unsigned int, unsigned short>(this->aiHitIndex[i], this->asdiff[i]));
    }
    std::sort(v.begin(), v.end());
    for (unsigned int i = 0; i < this->ForwardAlignmentLoad; i++) {
        this->aiHitIndex[i] = v.at(i).first;
        this->asdiff[i] = (unsigned short)(v.at(i).second);
    }

    vector< pair <int, int> > w;
    for (unsigned int i = this->ForwardAlignmentLoad; i < this->load; i++) {
        w.push_back(pair<unsigned int, unsigned short>(this->aiHitIndex[i], this->asdiff[i]));
    }
    std::sort(w.begin(), w.end());
    for (unsigned int i = this->ForwardAlignmentLoad; i < this->load; i++) {
        this->aiHitIndex[i] = w.at(i).first;
        this->asdiff[i] = (unsigned short)w.at(i).second;
    }
    return(0);
}

int CAlignmentsQ::filterAlignments(unsigned int mismatchThreshold, bool bKeepAllAlignmentsInThreshold)
{
    int noOfMinMisMapping = 0;
    if (this->MinDiff > mismatchThreshold) {
        this->clearHits();
    } else {
        unsigned int i, j; // move record from i to j
        for (i = 0, j = 0; i < this->load; i++) {
            if (i == this->ForwardAlignmentLoad) {
                this->ForwardAlignmentLoad = j;
            }
            bool isMinMisMapping = ((unsigned int)this->asdiff[i] == this->MinDiff);
            if (isMinMisMapping) {
                noOfMinMisMapping++;
            }
            if (isMinMisMapping || (bKeepAllAlignmentsInThreshold\
                                    && (unsigned int) this->asdiff[i] <= mismatchThreshold)) {
                this->aiHitIndex[j] = this->aiHitIndex[i];
                this->asdiff[j] = this->asdiff[i];
                j++;
            }
        }
        this->load = j;
        this->AmbiguousFlag = (noOfMinMisMapping > 1);
    }
    return(this->load);
}

//Simply to a linear search in the queue to find the best alignment and return the genome InDex
unsigned int CAlignmentsQ::topHitsinList(void)
{
    unsigned short mindiff = NULL_EDIT_DIS;//Check it again
    unsigned int bestHitsIndex = 0;
    unsigned int i;
    for (i = 0; i < this->load; i++) {
        if (mindiff > this->asdiff[i]) {
            mindiff = this->asdiff[i];
            bestHitsIndex = i;
            this->AmbiguousFlag = false;
        } else if (mindiff == this->asdiff[i])
            this->AmbiguousFlag = true;
    }
    if (mindiff < MAXTOLERATSUBMIS) {
        this->reverseIsBetter = (bestHitsIndex >= this->ForwardAlignmentLoad);
        return(this->aiHitIndex[bestHitsIndex]);
    } else
        return(NULL_RECORD);//(Confuse between bad kmer and not found)
}

