// =============================================================== //
//                                                                 //
//   File      : AP_codon_table.hxx                                //
//   Purpose   : codon definitions for DNA -> AA translation       //
//                                                                 //
//   Coded by Ralf Westram (coder@reallysoft.de)                   //
//   Institute of Microbiology (Technical University Munich)       //
//   http://www.arb-home.de/                                       //
//                                                                 //
// =============================================================== //

#ifndef AP_CODON_TABLE_HXX
#define AP_CODON_TABLE_HXX

#ifndef ARB_ASSERT_H
#include <arb_assert.h>
#endif
#ifndef STATIC_ASSERT_H
#include <static_assert.h>
#endif
#ifndef _STDINT_H
#include <stdint.h>
#endif

#define pn_assert(cond) arb_assert(cond)

// @@@ fix all historical prefixes to 'PN_' or remove them
// @@@ fix filenames

// --------------------------------------------------------------------------------

struct AWT_Codon_Code_Definition {
    const char *name;
    const char *aa;             // amino-codes
    const char *startStop;      // may contain 'M', '*' and '-'
    int         embl_feature_transl_table; // number of transl_table-entry in EMBL/GENEBANK features list
};

#define AWT_CODON_TABLES 25     // number of different translation tables (aka genetic codes and codon tables)
#define AWT_MAX_CODONS   64     // maximum of possible codon ( = 4^3)

const int AWAR_PROTEIN_TYPE_bacterial_code_index = 8; // contains the index of the bacterial code table

// --------------------------------------------------------------------------------
// initialize this module

void AP_initialize_codon_tables(); // call early

// --------------------------------------------------------------------------------

#define ALL_TABLES_MASK ((1<<AWT_CODON_TABLES)-1)

enum TranslationTableIndexType {
    TTIT_ARB, // arb internal table numbering [0 .. AWT_CODON_TABLES-1]. consecutive.
    TTIT_EMBL // embl numbering (AWT_Codon_Code_Definition::embl_feature_transl_table)
};

int TTIT_embl2arb(int embl_code_nr);
int TTIT_arb2embl(int arb_code_nr);

// --------------------------------------------------------------------------------

class TransTables {
    uint32_t allowed; // one bit for each arb-translation-table

    static uint32_t bitmask(int nr) { pn_assert(nr >= 0 && nr<AWT_CODON_TABLES); return 1<<nr; }

public:
    TransTables() { allowAll(); }

    bool is_allowed(int nr) const { return (allowed & bitmask(nr)) != 0; }
    bool any()  const { return  allowed; }
    bool none() const { return !allowed; }
    bool all() const { return allowed == ALL_TABLES_MASK; }

    void allow(int nr) { allowed |= bitmask(nr); }
    void forbid(int nr) { allowed &= ~bitmask(nr); }
    void forbid(const TransTables& other) { allowed &= ~other.allowed; }

    void allowAll() {
        STATIC_ASSERT((sizeof(allowed)*8) > AWT_CODON_TABLES); // one bit wasted
        allowed = ALL_TABLES_MASK;
    }
    void forbidAll() { allowed = 0; }
    void forbidAllBut(int nr) { allowed &= bitmask(nr); }

    int explicit_table() const {
        // return explicit table number (or -1 if not exactly one table is allowed)
        if (any()) {
            uint32_t tabs = allowed;
            for (int t = 0; t<AWT_CODON_TABLES; ++t) {
                if (tabs&1) return (tabs == 1) ? t : -1;
                tabs >>= 1;
            }
            pn_assert(0);
        }
        return -1;
    }

    bool is_subset_of(const TransTables& other) const { return (allowed&other.allowed) == allowed; }

    const char *to_string(TranslationTableIndexType type) const {
        const int    MAX_LEN = AWT_CODON_TABLES*3-1+1;
        static char  buffer[MAX_LEN];
        char        *out     = buffer;

        for (int a = 0; a<AWT_CODON_TABLES; ++a) {
            if (is_allowed(a)) {
                int shownNum =
                    type == TTIT_ARB
                    ? a
                    : TTIT_arb2embl(a);

                out += sprintf(out, ",%i", shownNum);
            }
        }
        pn_assert((out-buffer)<MAX_LEN);
        out[0] = 0;
        return buffer+1;
    }

};

// --------------------------------------------------------------------------------

bool        AWT_is_codon(char protein, const char *const dna, const TransTables& allowed, TransTables& allowed_left, const char **fail_reason_ptr = NULp);
const char *AP_get_codons(char protein, int code_nr);

const char *getAminoAcidAbbr(char aa);
const char* AWT_get_codon_code_name(int code);

#ifdef DEBUG
void AWT_dump_codons(TranslationTableIndexType type, bool skipX);
void test_AWT_get_codons();
#endif

// --------------------------------------------------------------------------------

#else
#error AP_codon_table.hxx included twice
#endif // AP_CODON_TABLE_HXX
