/******************************************************************************
 * @internal
 * @file        rCFD_types.h
 * @copyright   (C) 2021
 *              Particulate Flow Modelling,
 *              Johannes Kepler University, Linz, Austria
 * @website     www.particulate-flow.at
 * @authors     Stefan Pirker
 * @authors     Daniel Queteschiner
 *****************************************************************************/

#ifndef RCFD_TYPES
#define RCFD_TYPES

#include "rCFD.h"
#include <time.h>

    /* A. Global Dicts */

    typedef struct Solver_Dict_struct
    {
        short   report_priority;
        short   profiling_on;
        short   multifile_output_on;            /**< flag indicating on/off status of multifile output */

        /* main characteristics */

        short   mode;

        short   number_of_frames;
        short   number_of_states;
        short   number_of_phases;
        short   number_of_islands;
        short   number_of_layers;
        short   number_of_runs;

        /* sub-model flags */

        short   recurrence_process_on;
        short   data_convection_on;
        short   face_swap_diffusion_on;
        short   local_diff_on;
        short   parallel_diffusion_on;
        short   data_binarization_on;
        short   data_drifting_on;
        short   balance_correction_on;
        short   on_the_fly_post_on;
        short   interpolation_on;
        short   shifts_from_displacement_fields_on;

        /* sub-model params */

        short   max_number_of_cells_per_time_step;

        int     time_steps_per_monitoring_interval;
        double  start_time_for_monitoring;

        short   single_phase_vof;
        short   single_phase_vof_id;
        double  vof_cutoff_limit;

        int     min_number_of_cells_per_layer;
        short   max_number_of_faces_per_cell;
        short   max_number_of_children;
        short   number_redist_loops_per_layer;

        short   C2C_loading_reduction;

        double  global_time_step;

        short   max_fill_loops;

        double  face_swap_max_per_loop;
        double  face_swap_min;
        short   face_swap_max_loops;
        short   local_diff_format;

        short   number_of_drift_loops;

        short   balance_correction_update;
        short   control_conc_sum_on;

    } Solver_Dict_type;

    typedef struct File_Dict_struct
    {
        char *tracer_start_position_filename;

        char *Prep_Transscript_filename;

        char *Run_Transscript_filename;

        char *C2C_filename;
        
        char *frame_id_filename;

        char *Norm_filename;

        char *vof_filename;

        char *local_diff_filename;

        char *rec_cal_filename;

        char *rec_user_filename;

        char *Jump_filename;

        char *Matrix_filename;

        char *Balance_filename;

        char *Rec_Frames_filename;

        char *Topo_filename;

        char *Dump_filename;

        char *Probe_filename;

        char *Dict_filename;

    } File_Dict_type;

    typedef struct Phase_Dict_struct
    {
        int         number_of_data;

        int         number_of_concentration_data;

        double      time_step;

        double      shift_probability;

        double      density;

        double      heat_capacity;

        double      reference_temperature;

        double      vof_max;

        short       hindered_drift_on;

        short       local_drift_on;

        short       drift_control_conc_sum_on;

        double      d32_diam;

        int         number_of_user_vars;

        double      *user;

    } Phase_Dict_type;

    typedef struct Tracer_Dict_struct
    {
        short   format;

        int     number_of_Tracers_per_cell;

        short   region_of_interest_exists;

        double  ROI_x_min;
        double  ROI_x_max;
        double  ROI_y_min;
        double  ROI_y_max;
        double  ROI_z_min;
        double  ROI_z_max;

        int     coarse_graining;        /* 1: take every Tracer, 2: take every second Tracer, .... */

        short   *random_walk;           /* [i_phase] */

        double  *random_walk_velocity_ratio;

        short   C2C_format;

        short   reverse_direction_on;

        short   splitting_on;

        short   avoid_information_lock_cells_on;

        short   conservative_cross_partition_shifts_on;

    } Tracer_Dict_type;

    /**
     * Cell-to-cell shift interpolation formats
     */
    typedef enum {
        RCFD_INTERP_NEAREST_ALIGNED_NEIGHBOR_CELLS,
        RCFD_INTERP_MAX
    } rcfd_interpolation_format;

    typedef struct Calibration_Dict_struct
    {
        short   format;

        short   calibration_parallel_on;

        short   max_number_of_aligned_neigh_cells;

        short   calibration_pruning_on;
        short   calibration_min_shifts_after_pruning;
        double  calibration_pruning_weight;

        short   multiply_shift_weights_with_vof_on;

        double  small;  /**< distances below this value are considered equal 0 */
        double  great;  /**< value greater than any nearest neighbor cell distance */

    } Calibration_Dict_type;

    typedef struct Norm_Dict_struct
    {
        short   format;

        int     coarse_graining;        /* if ((i_cell % coarse_graining) == 0) */

        short   larger_than_mean_norm_only;

    } Norm_Dict_type;

    typedef struct Rec_Dict_struct
    {
        short   format;

        short   monitor_rec_frames_on;
        short   adapt_vof_stitching_on;

        short   min_seq_length;
        short   max_seq_length;

        short   off_diagonal_band_width;

        int     frame_id_vector_length;         // number of identifiers in frame_id vector (only for RCFD_REC_FRAME_ID)
        int     frame_id_sequence_length;       // number of frames per sequence            (only for RCFD_REC_FRAME_ID)
        int     frame_id_target_sequence_count;             // targeted number of sequences in frame_id_database (used by rcfd_reduce_database)
        short   frame_id_avoid_immediate_seq_repetition;    // option used in get_global_frame_by_frame_id (rCFD_run.h)
		short   frame_id_normalize_on;						// option to normalize frame_id_database upon loading and during run mode

        short   number_of_adapt_vof_loops;

        int     seed;

    } Rec_Dict_type;

    typedef struct Data_Dict_struct
    {
        short       type;

        double      face_swap_diffusion;

        double      binarization_art_diff;

        short       drifting_data;

        double*     drift_velocity;

        double      d1_diam, d2_diam, d3_diam;

        int         number_of_user_vars;

        double*     user;

    } Data_Dict_type;

    typedef struct Balance_Dict_struct
    {
        short       type;

        short       max_correction_loops;

        double      accuracy_level;

        short       write_balance_to_file;

        int         write_balance_to_file_interval;

    } Balance_Dict_type;

    typedef struct Cell_Dict_struct
    {
        int     number_of_cells;

        int     number_of_int_cells;

        int     number_of_ext_cells;

        int     number_of_user_vars;

        int     number_of_rec_user_vars;

        double  next_neigh_cell_skin_factor;

    } Cell_Dict_type;

    typedef struct Face_Dict_struct
    {
        int     number_of_int_faces;

        int     number_of_ext_faces;

        int     number_of_faces;

    } Face_Dict_type;

    typedef struct Node_Dict_struct
    {
        int     number_of_nodes;                /**< total number of unique nodes */

        int     number_of_int_node_references;  /**< number of nodes referenced by internal cells (includes multiple counting) */

        int     number_of_ext_node_references;  /**< number of nodes referenced by external cells (includes multiple counting) */

    } Node_Dict_type;

    typedef struct Topo_Dict_struct
    {
        Cell_Dict_type  *Cell_Dict;

        Face_Dict_type  *Face_Dict;

        Node_Dict_type  *Node_Dict;

    } Topo_Dict_type;

    /**
     * Probe types
     */
    typedef enum {
        RCFD_PROBE_CELL = 0,
        RCFD_PROBE_VOF,
        RCFD_PROBE_USER,
        RCFD_PROBE_REC_USER,
        RCFD_PROBE_MAX
    } rcfd_probe_type;

    typedef struct Dump_Dict_struct
    {
        int     dump_interval;          /**< write interval for dump files */

        int     overwrite_interval;     /**< overwrite interval for dump files */

        int     dump_file_type;         /**< flag indicating dump file type */

        short   multifile_output_on;    /**< flag indicating on/off status of multifile dump output */

        short   dump_vof;

        short   **dump_cell_data; // [i_phase][i_data]

        short   *dump_user_data;  // [i_user]

        short   *dump_rec_user_data;

        int     probe_interval;         /**< write interval for probe file */

        int     probe_count;            /**< global number of data probes */

        int     *probe_layer;           /**< array of probed cell layers */ // [i_probe]

        int     *probe_cell;            /**< array of probed cells */ // [i_probe]

        int     *probe_type;            /**< array of probe types (one of PROBE_TYPE) */ // [i_probe]

        int     *probe_phase;           /**< array of phase indices */ // [i_probe]

        int     *probe_data;            /**< array of data indices */ // [i_probe]

        double  *probe_value;           /**< value at probe coordinates */ // [i_probe]

        double  **probe_x;              /**< probe coordinates */ // [i_probe][i_dim]

    } Dump_Dict_type;

    /* B. Global Vars */

    typedef struct Solver_struct
    {
        int         current_state, upcoming_state;

        int         current_layer;

        int         analyse_CFD_counter;

        int         displ_reset_counter;

        int         global_run_counter;

        short       balance_file_opened;

        short       rec_frames_monitor_file_opened;

        int         total_number_of_C2Cs_read;

        double      global_time;

        double      *timestep_width;

    } Solver_type;

    typedef struct Cell_struct
    {
        double      **x;
        double      *volume;
        double      *grid_spacing;

        double      x_min_node[3];
        double      x_max_node[3];
        double      x_min_global[3];
        double      x_max_global[3];

        int         *number_of_neigh_cells;
        int         *number_of_next_neigh_cells;
        int         **neigh_cell;       /* neighbor cells */
        int         **next_neigh_cell;  /* neighbor and next neighbor cells */

        double      **average_velocity; /* [i_phase][i_cell] */
        double      **crossing_time;

        short       *hit_by_other_cell;
        short       *island_id;

        double      *weight_after_shift;
        double      *weight_after_swap;

        double      ***vof;             /* [i_frame][i_cell][i_phase] */

        double      **vof_changed;      /* [i_phase][i_cell] */

        double      ***data;            /* [i_phase][i_cell][i_data] */
        double      ***data_shift;
        double      ***data_swap;

        double      ***local_diff;      /* [i_frame][i_phase][i_cell] */

        double      *drift_exchange;    /* [i_cell] */

        double      ***cfd_displ;       /**< displacement vector field */ /* [i_phase][i_cell][i_dim] */
        double      ***cfd_diff;        /* [i_phase][i_cell][i_dim] ; i_dim == {xx, yy, zz, xy, yz, zx} */

        double      *tmp;               /* [i_cell] */

        double      **user;             /* [i_cell][i_user] */
        double      ***rec_user;        /* [i_frame][i_cell][i_rec_user] */

        short       *marked;            /* [i_cell] */
        int         *parent_cell;
        short       *number_of_children;
        int         **child_index;

        int         *type;              /**< VTK cell type */
        int         *number_of_nodes;   /**< number of cell nodes */
        int         **nodes;            /**< indices into cell node array [i_cell][i_node] */
        // polyhedra
        int         number_of_polyhedra;        /**< number of all polyhedral cells */
        int         *number_of_poly_faces;      /**< number of cell faces for polyhedral cells [i_polyhedron] */
        int         **number_of_poly_face_nodes;/**< number of face nodes for polyhedral cells [i_polyhedron][i_face] */
        int         number_of_poly_nodes;       /**< total number of all poly nodes (length of poly_nodes array) */
        int         *poly_nodes;                /**< indices into face node array, consecutive for all polyhedral cells [i_node] */

    } Cell_type;

    typedef struct Face_struct
    {
        int     *c0, *c1;

        double  **area;

        double  **local_drift_adaption;     // used by Phase_Dict.local_drift_on

    } Face_type;

    typedef struct Node_struct
    {
        double      **x;

    } Node_type;

    typedef struct Topo_struct
    {
        Cell_type   *Cell;  // [i_layer]

        Face_type   *Face;

        Node_type   *Node;

    } Topo_type;

    /**
     * Cell-to-cell shift formats
     */
    typedef enum {
        RCFD_SHIFT_C0_N0_C1_N1_W0,
        RCFD_SHIFT_MAX
    } rcfd_shift_format;

    typedef struct C2C_shift_struct
    {
      int           c0,node0;
      int           c1,node1;
      double        w0;

    } C2C_shift_type;

    typedef struct C2C_struct
    {
        short               format;

        int                 *number_of_shifts;      /* [i_layer] */
        int                 *number_of_shifts_in;
        int                 *number_of_shifts_out;

        C2C_shift_type      **shifts;               /* [i_layer][i_shift] */
        C2C_shift_type      **shifts_in;
        C2C_shift_type      **shifts_out;

        int                 **island_offsets;       /* [i_layer][i_island] */
        int                 **island_offsets_in;

        /* MPI variables, only used by Node-0 */

        int                 **number_of_shifts_to_node_zero;        /* [i_layer][i_node] */
        int                 **number_of_shifts_from_node_zero;
        int                 ***in2out;                              /* [i_layer][i_node][i_node] */

    } C2C_type;

    typedef struct tmp_C2C_struct
    {
        /* same as C2C_type, but only for one layer */
        short               format;

        int                 number_of_shifts;
        int                 number_of_shifts_in;
        int                 number_of_shifts_out;

        C2C_shift_type      *shifts;
        C2C_shift_type      *shifts_in;
        C2C_shift_type      *shifts_out;

        int                 *island_offsets;
        int                 *island_offsets_in;

        /* MPI variables, only used by Node-0 */

        int                 *number_of_shifts_to_node_zero;
        int                 *number_of_shifts_from_node_zero;
        int                 **in2out;

    } tmp_C2C_type;

    typedef struct Balance_struct
    {
        double      mass_integral, mass_integral_target;

        double      mass_integral_global, mass_integral_target_global;

        double      mass_source, mass_source_global;

        double      **node2node_flux, **node2node_data_flux;

        double      mass_error, mass_error_global;

    } Balance_type;

    typedef struct Tracer_struct
    {
        short   allocated;
        short   initialized;
        short   ready2write;
        short   monitoring_started;

        int     *monitor_counter;       /* Tracers per Frame [i_phase] */

        int     frame_counter;

        int     *number_of_shifts;      /* [i_phase] */

        C2C_shift_type  **shifts;

    } Tracer_type;

    typedef struct Norm_struct
    {
        int     number_of_norms;

        int     frame_counter; // used for all fields!

        double  *norm;

    } Norm_type;

    typedef struct Rec_struct
    {
        int     global_frame;

        int     prev_global_frame;

#ifdef RCFD_LIB_ISLANDS
        int     *global_frame_of_island;     /* [i_island] */

        int     *prev_global_frame_of_island;
#endif

        int     frame_in_sequence;

        short   jumped_at_last_frame;

        int     sequence_length;

        int     ****jumps;                  // [state, state2, island, frame]

        double  *frame_id;                  // [i_id]           (only for RCFD_REC_FRAME_ID)
        double  *frame_id_means;			// [i_id]			(only for RCFD_REC_FRAME_ID, run mode, node-0)
        double  *frame_id_vars;				// [i_id]			(only for RCFD_REC_FRAME_ID, run mode, node-0)

        double  **frame_id_database;        // [i_frame][i_id]  (only for RCFD_REC_FRAME_ID, run mode, node-0)

    } Rec_type;

    typedef struct Parallel_struct{

        int     rank;
        int     size;

        MPI_Comm world;

        int     finalize;

        struct MPI_topo_struct *MPI_Topo;

        struct C2C_MPI_struct  *C2Cs_MPI;

    } Parallel_type;


    typedef struct rCFD_UDF_struct
    {
        void (*_user_set_solver_dict)   (void *handle);
        void (*_user_set_file_dict)     (void *handle);
        void (*_user_set_phase_dict)    (void *handle);
        void (*_user_set_tracer_dict)   (void *handle);
        void (*_user_set_calibration_dict)(void *handle);
        void (*_user_set_norm_dict)     (void *handle);
        void (*_user_set_rec_dict)      (void *handle);
        void (*_user_set_data_dict)     (void *handle);
        void (*_user_set_balance_dict)  (void *handle);
        void (*_user_set_dump_dict)     (void *handle);
        void (*_user_set_topo_dict)     (void *handle);
        void (*_user_set_cell_dict)     (void *handle, const short i_layer);
        void (*_user_set_face_dict)     (void *handle, const short i_layer);
        void (*_user_set_node_dict)     (void *handle, const short i_layer);

        void (*_user_pre_proc)          (void *handle); // preparation only

        void (*_user_init_cells)        (void *handle, const short i_layer); // no default function
        void (*_user_init_faces)        (void *handle, const short i_layer); // no default function
        void (*_user_init_cell_hosts)   (void *handle, const short i_layer); // no default function
        void (*_user_init_nodes)        (void *handle, const short i_layer);
        void (*_user_init_data)         (void *handle, const short i_layer); // run only

        short (*_user_set_layer)        (void *handle, const short current_layer); // run only

        void (*_user_access_data_before_phase_loop) (void *handle); // run only
        void (*_user_access_data_before_shift)      (void *handle, const short i_phase); // run only
        void (*_user_access_data_after_swap)        (void *handle, const short i_phase); // run only

        void (*_user_post)(void *handle); // run only

        void (*_user_set_norm)     (void *handle); // preparation only
        void (*_user_set_rec_vars) (void *handle, const short i_layer); // preparation only
        void (*_user_set_frame_id) (void *handle, double* id_vector);

    } rCFD_UDF_type;


    typedef clock_t ticks_t;

    typedef struct Timer_struct
    {
        ticks_t m_value;
        ticks_t m_total_value;
        short is_running;
    } Timer_type;

    /**
     * Time units usable in reporting module
     */
    typedef enum {
        RCFD_TIMER_NANOSEC = 0,
        RCFD_TIMER_MICROSEC,
        RCFD_TIMER_MILLISEC,
        RCFD_TIMER_SEC
    } rcfd_timer_time_unit;

    /**
     * Code units timeable in reporting module
     */
    typedef enum {
        RCFD_TIMER_GLOBAL = 0,
        RCFD_TIMER_INIT,
        RCFD_TIMER_READ_DB,
        RCFD_TIMER_READ_SHIFT,
        RCFD_TIMER_RUN,
        RCFD_TIMER_RUN_UPDATE,
        RCFD_TIMER_RUN_SHIFT,
        RCFD_TIMER_RUN_SWAP,
        RCFD_TIMER_RUN_BALANCE,
        RCFD_TIMER_USER,
        RCFD_TIMER_MAX
    } rcfd_timer_code_unit;

    /**
     * Code section categories for reporting module
     */
    typedef enum {
        RCFD_REPORT_CATEGORY_NOTSET = 0,
        RCFD_REPORT_CATEGORY_INITIALIZE,
        RCFD_REPORT_CATEGORY_CREATE_SHIFT,
        RCFD_REPORT_CATEGORY_WRITE_SHIFT,
        RCFD_REPORT_CATEGORY_WRITE_FIELDS,
        RCFD_REPORT_CATEGORY_WRITE_RECURRENCE,
        RCFD_REPORT_CATEGORY_WRITE_TOPOLOGY,
        RCFD_REPORT_CATEGORY_READ_TOPOLOGY,
        RCFD_REPORT_CATEGORY_READ_DB,
        RCFD_REPORT_CATEGORY_RUN_UPDATE,
        RCFD_REPORT_CATEGORY_RUN_SHIFT,
        RCFD_REPORT_CATEGORY_RUN_SWAP,
        RCFD_REPORT_CATEGORY_RUN_BALANCE,
        RCFD_REPORT_CATEGORY_POSTPROCESS,
        RCFD_REPORT_CATEGORY_USER,
        RCFD_REPORT_CATEGORY_UNKNOWN,
        RCFD_REPORT_CATEGORY_MAX
    } rcfd_report_category;


    typedef struct Report_struct
    {
        /* public members / determined by dict entries */
        short       console_output_on;      /**< flag indicating on/off status of console output */
        short       file_output_on;         /**< flag indicating on/off status of file output */
        short       timing_on;              /**< flag indicating on/off status of timing */
        short       fflush_on;              /**< flag indicating on/off status of stream flush */
        short       priority;               /**< report priority determining visibility of messages in output stream */
        short       time_unit;              /**< unit of measured time, one of @ref rcfd_timer_time_unit */
        /* private members / internal usage only */
        short       file_is_open;           /**< flag indicating if per-process output file is open */
        short       file0_is_open;          /**< flag indicating if node0 output file is open */
        int         **number_of_reports;    /**< statistics of the number of messages and their priority for each report category */
        double      ticks_2_time_unit;      /**< conversion factor from clock ticks to time unit */
        FILE        *report_file;           /**< pointer to per-process output FILE object */
#if defined(RCFD_LIB_MPI)
        MPI_File    report_file0;           /**< shared MPI_File output object */
#else
        FILE        *report_file0;          /**< pointer to node0 output FILE object */
#endif
        Timer_type  *timers[RCFD_TIMER_MAX];/**< array of timers for all timeable code units */
    } Report_type;


    typedef struct rCFD_struct
    {
        int     dict_write_permission_mask; /**< mask for setting write permission of individual dictionaries */
        int     initialized;                /**< flag indicating if all components are initialized */

        Report_type         *Report;

        File_Dict_type      *File_Dict;

        Solver_Dict_type    *Solver_Dict;

        Solver_type         *Solver;

        Rec_Dict_type       *Rec_Dict;

        Rec_type            *Rec;

        Topo_Dict_type      *Topo_Dict;

        rCFD_UDF_type       *rCFD_UDF;

        Parallel_type       *Parallel;

#if RP_NODE
        Phase_Dict_type     *Phase_Dict;

        Tracer_Dict_type    *Tracer_Dict;

        Calibration_Dict_type *Calibration_Dict;

        Norm_Dict_type      *Norm_Dict;

        Data_Dict_type      **Data_Dict;

        Balance_Dict_type   **Balance_Dict;

        Dump_Dict_type      *Dump_Dict;

        Topo_type           *Topo;

        Tracer_type         *Tracer;

        Norm_type           *Norms;

        C2C_type            ***C2Cs;             /* [states, phases, frames] */

        Balance_type        **Balance;
#endif
    } rCFD_type;

#endif
