// ============================================================= //
//                                                               //
//   File      : MP_sondentopf.cxx                               //
//   Purpose   :                                                 //
//                                                               //
//   Institute of Microbiology (Technical University Munich)     //
//   http://www.arb-home.de/                                     //
//                                                               //
// ============================================================= //

#include "MultiProbe.hxx"
#include "MP_probe.hxx"

#include <AP_TreeColors.hxx>
#include <aw_msg.hxx>
#include <arbdbt.h>

#include <cmath>

ST_Container::ST_Container(int anz_sonden) {
    long laenge_markierte;

    Sondennamen = new List<char>;

    Auswahlliste = new MO_Liste;
    Bakterienliste = new MO_Liste;

    GB_ERROR error = Bakterienliste->get_all_species(mp_gl_awars.ptserver);
    if (error) {
        aw_message(error);
    }

    if (pt_server_different) return;
    laenge_markierte = Auswahlliste->fill_marked_bakts();

    anz_elem_unmarked = Bakterienliste->debug_get_current()-1 - laenge_markierte;
    // STATISTIK

    anzahl_basissonden =  anz_sonden;

    cachehash = GBS_create_hash(anzahl_basissonden + 1, GB_IGNORE_CASE);
    // hashlaenge darf nicht 0 sein, groesser schadet nicht

    sondentopf = new Sondentopf(Bakterienliste, Auswahlliste);
    // Momentan wird auf diesem sondentopf gearbeitet
}


ST_Container::~ST_Container() {
    char* Sname;
    Sonde* csonde;

    delete Bakterienliste;
    delete Auswahlliste;
    delete sondentopf;

    Sname = Sondennamen->get_first();
    while (Sname) {
        csonde = get_cached_sonde(Sname);
        delete csonde;
        free(Sname);
        Sondennamen->remove_first();
        Sname = Sondennamen->get_first();
    }

    delete Sondennamen;
    GBS_free_hash(cachehash);
}



Sonde* ST_Container::cache_Sonde(const char *name, int allowed_mis, double outside_mis) {
    char*  name_for_plist = ARB_strdup(name);
    int    num_probes     = mp_gl_awars.no_of_probes;
    Sonde* s              = new Sonde(name, num_probes, allowed_mis, outside_mis);

    Sondennamen->insert_as_first(name_for_plist);
    s->gen_Hitliste(Bakterienliste);

    GBS_write_hash(cachehash, name, (long) s);
    // Reine Sonde plus Hitliste geschrieben, der Zeiger auf die Sonde liegt als long gecastet im Hash
    return s;
}

Sonde* ST_Container::get_cached_sonde(const char* name) {
    return name ? (Sonde*)GBS_read_hash(cachehash, name) : NULp;
}

// ############################################################################################
/*
  Zu jeder Kombination von Mehrfachsonden gehoert ein Sondentopf. Dieser enthaelt eine Liste mit
  Sonden und eine Liste mit Kombinationen aus diesen Sonden. Die Kombinationen entstehen aus den
  Sonden und/oder aus Kombinationen durch Verknuepfung mit der Methode Probe_AND.
*/
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Methoden SONDENTOPF ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Sondentopf::Sondentopf(MO_Liste *BL, MO_Liste *AL) {
    // @@@ use assertions here?
    if (!BL) GBK_terminate("List of species is empty");
    if (!AL) GBK_terminate("List of marked species is empty");

    Listenliste = new List<void*>;
    color_hash = GBS_create_hash(BL->get_laenge()*1.25+1, GB_IGNORE_CASE);

    BaktList    = BL;
    Auswahllist = AL;

    Listenliste->insert_as_last((void**) new List<Sonde>);
}



