/* 
    Autogenerated using:
    - Template: 'include/cubnm/models/model.hpp.mako'
    - Model definition: 'codegen/recipes/rww.yaml'
    Do not modify this autogenerated code. Instead modify template
    or model definition.
*/
#ifndef RWW_HPP
#define RWW_HPP
#include "cubnm/models/base.hpp"

extern void analytical_fic_het(
        gsl_matrix * sc, double G, double * w_EE, double * w_EI,
        gsl_vector * w_IE_out, bool * _unstable
        );


class rWWModel : public BaseModel {
public:
    // Constants struct - must always be defined even if empty
    struct Constants {
        double dt; // integration step
        double sqrt_dt; // square root of integration step
        double a_E; // excitatory input-output parameter a (n/C)
        double b_E; // excitatory input-output parameter b (Hz)
        double d_E; // excitatory input-output parameter d (s)
        double a_I; // inhibitory input-output parameter a (n/C)
        double b_I; // inhibitory input-output parameter b (Hz)
        double d_I; // inhibitory input-output parameter d (s)
        double gamma_E; // excitatory kinetic parameter
        double gamma_I; // inhibitory kinetic parameter
        double tau_E; // NMDA time constant (ms)
        double tau_I; // GABA time constant (ms)
        double I_0; // overall effective external input (nA)
        double w_E; // scaling of external input for excitatory pool
        double w_I; // scaling of external input for inhibitory pool
        double w_II; // inhibitory self-coupling
        double I_ext; // external input (nA)
        double w_E__I_0; // pre-calculated w_E * I_0
        double w_I__I_0; // pre-calculated w_I * I_0
        double b_a_ratio_E; // pre-calculated b_E / a_E
        double itau_E; // inverse of tau_E
        double itau_I; // inverse of tau_I
        double dt_itau_E; // dt / tau_E
        double dt_gamma_E; // dt * gamma_E
        double dt_itau_I; // dt / tau_I
        double dt_gamma_I; // dt * gamma_I
        double tau_E_s; // tau_E in seconds for FIC
        double tau_I_s; // tau_I in seconds for FIC
        double gamma_E_s; // gamma_E for FIC
        double gamma_I_s; // gamma_I for FIC
        double r_I_ss; // steady-state inhibitory firing rate (Hz)
        double r_E_ss; // steady-state excitatory firing rate (Hz)
        double I_I_ss; // steady-state inhibitory current (nA)
        double I_E_ss; // steady-state excitatory current (nA)
        double S_I_ss; // steady-state inhibitory gating
        double S_E_ss; // steady-state excitatory gating
    };

    // Config struct - must always be defined even if empty
    struct Config {
        bool do_fic{true}; // whether to apply feedback inhibition control. If provided wIE parameters will be ignored
        int max_fic_trials{0}; // maximum number of numerical FIC trials. If set to 0, FIC will be done only analytically
        int I_SAMPLING_START{1000}; // starting time of numerical FIC I_E sampling (msec)
        int I_SAMPLING_END{10000}; // end time of numerical FIC I_E sampling (msec)
        int I_SAMPLING_DURATION{I_SAMPLING_END - I_SAMPLING_START + 1}; // duration of numerical FIC I_E sampliing (bw iterations)
        double init_delta{0.02}; // initial delta for numerical FIC adjustment
        double fic_penalty_scale{0.5}; // how much deviation from FIC target mean rE of 3 Hz is penalized. Set to 0 to disable FIC penalty.
    };

    using BaseModel::BaseModel;
    
    ~rWWModel() {
        if (cpu_initialized) {
            this->free_cpu();
            if (cpu_noise_initialized) {
                this->free_cpu_noise();
            }
        }
        if (gpu_initialized) {
            this->free_gpu();
            if (gpu_noise_initialized) {
                this->free_gpu_noise();
            }
        }
    }

    // Static model properties
    static constexpr char* name = "rWW";
    static constexpr int n_state_vars = 6;
    static constexpr int n_intermediate_vars = 7;
    static constexpr int n_noise = 2;
    static constexpr int n_global_params = 1;
    static constexpr int n_regional_params = 4;
    static constexpr int conn_state_var_idx = 4;
    static constexpr int bold_state_var_idx = 4;
    static constexpr int n_ext_int = 0;
    static constexpr int n_ext_bool = 0;
    static constexpr int n_ext_int_shared = 1;
    static constexpr int n_ext_bool_shared = 2;
    static constexpr int n_global_out_int = 1;
    static constexpr int n_global_out_bool = 2;
    static constexpr int n_global_out_double = 0;
    static constexpr int n_regional_out_int = 0;
    static constexpr int n_regional_out_bool = 0;
    static constexpr int n_regional_out_double = 0;
    static constexpr bool has_post_bw_step = true;
    static constexpr bool has_post_integration = true;
    static constexpr bool is_osc = false;

    // Static members
    static Constants mc;
    Config conf;

