#ifndef UTILITIES_HEADER_H
#define UTILITIES_HEADER_H

// C++ headers
#include <string>
#include <vector>
#include <tuple>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <stdexcept>
#include <unordered_map>

// Library headers


// Project headers



namespace SequenceUtilities {

// Codes for sequence validation.
static constexpr int VALID_SEQUENCE = 1;
static constexpr int INVALID_SEQUENCE = 0;

// Codes for chain type identification when building MSAs.
static constexpr int MSA_HEAVY_CHAIN_ONLY = 1;
static constexpr int MSA_LIGHT_CHAIN_ONLY = 2;


/// @brief Check that the input sequence is valid. Does not allow X or gaps
/// @param query_sequence The sequence to validate.
/// @return VALID_SEQUENCE if valid, INVALID_SEQUENCE otherwise.
int validate_sequence(std::string &query_sequence);

/// @brief Check that the input sequence is valid, allowing X but not gaps.
/// @param query_sequence The sequence to validate.
/// @return VALID_SEQUENCE if valid, INVALID_SEQUENCE otherwise.
int validate_x_sequence(std::string &query_sequence);

/// @brief Check that the input sequence is valid, allowing gaps but not X.
/// @param query_sequence The sequence to validate.
/// @return VALID_SEQUENCE if valid, INVALID_SEQUENCE otherwise.
int validate_gapped_sequence(std::string &query_sequence);



/// @brief Convert an input sequence to an array where each element is the
///        number corresponding to that amino acid (e.g. 0=A, 1=C etc.) Gaps
///        and X are not allowed.
/// @param queryAsIdx A pointer to the first element of the output array. Must
///                   have the same number of elements as query_sequence.
/// @param query_sequence The sequence to validate.
/// @return VALID_SEQUENCE if valid, INVALID_SEQUENCE otherwise.
int convert_sequence_to_array(int *queryAsIdx, std::string &query_sequence);


/// @brief Convert an input sequence to an array where each element is the
///        number corresponding to that amino acid (e.g. 0=A, 1=C etc.) Gaps
///        are not allowed but X is allowed.
/// @param queryAsIdx A pointer to the first element of the output array. Must
///                   have the same number of elements as query_sequence.
/// @param query_sequence The sequence to validate.
/// @return VALID_SEQUENCE if valid, INVALID_SEQUENCE otherwise.
int convert_x_sequence_to_array(int *queryAsIdx, std::string &query_sequence);


/// @brief Convert an input sequence to an array where each element is the
///        number corresponding to that amino acid (e.g. 0=A, 1=C etc.) Gaps
///        and X are allowed.
/// @param queryAsIdx A pointer to the first element of the output array. Must
///                   have the same number of elements as query_sequence.
/// @param query_sequence The sequence to validate.
/// @return VALID_SEQUENCE if valid, INVALID_SEQUENCE otherwise.
int convert_gapped_x_sequence_to_array(int *queryAsIdx,
        std::string &query_sequence);


/// @brief Sorts an input vector of position codes, respecting the rules of the
///        numbering scheme.
/// @param position_codes A vector of position codes each of which is a string.
/// @param scheme One of "imgt", "aho", "kabat" or "martin".
/// @param ordered_translated_codes A vector of sorted position codes in which
///        output is stored. Should be empty initially.
/// @return Returns VALID_SEQUENCE if no error, INVALID_SEQUENCE if an error.
int sort_position_codes_utility(std::vector<std::string> &position_codes,
        std::string scheme, std::vector<std::string> &ordered_translated_codes);



/// @brief Converts a set of numbered sequences into a multiple sequence alignment or
///        MSA by gapping all sequences so they are all the same length.
/// @param sequences A vector of sequences.
/// @param annotations A vector of tuples, each of which has four elements: a vector of
///                    position codes, a percent identity, a chain type and an error message.
///                    This tuple is generated by classes in NumberingTools when calling
///                    analyze_seq. Must be of the same size() as sequences.
/// @param position_codes A vector to store the output position codes. The position codes for
///                       each element in the MSA are stored here. Should be empty initially.
/// @param aligned_seqs A vector to store the output gapped sequences. Gapped sequences for each
///                     input sequence are stored here. Should be empty initially.
/// @param scheme One of "imgt", "aho", "kabat" or "martin".
/// @param add_unobserved_positions If True, add positions expected for a given numbering
///        scheme even if they are not observed in any of the aligned sequences.
/// @return Returns VALID_SEQUENCE if no error, INVALID_SEQUENCE if an error.
int build_msa_utility(std::vector<std::string> &sequences,
    std::vector<std::tuple<std::vector<std::string>, double, std::string, std::string>> &annotations,
    std::vector<std::string> &position_codes,
    std::vector<std::string> &aligned_seqs,
    const std::string &scheme,
    bool add_unobserved_positions);



/// @brief Trims a sequence using the corresponding alignment so
///        that c- or n-terminal gaps are removed.
/// @param sequence An input sequence.
/// @param alignment A tuple which has four elements: a vector of
///                  position codes, a percent identity, a chain type
///                  and an error message. This tuple is generated by classes
///                  in NumberingTools when calling
///                  analyze_seq. Must be of the same size() as sequences.
/// @param trimmed_alignment A vector in which the trimmed alignment output
///                          will be stored. Should be empty initially.
/// @param exstart An int in which the first non-trimmed position will
///                be stored.
/// @param exend An int in which the last non-trimmed position will be stored.
/// @param trimmed_seq A string in which the trimmed sequence is stored.
/// @return Returns VALID_SEQUENCE if no error, INVALID_SEQUENCE if an error.
int trim_alignment_utility(const std::string &sequence,
    std::tuple<std::vector<std::string>, double, std::string, std::string> &alignment,
    std::vector<std::string> &trimmed_alignment,
    int &exstart, int &exend,
    std::vector<char> &trimmed_seq);

}  // namespace SequenceUtilities

#endif
