/* 
 * File:   QualityUtil.h
 * Author: yangxi
 *
 * Created on September 26, 2013, 3:16 PM
 */

#ifndef HTIO2_QUALITY_UTIL_H
#define	HTIO2_QUALITY_UTIL_H

#include "htio2/Enum.h"
#include <cmath>
#include <stdint.h>
#include <vector>

namespace htio2
{

class FastqIO;


/**
 * guess quality encode from a range of quality
 * @param qual_ascii_min minimum quality in ascii
 * @param qual_ascii_max maximum quality in ascii
 * @return
 */
QualityEncode guess_quality_encode(char qual_ascii_min, char qual_ascii_max);

/**
 * guess quality encode from a series of quality
 * @param quality a series of quality in ascii
 * @return 
 */
QualityEncode guess_quality_encode(const std::string& quality);

QualityEncode guess_quality_encode(FastqIO& stream, size_t num_to_guess);

/**
 * get the possible range of decoded quality of a specific quality encoding
 * @param encode
 * @param qual_from output for minimum possible value of encode
 * @param qual_to output for maximum possible value of encode
 */
void get_quality_range(QualityEncode encode, int16_t& qual_from, int16_t& qual_to);

/**
 * get the ascii offset of a specific quality encoding
 * @param encode
 * @return The offset. Currently there are only 33-based and 64-based. -1 for invalid encode.
 */
inline int16_t get_quality_offset(QualityEncode encode)
{
    switch (encode)
    {
    case ENCODE_SANGER:
    case ENCODE_CASAVA_1_8:
        return 33;
    case ENCODE_SOLEXA:
    case ENCODE_ILLUMINA:
        return 64;
    default:
        return -1;
    }
}

inline float quality_to_probability(int16_t qual, QualityEncode encode)
{
    float tmp = pow(10.0, float(qual) / -10.0);

    switch (encode)
    {
    case ENCODE_SANGER:
    case ENCODE_CASAVA_1_8:
        return tmp;
    case ENCODE_SOLEXA:
    case ENCODE_ILLUMINA:
        return tmp / (1.0 + tmp);
    default:
        return -1;
    }
}

inline int16_t probability_to_quality(float p, QualityEncode encode)
{
    int16_t q = 0;
    switch (encode)
    {
    case ENCODE_SANGER:
    case ENCODE_CASAVA_1_8:
        q = round(-10 * log10(p));
        break;
    case ENCODE_SOLEXA:
    case ENCODE_ILLUMINA:
        q = round(-10 * log10(p / (1.0 - p)));
        break;
    default:
        q = -1;
        break;
    }

    if (encode == ENCODE_CASAVA_1_8)
    {
        if (q < 2) q = 2;
        else if (q > 41) q = 41;
    }

    return q;
}

/**
 * decode ascii quality
 * @param data quality in ascii
 * @param encode
 * @return decoded quality
 */
inline int16_t decode_quality(char data, QualityEncode encode)
{
    return data - get_quality_offset(encode);
}

/**
 * decode a series of ascii quality
 * @param input a series of quality in ascii
 * @param output a series of decoded quality
 * @param encode
 */
void decode_quality(const std::string& input, std::vector<int16_t>& output, QualityEncode encode);

/**
 * encode a series of quality to ascii
 * @param input a series of quality
 * @param output a series of quality in ascii
 * @param encode
 */
void encode_quality(const std::vector<int16_t>& input, std::string& output, QualityEncode encode);

/**
 * 
 * @param input
 * @param output
 * @param encode_input
 * @param encode_output
 */
void recode_quality(const std::string& input, std::string& output, QualityEncode encode_input, QualityEncode encode_output);

} // namespace htio2

#endif	/* HTIO2_QUALITY_UTIL_H */

