#include "butterflies_stats.h"
#include "logging.h"
#include "cats_global.h"
#include "data/cats_grid.h"
#include "modules/module_header.h"
#include "butterflies_main.h"
#include "inline_overlays.h"
#include "inline_population.h"
#include "inline.h"
#include "butterflies_paths.h"
#include "temporal/phase_names.h"
#include "temporal/years.h"


const char *bf_get_stats_field_name(enum butterfly_stats which)
{
        switch (which) {
                case BF_STAT_POPULATED_FIT:
                        return "populated_fit";
                case BF_STAT_POPULATED_UNFIT:
                        return "populated_unfit";
                case BF_STAT_UNPOPULATED_FIT:
                        return "unpopulated_fit";
                case BF_STAT_UNPOPULATED_UNFIT:
                        return "unpopulated_unfit";
                case BF_STAT_EXCLUDED:
                        return "excluded";
                case BF_RANDOM_WALK_DEPOSIT_COUNT:
                        return "random_walk_deposit_count";
                case BF_RANDOM_WALK_COUNT:
                        return "random_walk_count";
                case BF_RANDOM_WALK_STEP_COUNT:
                        return "random_walk_step_count";
                case BF_OUTPUT_STAT_MAX:
                        return "<guard value>";
                case BF_STAT_MAX:
                        break;
                case BF_CELLS_WITH_EGGS:
                        return "cells with eggs";
                case BF_CELLS_WITH_EGGS_REMOVED:
                        return "removed cells with eggs";

        }

        log_message(LOG_ERROR, "unknown butterfly stats name with id %d", which);
        exit_cats(EXIT_FAILURE);
}


struct string_array *bf_assemble_stats(struct cats_configuration *conf, struct cats_grid *grid, bool header)
{
        struct conf_data_butterflies *module_conf = CATS_MODULE_DATA;
        assert(module_conf->stats_file != NULL);
        const int32_t grid_id = grid->id;
        int module_id = CATS_MODULE_ID;
        struct grid_data_butterflies *data = grid->grid_modules[module_id].module_data;


        struct string_array *x = new_string_array();

        if (header) {

                string_array_add(x, "phase");
                string_array_add(x, "year");
                string_array_add(x, "run");
                string_array_add(x, "replicate");
                string_array_add(x, "species");
                string_array_add(x, "id");
                string_array_add(x, "generation");

                for (enum butterfly_stats which = BF_STAT_MIN; which < BF_OUTPUT_STAT_MAX; which++) {
                        const char *name = bf_get_stats_field_name(which);
                        string_array_add(x, name);
                }
        } else {
                string_array_add(x, get_phase_shortname(conf->time.phase));
                string_array_add_int64(x, get_phase_year_abs(conf), NULL);
                string_array_add(x, conf->run_name);
                string_array_add_int64(x, conf->simulation.replicate, NULL);
                string_array_add(x, conf->param[grid_id].species_name);
                string_array_add_int64(x, grid_id, NULL);
                string_array_add_int(x, data->generation_current, NULL);

                for (enum butterfly_stats which = BF_STAT_MIN; which < BF_OUTPUT_STAT_MAX; which++) {
                        int64_t stat_id = module_conf->stat_ids[which];
                        string_array_add_int64(x, grid->stats.custom_stats[stat_id], NULL);
                }

        }


        return x;

}


void bf_stats_write(struct cats_configuration *conf, struct cats_grid *grid)
{
        struct conf_data_butterflies *module_conf = CATS_MODULE_DATA;
        struct string_array *data = NULL;

        if (module_conf->stats_file == NULL) {
                char *fn = bf_stats_filename(conf, grid);
                module_conf->stats_file = fopen(fn, "w");
                ENSURE_FILE_OPENED(module_conf->stats_file, fn);
                free(fn);
                data = bf_assemble_stats(conf, grid, true);

                char *string = string_array_paste(data, ",");
                fprintf(module_conf->stats_file, "%s\n", string);
                free(string);

                free_string_array(&data);

        }

        data = bf_assemble_stats(conf, grid, false);
        char *string = string_array_paste(data, ",");
        fprintf(module_conf->stats_file, "%s\n", string);
        free(string);
        free_string_array(&data);
}


void bf_area_stats_gather(struct cats_grid *grid, struct cats_thread_info *ts)
{
        struct conf_data_butterflies *module_conf = CATS_MODULE_DATA;
        struct cats_configuration *conf = ts->conf;
        const int64_t id_excluded = module_conf->stat_ids[BF_STAT_EXCLUDED];

        const int64_t id_pop_fit = module_conf->stat_ids[BF_STAT_POPULATED_FIT];
        const int64_t id_pop_unfit = module_conf->stat_ids[BF_STAT_POPULATED_UNFIT];

        const int64_t id_unpop_fit = module_conf->stat_ids[BF_STAT_UNPOPULATED_FIT];
        const int64_t id_unpop_unfit = module_conf->stat_ids[BF_STAT_UNPOPULATED_UNFIT];
        const int32_t grid_id = grid->id;

        const cats_dt_rates ot = grid->param.OT;
        struct statistics *stats = &ts->stats[grid_id];

        const cats_dt_coord start_row = ts->area.start_row;
        const cats_dt_coord end_row = ts->area.end_row;
        const cats_dt_coord start_col = ts->area.start_col;
        const cats_dt_coord end_col = ts->area.end_col;

        for (cats_dt_coord row = start_row; row < end_row; row++) {
                for (cats_dt_coord col = start_col; col < end_col; col++) {
                        cats_dt_population pop = get_adult_population(grid, row, col);

                        if (cell_excluded_by_overlay(conf, row, col)) {
                                stats->custom_stats[id_excluded] += 1;
                                if (pop > 0) {
                                        log_message(LOG_ERROR, "%s: population > 0 (%d) in excluded cell %d %d",
                                                    __func__, pop, row, col);
                                        exit_cats(EXIT_FAILURE);
                                }
                                continue;
                        }

                        cats_dt_environment suit = get_suitability(grid, row, col);

                        if (pop > 0) {
                                if (suit >= ot) {
                                        stats->custom_stats[id_pop_fit] += 1;

                                } else {
                                        stats->custom_stats[id_pop_unfit] += 1;
                                }
                        } else {
                                if (suit >= ot) {
                                        stats->custom_stats[id_unpop_fit] += 1;

                                } else {
                                        stats->custom_stats[id_unpop_unfit] += 1;
                                }

                        }
                }
        }

}