#ifndef MAT_USER_H_
#define MAT_USER_H_

#include "mat_user_defines.h"
#include <cmath>
#include <vector>
#include <string>

enum ReductionType {
    NO_REDUCTION = 0,
    MIN_REDUCTION = 1,
    MAX_REDUCTION = 2
};

constexpr int MAX_ARRAY_SIZE = 50;
constexpr int SHEAR_SHIFT = 6;
constexpr int BULK_SHIFT = 4;
constexpr int XI_SHIFT = 2;

class MatUser {
  public:
    MatUser() = default;
    virtual ~MatUser() = default;

    MatUser(const MatUser&) = delete;
    MatUser(MatUser&&) = delete;
    MatUser& operator=(const MatUser&) = delete;
    MatUser& operator=(MatUser&&) = delete;

    virtual void runInit(UserMatInitCPU data) const = 0;
    virtual void runMat(UserMatCPU data) const = 0;

    virtual void runInitGPU(UserMatHost host, UserMatDevice device, cudaStream_t stream) const = 0;
    virtual void runMatGPU(UserMatHost host, UserMatDevice device, cudaStream_t stream) const = 0;

    inline int packStiffnessProperties() const {
        return (stiff_shear << SHEAR_SHIFT) | (stiff_bulk << BULK_SHIFT) | (stiff_xi << XI_SHIFT) | stiff_bfac;
    }

    void getConfig(UserMatConfig* config, UserMatProp* properties) const {
        properties->stiffness = packStiffnessProperties();

        int curve_size = std::min(static_cast<int>(curve.size()), MAX_ARRAY_SIZE);
        for (int i = 0; i < curve_size ; ++i) {
            config->curve[i] = curve[i];
        }

        int contour_size = std::min(static_cast<int>(contour.size()), MAX_ARRAY_SIZE);
        for (int i = 0; i < contour_size; ++i) {
            config->contour[i] = ContourMap(contour[i].first, contour[i].second);
        }

        // General configuration data
        config->gpu_enabled = gpu_enabled;
        config->num_hist = num_hist;
        config->pos_epsp = pos_epsp;
        config->pos_temp = pos_temp;
        config->pos_texture = pos_texture;
        config->pos_damage = pos_damage;
        config->erode_flag = erode_flag;
        config->pos_erode_flag = pos_erode_flag;

        // Set properties for damage calculation
        properties->pos_epsp = pos_epsp;
        properties->pos_depsp = pos_depsp;
        properties->pos_rate = pos_rate;
        properties->pos_temp = pos_temp;
        properties->pos_evol = pos_evol;
        properties->pos_damage1 = pos_damage1;
        properties->pos_damage2 = pos_damage2;
        properties->pos_damage_active = pos_damage_active;
    }

  protected:
    // Configure parameters
    bool gpu_enabled = false;
    int num_hist = 0;
    int pos_texture = -1;
    int pos_damage = -1;
    int erode_flag = 0;
    int pos_erode_flag = -1;

    // Stiffness parameters for critical time step calculations
    int stiff_shear = 0;
    int stiff_bulk = 0;
    int stiff_xi = 0;
    int stiff_bfac = 0;

    // Properties for damage calculation
    int pos_epsp = -1;
    int pos_depsp = -1;
    int pos_rate = -1;
    int pos_temp = -1;
    int pos_evol = -1;
    int pos_damage1 = -1;
    int pos_damage2 = -1;
    int pos_damage_active = -1;

    std::vector<int> curve;
    std::vector<std::pair<int, std::string>> contour;
};

#endif // MAT_USER_H_
