#include "stdafx.h"
#include "chromosomeInBits.h"

CChromosomeInBits::CChromosomeInBits(void)
{
}

CChromosomeInBits::~CChromosomeInBits(void)
{
    delete [] this->pLowerBits;
    delete [] this->pUpperBits;
    // don't delete this->caChromosome.
}

CChromosomeInBits::CChromosomeInBits(char* caChromosome, unsigned int uiChrLength)
{
    this->caChromosome = caChromosome;
    this->uiChrLength = uiChrLength;
    this->uiChrLengthInWordSize = (uiChrLength - 1) / wordSize + 1;
    this->pLowerBits = new WORD_SIZE[uiChrLengthInWordSize];
    this->pUpperBits = new WORD_SIZE[uiChrLengthInWordSize];
    memset(pLowerBits, 0x00, uiChrLengthInWordSize);
    memset(pUpperBits, 0x00, uiChrLengthInWordSize);
    //TODO, find a better way to interpret the non ACGT character
    //If there are non-ACGT characters in caChromosome. The memory will still set to A

    unsigned int i;
    for (i = 0; i + 1 < this->uiChrLengthInWordSize; i++) {  //The first this->uiChrLengthInWordSize - 1 word are fully encoded
        encodeRead(&caChromosome[i * wordSize], wordSize,
                   &this->pUpperBits[i], &this->pLowerBits[i]);
    }
    encodeRead(&caChromosome[i * wordSize], uiChrLength - wordSize * i,
               &this->pUpperBits[i], &this->pLowerBits[i]);
}

int CChromosomeInBits::initialization()
{
    this->pLowerBits = NULL;
    this->pUpperBits = NULL;
    this->caChromosome = NULL;
    caSubstring[0] = '\0';
    return (0);
}

/* Note the chromosome encoding is NOT continuous. The more significant bits of each words encodes
 * nucleotides in front of those less significant bits. This encodeing make bits incontinuously mapped
 * to chromosome index. Note the first bit of each WORD represent the first base of each section
 */
CReadInBits CChromosomeInBits::getSubstringInBits(unsigned int uiGenomeIndex)
{
    CReadInBits r;
    unsigned int indexInWords = uiGenomeIndex / wordSize;
    unsigned int bitsShits = uiGenomeIndex % wordSize;
    if (this->uiChrLengthInWordSize > indexInWords) {
        r.UpperBits = this->pUpperBits[indexInWords] >> bitsShits;
        r.LowerBits = this->pLowerBits[indexInWords] >> bitsShits;
        if (bitsShits != 0) {
            r.UpperBits |= (this->pUpperBits[indexInWords + 1] << (wordSize - bitsShits));
            r.LowerBits |= (this->pLowerBits[indexInWords + 1] << (wordSize - bitsShits));
        }
    } else
        cout << "Warning, wrong chromosome index " << endl;
    return(r);
}

// eliminate the tail bits out of read length range
CReadInBits CChromosomeInBits::getSubstringInBits(unsigned int uiGenomeIndex, unsigned int uiSubstringLength)
{
    CReadInBits r;
    unsigned int indexInWords = uiGenomeIndex / wordSize;
    unsigned int bitsShits = uiGenomeIndex % wordSize;
    r.UpperBits = this->pUpperBits[indexInWords] >> bitsShits;
    r.LowerBits = this->pLowerBits[indexInWords] >> bitsShits;
    if (bitsShits != 0) {
        r.UpperBits |= (this->pUpperBits[indexInWords + 1] << (wordSize - bitsShits));
        r.LowerBits |= (this->pLowerBits[indexInWords + 1] << (wordSize - bitsShits));
    }
    unsigned int elimatedBitsNo = wordSize - uiSubstringLength;
    r.UpperBits <<= elimatedBitsNo;
    r.LowerBits <<= elimatedBitsNo;
    r.UpperBits >>= elimatedBitsNo;
    r.LowerBits >>= elimatedBitsNo;
    return (r);
}


char* CChromosomeInBits::getSubstring(unsigned int uiGenomeIndex)
{
    CReadInBits r = getSubstringInBits(uiGenomeIndex);
    decodeRead(caSubstring, wordSize, r.UpperBits, r.LowerBits);
    return (caSubstring);
}

char* CChromosomeInBits::getSubstring(unsigned int uiGenomeIndex, unsigned int uiSubstringLength)
{
    CReadInBits r = getSubstringInBits(uiGenomeIndex);
    decodeRead(caSubstring, wordSize, r.UpperBits, r.LowerBits);
    if (uiSubstringLength <= wordSize) {
        caSubstring[uiSubstringLength] = '\0';
    }

    if (uiGenomeIndex +  uiSubstringLength > uiChrLength) {
        if (uiChrLength > uiGenomeIndex)
            caSubstring[uiChrLength - uiGenomeIndex] = '\0';
        else {
            cout << "Warning, Wrong genome index " << endl;
            caSubstring[0] = '\0';
        }
    }
    return (caSubstring);
}