    // Static initialization method
    static void init_constants(double dt = 0.1);
    void set_conf(std::map<std::string, std::string> config_map) override;
    void prep_params(double ** global_params, double ** regional_params, double * v_list,
            double ** SC, int * SC_indices, double * SC_dist,
            bool ** global_out_bool, int ** global_out_int) override final;
    // Device-callable methods (GPU)
    CUDA_CALLABLE_MEMBER void init(
        double* _state_vars, double* _intermediate_vars, 
        double* _global_params, double* _regional_params,
        int* _ext_int, bool* _ext_bool,
        int* _ext_int_shared, bool* _ext_bool_shared);

    CUDA_CALLABLE_MEMBER void step(
        double* _state_vars, double* _intermediate_vars,
        double* _global_params, double* _regional_params,
        double& tmp_globalinput,
        double* noise, long& noise_idx);

    CUDA_CALLABLE_MEMBER void post_bw_step(
        double* _state_vars, double* _intermediate_vars,
        int* _ext_int, bool* _ext_bool, 
        int* _ext_int_shared, bool* _ext_bool_shared,
        bool& restart,
        double* _global_params, double* _regional_params,
        int& ts_bold);

    CUDA_CALLABLE_MEMBER void restart(
        double* _state_vars, double* _intermediate_vars, 
        double* _global_params, double* _regional_params,
        int* _ext_int, bool* _ext_bool,
        int* _ext_int_shared, bool* _ext_bool_shared);

    CUDA_CALLABLE_MEMBER void post_integration(
        double ***state_vars_out, 
        int **global_out_int, bool **global_out_bool,
        double* _state_vars, double* _intermediate_vars, 
        int* _ext_int, bool* _ext_bool, 
        int* _ext_int_shared, bool* _ext_bool_shared,
        double** global_params, double** regional_params,
        double* _global_params, double* _regional_params,
        int& sim_idx, const int& nodes, int& j);

    // GPU interface methods
    void init_gpu(BWConstants bwc, bool force_reinit) override final {
        _init_gpu<rWWModel>(this, bwc, force_reinit);
    }

    void run_simulations_gpu(
        double * BOLD_ex_out, double * fc_trils_out, double * fcd_trils_out,
        double ** global_params, double ** regional_params, double * v_list,
        double ** SC, int * SC_indices, double * SC_dist) override final {
        _run_simulations_gpu<rWWModel>(
            BOLD_ex_out, fc_trils_out, fcd_trils_out, 
            global_params, regional_params, v_list,
            SC, SC_indices, SC_dist, this);
    }

    // CPU host methods (always required)
    void h_init(
        double* _state_vars, double* _intermediate_vars, 
        double* _global_params, double* _regional_params,
        int* _ext_int, bool* _ext_bool,
        int* _ext_int_shared, bool* _ext_bool_shared) override final;

    void h_step(
        double* _state_vars, double* _intermediate_vars,
        double* _global_params, double* _regional_params,
        double& tmp_globalinput,
        double* noise, long& noise_idx) override final;

    void _j_restart(
        double* _state_vars, double* _intermediate_vars, 
        double* _global_params, double* _regional_params,
        int* _ext_int, bool* _ext_bool,
        int* _ext_int_shared, bool* _ext_bool_shared) override final;

    void _j_post_bw_step(
        double* _state_vars, double* _intermediate_vars,
        int* _ext_int, bool* _ext_bool,
        int* _ext_int_shared, bool* _ext_bool_shared,
        bool& restart,
        double* _global_params, double* _regional_params,
        int& ts_bold) override final;
    void h_post_bw_step(
        double** _state_vars, double** _intermediate_vars,
        int** _ext_int, bool** _ext_bool,
        int* _ext_int_shared, bool* _ext_bool_shared,
        bool& restart,
        double* _global_params, double** _regional_params,
        int& ts_bold) override final;

    void h_post_integration(
        double ***state_vars_out, 
        int **global_out_int, bool **global_out_bool,
        double* _state_vars, double* _intermediate_vars,
        int* _ext_int, bool* _ext_bool,
        int* _ext_int_shared, bool* _ext_bool_shared,
        double** global_params, double** regional_params,
        double* _global_params, double* _regional_params,
        int& sim_idx, const int& nodes, int& j) override final;

    // CPU interface methods
    void init_cpu(bool force_reinit) override final {
        _init_cpu<rWWModel>(this, force_reinit);
    }

    void run_simulations_cpu(
        double * BOLD_ex_out, double * fc_trils_out, double * fcd_trils_out,
        double ** global_params, double ** regional_params, double * v_list,
        double ** SC, int * SC_indices, double * SC_dist) override final {
        _run_simulations_cpu<rWWModel>(
            BOLD_ex_out, fc_trils_out, fcd_trils_out, 
            global_params, regional_params, v_list,
            SC, SC_indices, SC_dist, this);
    }

    // Getter methods
    int get_n_state_vars() override final {
        return n_state_vars;
    }

    int get_n_global_out_bool() override final {
        return n_global_out_bool;
    }

    int get_n_global_out_int() override final {
        return n_global_out_int;
    }

    int get_n_global_params() override final {
        return n_global_params;
    }

    int get_n_regional_params() override final {
        return n_regional_params;
    }

    char * get_name() override final {
        return name;
    }
};

#endif