#ifndef INCLUDED_LOOP_
#define INCLUDED_LOOP_

#include <iosfwd>
#include <unordered_set>

#include "../typedefs/typedefs.h"
#include "../globals/globals.h"
#include "../scenario/scenario.h"
#include "../screening/screening.h"
#include "../densities/densities.h"
#include "../modalities/modalities.h"
#include "../tumorinfo/tumorinfo.h"
#include "../tumor/tumor.h"
#include "../costs/costs.h"

class Options;
class Scenario;

namespace FBB
{
    class CSVTable;
}

class Loop: public Globals
{
    enum Status
    {
        PRESENT,            // 0
        LEFT_PRE,           // 1 left the pre, during, post screening
        LEFT_DURING,        // 2
        LEFT_POST,          // 3
        TUMOR_PRE,          // 4
        TUMOR_DURING,       // 5
        TUMOR_POST,         // 6
    };

    enum                    // round info (per case) '0': not attended
    {
        ATTENDED        = '1',
        FALSE_NEGATIVE  = '2',
        FALSE_POSITIVE  = '3'
    };

    enum
    {
        N_RESULTS = 13              // weird... used in results()
    };

    Options const &d_options;
    Status d_status = PRESENT;

    StringVect const &d_labels;

    Scenario d_scenario;
    Costs d_costs;
    Densities d_densities;
    TumorInfo d_tumorInfo;
    Tumor d_tumor;
    Modalities d_modalities;
    Screening d_screening;

    bool d_selfDetected;
    uint16_t d_roundDetected;

            // Analysis &d_analysis;

            // Beir7 const &d_beir7;
            // Incidence const &d_incidence;

    double d_sumDeathAge = 0;
    double d_naturalDeathAge = 0;      // dying age w/o tumor
    double d_deathAge = 0;             // actual dying age

    uint16_t d_nRounds;                 // from Screening::nRounds()
    uint16_t d_round = 0;               // currently used screening round

    std::string d_roundInfo;

    SizeVect d_nIntervals;
    SizeVect d_nRoundFP;                // # of false-positives per round
    SizeVect d_nRoundFN;                // # of false-negatives per round

    SizeVect d_nDetections;

    double d_caseCost;
    double d_totalCost;

    SizeVect d_roundCost;               // sum of costs over all scr. rounds
                                        // (org: screeningRoundCosts)

                                    // randomly determined (breast cancer)
                                    // bi-rad indices for the ages of the
                                    // screening rounds, or table S3 indices
                                    // for lung cancer.
    Uint16Vect d_indices;           // ('densities' in the original sources)

    std::string d_timestamp;

    void (*d_tnm)(std::ostream &out);   // noTNM or fillTNM

    ProbGroup::Vector const *d_groupVector; // breast- or lung cancer
                                            // probGroup::Vector to use when
                                            // constructing d_indices

    static std::unordered_set<std::string> s_availableFiles;

    public:
        Loop(StringVect const &labels);

        void iterate();

    private:
                                        // add to totalCost, screeningCose
        void addCost(double cost);      //+ and womenCost
        bool betaFunction(uint16_t modalityNr);
        void caseInit();
        size_t cases() const;                   // #cases in womenLoop
        void characteristics(Status natural, Status tumor);
        Uint16Vect cptIndices() const;          // cpt d_indices
        static void fillTNM(std::ostream &out);
        static void fillZeroes(std::ostream &out, size_t idx);
        void genCases(size_t iter, size_t nCases);
        FBB::CSVTable headerData(size_t iter) const;
        FBB::CSVTable headerRounds(size_t iter) const;
        FBB::CSVTable headerSensitivity() const;

//        void removeCase(double screeningAge, double selfDetectAge);
        void intervalCancer(double screeningAge);

        void labels(std::ostream &out) const;
        bool leaving(double screeningAge);
        void maybeDetect(ModBase *modBase, double screeningAge);
        void maybeFalsePositive(ModBase *modBase, double screeningAge);
        double naturalDeathAge();
        static void noTNM(std::ostream &out);
        static std::ofstream open(std::string const &fname);
        static std::ofstream outStream(std::string const &fname, size_t idx);
        void postScreen();
        void preTumorDeath();   // early natural death
        void preScreen();
        static std::string replaceDollar(std::string const &settingsFile,
                                         size_t idx);
        void resetCounters();
        void selfDetected();    // self detected the tumor              // .ih
                                            // returns the sensitivity for the
        double sensitivity(ModBase *modBase) const;    // current round  1.cc
        void setStatus(Status status, double age);
        void screen(double screeningAge);
        void screening();
        double treatmentCosts(double screeningAge) const;               // .ih
        void treatmentDeath();                                          // .ih

                // NOTE: in the orig. source density NRs are used: 1..4
                //       here density INDICES are usd: 0..3
                // but when writing the data file (e.g., data-0.txt, original
                // name e.g., women-test-i0.txt) a + 1 correction is
                // currently applied
        bool use(ModBase *modBase);         // use this modality
        void writeData(FBB::CSVTable &dataTable, size_t idx) const;
        void writeParameters(size_t iter) const;    // also opens the file
        void writeRounds(FBB::CSVTable &roundTable) const;
        void writeSensitivity(FBB::CSVTable &sensTable, size_t iter) const;
};

#endif
