#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include "kmer_general.h"
#include "nucleotide.h"


/**
 * @brief  Given a k-mer representation in bits convert it to string
 * @param  64-bit k-mer representation 
 * @return  string with bp representation
 */
void bits2kmer31(kmerGWAS_kmer w, const uint8_t kmer_size, char * res) {
	const static char dict_bp[] = {'A','C','G','T'};
	const static kmerGWAS_kmer mask2bits = KMERGWAS_MASK;
	uint8_t i;
	if(!kmer_is_canonical(w, kmer_size))
		w = kmer_reverse_complement(w, kmer_size);
	for(i=0; i<kmer_size; i++) {
		res[kmer_size-i-1] = dict_bp[w & mask2bits];
		w = (w>>2);
	}
	res[kmer_size] = '\0';
	return;
}

bool kmer_is_canonical(kmerGWAS_kmer b, const uint8_t kmer_size){
	kmerGWAS_kmer flag_forward = b & KMERGWAS_FORWARD;
	kmerGWAS_kmer flag_reverse = b & KMERGWAS_REVERSE;
	if(flag_forward)
		return true;
	if(flag_reverse)
		return false;
	kmerGWAS_kmer bt = kmer_reverse_complement(b,  kmer_size);
	return b < bt;
}

char * kmer_string_alloc(const uint8_t kmer_size){
	char* res = malloc(sizeof(char) * (kmer_size+1));
	assert(res != NULL);
	return res;
}

void kmer_string_free(char * res){
	free(res);
}

kmerGWAS_kmer kmer_shift_and_insert(kmerGWAS_kmer kmer, char base, uint8_t kmer_size){
	kmerGWAS_kmer n = (kmerGWAS_kmer) char_to_binary_nucleotide(base);
	kmerGWAS_kmer kmer2 = kmer << 2;
	kmerGWAS_kmer wall  = (kmerGWAS_kmer)  KMERGWAS_MASK << kmer_size * 2;
	kmer2 |= (kmerGWAS_kmer) n;
	kmer2 &= ~wall;
	//fprintf(stderr, "shifting %llu %llu %c %llu %llu\n", kmer, kmer2, base, n, wall);
	return kmer2 ;
}

kmerGWAS_kmer kmer2bits(char * kmer, uint8_t kmer_size) {
	kmerGWAS_kmer b  = 0;
	kmerGWAS_kmer bt = 0;
	int i;
	for(i=0; i < kmer_size; i++) {
		b = kmer_shift_and_insert(b, kmer[i], kmer_size);
	}
	b &= KMERGWAS_ORIENTATION_MASK;
	// for(uint64_t i=0; i < kmer_size; i++) {
	// 	Nucleotide n = char_to_binary_nucleotide(kmer[kmer_size - i - 1]);
	// 	b |= ((uint64_t) n << (i*2));
	// }
	
	bt = kmer_reverse_complement(b,  kmer_size);
	if (bt < b)
		return bt | KMERGWAS_REVERSE;
	else
		return b  | KMERGWAS_FORWARD;
}

int kmer_compare_internal (const void * a, const void * b) {
	kmerGWAS_kmer ak = *(kmerGWAS_kmer*)a;
	kmerGWAS_kmer bk = *(kmerGWAS_kmer*)b; 
	ak &= KMERGWAS_ORIENTATION_MASK;
	bk &= KMERGWAS_ORIENTATION_MASK;
	return (ak > bk) - (ak < bk);
	//return ( ak - bk );
}

int kmer_compare (kmerGWAS_kmer  a, kmerGWAS_kmer  b){
	return kmer_compare_internal( &a, &b );
} 