Sondentopf::~Sondentopf() {
    // darf nur delete auf die listenliste machen, nicht auf die MO_Lists, da die zu dem ST_Container gehoeren
    Sonde *stmp = NULp;

    List<Sonde> *ltmp = LIST(Listenliste->get_first());
    if (ltmp) {
        stmp = ltmp->get_first();
    }
    while (ltmp) {
        while (stmp) {
            ltmp->remove_first();
            stmp = ltmp->get_first();
        }
        Listenliste->remove_first();
        delete ltmp;
        ltmp = LIST(Listenliste->get_first());
    }

    delete Listenliste;
    GBS_free_hash(color_hash);
}



void Sondentopf::put_Sonde(const char *name, int allowed_mis, double outside_mis) {
    if (!name) GBK_terminate("No name specified for species");

    positiontype  pos;
    ST_Container *stc         = mp_main->get_stc();
    List<Sonde>  *Sondenliste = LIST(Listenliste->get_first());
    Sonde        *s;
    int           i           = 0;

    if (!Sondenliste) {
        Sondenliste = new List<Sonde>;
        Listenliste->insert_as_last((void**) Sondenliste);
    }

    s = stc->get_cached_sonde(name);
    if (!s) {
        s = stc->cache_Sonde(name, allowed_mis, outside_mis);
    }
    pos = Sondenliste->insert_as_last(s);
    if (! s->get_bitkennung())
        s->set_bitkennung(new Bitvector(((int) pos)));
    s->set_far(0);
    s->set_mor(pos);
    s->get_bitkennung()->setbit(pos-1);
    // im cache steht die Mismatch info noch an Stelle 0. Hier muss sie an Stelle pos  verschoben werden
    if (pos!=0)
        for (i=0; i<s->get_length_hitliste(); i++)
            if (s->get_hitdata_by_number(i))
                s->get_hitdata_by_number(i)->set_mismatch_at_pos(pos, s->get_hitdata_by_number(i)->get_mismatch_at_pos(0));
}


double** Sondentopf::gen_Mergefeld() {
    // Zaehler
    int         i, j;

    Sonde*      sonde;

    List<Sonde>*    Sondenliste = LIST(Listenliste->get_first());
    long        alle_bakterien = BaktList->debug_get_current()-1;
    long        H_laenge, sondennummer;
    double**        Mergefeld = new double*[alle_bakterien+1];

    for (i=0; i<alle_bakterien+1; i++) {
        Mergefeld[i] = new double[mp_gl_awars.no_of_probes];
        for (j=0; j<mp_gl_awars.no_of_probes; j++) { // LOOP_VECTORIZED
            Mergefeld[i][j] = 100;
        }
    }

    sondennummer=0;
    sonde = Sondenliste->get_first();
    while (sonde) {
        H_laenge = sonde->get_length_hitliste();
        for (i=0; i<H_laenge; i++) {
            Mergefeld[sonde->get_hitdata_by_number(i)->get_baktid()][sondennummer] =
                sonde->get_hitdata_by_number(i)->get_mismatch_at_pos(0);
        }

        sondennummer++;
        sonde = Sondenliste->get_next();
    }

    return Mergefeld;
}

probe_tabs* Sondentopf::fill_Stat_Arrays() {
    // erstmal generische Felder
    List<Sonde>*    Sondenliste = LIST(Listenliste->get_first());
    long        feldlen = (long) pow(3.0, (int)(mp_gl_awars.no_of_probes));
    int*        markierte = new int[feldlen];                   // MEL
    int*        unmarkierte = new int[feldlen];                 // MEL
    int     i=0, j=0;
    long        alle_bakterien = BaktList->debug_get_current()-1;
    double**    Mergefeld;
    int*        AllowedMismatchFeld = new int[mp_gl_awars.no_of_probes];
    Sonde*      sonde;

    sonde = Sondenliste->get_first();
    for (i=0; i<mp_gl_awars.no_of_probes; i++) {
        AllowedMismatchFeld[i] = (int) sonde->get_Allowed_Mismatch_no(0);
        sonde = Sondenliste->get_next();
    }


    for (i=0; i<feldlen; i++) { // LOOP_VECTORIZED[!>=5]
        markierte[i] = 0;
        unmarkierte[i] = 0;
    }

    int faktor=0;
    Mergefeld = gen_Mergefeld();


    for (i=0; i < alle_bakterien+1; i++) {
        if (BaktList->get_entry_by_index(i)) {
            long wertigkeit = 0;
            for (j=0; j<mp_gl_awars.no_of_probes; j++) {
                if (Mergefeld[i][j] <= ((double) AllowedMismatchFeld[j] + (double) mp_gl_awars.greyzone)) {
                    faktor = 0;
                }
                else if (Mergefeld[i][j] <= ((double) AllowedMismatchFeld[j] +
                                             (double) mp_gl_awars.greyzone +
                                             mp_gl_awars.outside_mismatches_difference))
                {
                    faktor = 1;
                }
                else {
                    faktor = 2;
                }

                wertigkeit += faktor * (long) pow(3, j);
            }

            if (Auswahllist->get_index_by_entry(BaktList->get_entry_by_index(i))) {
                markierte[wertigkeit]++;
            }
            else {
                unmarkierte[wertigkeit]++;
            }
        }
    }

    for (i=0; i<alle_bakterien+1; i++) {
        delete [] Mergefeld[i];
    }
    delete [] Mergefeld;
    delete [] AllowedMismatchFeld;
    probe_tabs *pt = new probe_tabs(markierte, unmarkierte, feldlen);           // MEL (sollte bei Andrej passieren
    return pt;
}


void Sondentopf::gen_color_hash(positiontype anz_sonden) {
    if (!anz_sonden) return;

    List<Sonde>*    Sondenliste = LIST(Listenliste->get_first());
    double**        Mergefeld;
    long        alle_bakterien = BaktList->debug_get_current() -1;
    int*        AllowedMismatchFeld = new int[mp_gl_awars.no_of_probes];            // MEL
    int*        rgb = new int[3];                               // MEL
    Sonde*      sonde;
    int         i=0, j=0;

    sonde = Sondenliste->get_first();
    for (i=0; i<mp_gl_awars.no_of_probes; i++) {
        AllowedMismatchFeld[i] = (int) sonde->get_Allowed_Mismatch_no(0);
        sonde = Sondenliste->get_next();
    }


    Mergefeld = gen_Mergefeld();


    for (i=1; i < alle_bakterien+1; i++) {
        rgb[0]=0; rgb[1]=0; rgb[2]=0;

        for (j=0; j<mp_gl_awars.no_of_probes; j++) {
            if (Mergefeld[i][j] <= ((double) AllowedMismatchFeld[j] + (double) mp_gl_awars.greyzone + mp_gl_awars.outside_mismatches_difference)) {
                rgb[j%3]++;
            }
        }

        int color = 0;

        if      (!rgb[0] && !rgb[1] && !rgb[2]) { color = AWT_GC_BLACK; }
        else if ( rgb[0] && !rgb[1] && !rgb[2]) { color = AWT_GC_RED; }
        else if (!rgb[0] &&  rgb[1] && !rgb[2]) { color = AWT_GC_GREEN; }
        else if (!rgb[0] && !rgb[1] &&  rgb[2]) { color = AWT_GC_BLUE; }
        else if ( rgb[0] &&  rgb[1] && !rgb[2]) { color = AWT_GC_YELLOW; }
        else if ( rgb[0] && !rgb[1] &&  rgb[2]) { color = AWT_GC_MAGENTA; }
        else if (!rgb[0] &&  rgb[1] &&  rgb[2]) { color = AWT_GC_CYAN; }
        else if ( rgb[0] &&  rgb[1] &&  rgb[2]) { color = AWT_GC_WHITE; }
        else {
            GBK_terminate("logic error: should never be reached");
        }

        GBS_write_hash(color_hash, BaktList->get_entry_by_index(i), (long)color);
    }

    for (i=0; i<alle_bakterien+1; i++)
        delete [] Mergefeld[i];
    delete [] Mergefeld;


    delete [] AllowedMismatchFeld;
    delete [] rgb;
}

