/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 */
#ifndef __BKE_EFFECT_H__
#define __BKE_EFFECT_H__

/** \file
 * \ingroup bke
 */

#include "DNA_modifier_types.h"

#include "BLI_utildefines.h"

struct Collection;
struct Depsgraph;
struct ListBase;
struct Object;
struct ParticleData;
struct ParticleKey;
struct ParticleSimulationData;
struct Scene;
struct ViewLayer;

struct EffectorWeights *BKE_effector_add_weights(struct Collection *collection);

/* Input to effector code */
typedef struct EffectedPoint {
  float *loc;
  float *vel;
  float *ave; /* angular velocity for particles with dynamic rotation */
  float *rot; /* rotation quaternion for particles with dynamic rotation */
  float vel_to_frame;
  float vel_to_sec;

  /* only for particles */
  float size, charge;

  unsigned int flag;
  int index;

  struct ParticleSystem *psys; /* particle system the point belongs to */
} EffectedPoint;

typedef struct GuideEffectorData {
  float vec_to_point[3];
  float strength;
} GuideEffectorData;

typedef struct EffectorData {
  /* Effector point */
  float loc[3];
  float nor[3];
  float vel[3];

  float vec_to_point[3];
  float distance, falloff;

  /* only for effector particles */
  float size, charge;

  /* only for vortex effector with surface falloff */
  float nor2[3], vec_to_point2[3];

  int *index; /* point index */
} EffectorData;

/* used for calculating the effector force */
typedef struct EffectorCache {
  struct EffectorCache *next, *prev;

  struct Depsgraph *depsgraph;
  struct Scene *scene;
  struct Object *ob;
  struct ParticleSystem *psys;
  struct SurfaceModifierData *surmd;

  struct PartDeflect *pd;

  /* precalculated for guides */
  struct GuideEffectorData *guide_data;
  float guide_loc[4], guide_dir[3], guide_radius;

  float frame;
  int flag;
} EffectorCache;

typedef struct EffectorRelation {
  struct EffectorRelation *next, *prev;

  struct Object *ob;
  struct ParticleSystem *psys;
  struct PartDeflect *pd;
} EffectorRelation;

struct PartDeflect *BKE_partdeflect_new(int type);
struct PartDeflect *BKE_partdeflect_copy(const struct PartDeflect *pd_src);
void BKE_partdeflect_free(struct PartDeflect *pd);

struct ListBase *BKE_effector_relations_create(struct Depsgraph *depsgraph,
                                               struct ViewLayer *view_layer,
                                               struct Collection *collection);
void BKE_effector_relations_free(struct ListBase *lb);

struct ListBase *BKE_effectors_create(struct Depsgraph *depsgraph,
                                      struct Object *ob_src,
                                      struct ParticleSystem *psys_src,
                                      struct EffectorWeights *weights);
void BKE_effectors_apply(struct ListBase *effectors,
                         struct ListBase *colliders,
                         struct EffectorWeights *weights,
                         struct EffectedPoint *point,
                         float *force,
                         float *impulse);
void BKE_effectors_free(struct ListBase *lb);

void pd_point_from_particle(struct ParticleSimulationData *sim,
                            struct ParticleData *pa,
                            struct ParticleKey *state,
                            struct EffectedPoint *point);
void pd_point_from_loc(
    struct Scene *scene, float *loc, float *vel, int index, struct EffectedPoint *point);
void pd_point_from_soft(
    struct Scene *scene, float *loc, float *vel, int index, struct EffectedPoint *point);

/* needed for boids */
float effector_falloff(struct EffectorCache *eff,
                       struct EffectorData *efd,
                       struct EffectedPoint *point,
                       struct EffectorWeights *weights);
int closest_point_on_surface(SurfaceModifierData *surmd,
                             const float co[3],
                             float surface_co[3],
                             float surface_nor[3],
                             float surface_vel[3]);
int get_effector_data(struct EffectorCache *eff,
                      struct EffectorData *efd,
                      struct EffectedPoint *point,
                      int real_velocity);

/* required for particle_system.c */
#if 0
void do_physical_effector(struct EffectorData *eff,
                          struct EffectorPoint *point,
                          float *total_force);
float effector_falloff(struct EffectorData *eff,
                       struct EffectorPoint *point,
                       struct EffectorWeights *weights);
#endif

/* EffectedPoint->flag */
#define PE_WIND_AS_SPEED 1
#define PE_DYNAMIC_ROTATION 2
#define PE_USE_NORMAL_DATA 4

/* EffectorData->flag */
#define PE_VELOCITY_TO_IMPULSE 1

/* ======== Simulation Debugging ======== */

#define SIM_DEBUG_HASH_BASE 5381

unsigned int BKE_sim_debug_data_hash(int i);
unsigned int BKE_sim_debug_data_hash_combine(unsigned int kx, unsigned int ky);

/* _VA_SIM_DEBUG_HASH#(i, ...): combined hash value of multiple integers */
/* internal helpers*/
#define _VA_SIM_DEBUG_HASH1(a) (BKE_sim_debug_data_hash(a))
#define _VA_SIM_DEBUG_HASH2(a, b) \
  (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH1(b)))
#define _VA_SIM_DEBUG_HASH3(a, b, c) \
  (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH2(b, c)))
#define _VA_SIM_DEBUG_HASH4(a, b, c, d) \
  (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH3(b, c, d)))
#define _VA_SIM_DEBUG_HASH5(a, b, c, d, e) \
  (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH4(b, c, d, e)))
#define _VA_SIM_DEBUG_HASH6(a, b, c, d, e, f) \
  (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH5(b, c, d, e, f)))
#define _VA_SIM_DEBUG_HASH7(a, b, c, d, e, f, g) \
  (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), \
                                   _VA_SIM_DEBUG_HASH6(b, c, d, e, f, g)))
#define _VA_SIM_DEBUG_HASH8(a, b, c, d, e, f, g, h) \
  (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), \
                                   _VA_SIM_DEBUG_HASH7(b, c, d, e, f, g, h)))

#define SIM_DEBUG_HASH(...) VA_NARGS_CALL_OVERLOAD(_VA_SIM_DEBUG_HASH, __VA_ARGS__)

typedef struct SimDebugElement {
  unsigned int category_hash;
  unsigned int hash;

  int type;
  float color[3];

  float v1[3], v2[3];
  char str[64];
} SimDebugElement;

typedef enum eSimDebugElement_Type {
  SIM_DEBUG_ELEM_DOT,
  SIM_DEBUG_ELEM_CIRCLE,
  SIM_DEBUG_ELEM_LINE,
  SIM_DEBUG_ELEM_VECTOR,
  SIM_DEBUG_ELEM_STRING,
} eSimDebugElement_Type;

typedef struct SimDebugData {
  struct GHash *gh;
} SimDebugData;

extern SimDebugData *_sim_debug_data;

void BKE_sim_debug_data_set_enabled(bool enable);
bool BKE_sim_debug_data_get_enabled(void);
void BKE_sim_debug_data_free(void);

void BKE_sim_debug_data_add_element(int type,
                                    const float v1[3],
                                    const float v2[3],
                                    const char *str,
                                    float r,
                                    float g,
                                    float b,
                                    const char *category,
                                    unsigned int hash);
void BKE_sim_debug_data_remove_element(unsigned int hash);

#define BKE_sim_debug_data_add_dot(p, r, g, b, category, ...) \
  { \
    const float v2[3] = {0.0f, 0.0f, 0.0f}; \
    BKE_sim_debug_data_add_element( \
        SIM_DEBUG_ELEM_DOT, p, v2, NULL, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \
  }

#define BKE_sim_debug_data_add_circle(p, radius, r, g, b, category, ...) \
  { \
    const float v2[3] = {radius, 0.0f, 0.0f}; \
    BKE_sim_debug_data_add_element( \
        SIM_DEBUG_ELEM_CIRCLE, p, v2, NULL, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \
  }

#define BKE_sim_debug_data_add_line(p1, p2, r, g, b, category, ...) \
  { \
    BKE_sim_debug_data_add_element( \
        SIM_DEBUG_ELEM_LINE, p1, p2, NULL, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \
  }

#define BKE_sim_debug_data_add_vector(p, d, r, g, b, category, ...) \
  { \
    BKE_sim_debug_data_add_element( \
        SIM_DEBUG_ELEM_VECTOR, p, d, NULL, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \
  }

#define BKE_sim_debug_data_add_string(p, str, r, g, b, category, ...) \
  { \
    BKE_sim_debug_data_add_element( \
        SIM_DEBUG_ELEM_STRING, p, NULL, str, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \
  }

#define BKE_sim_debug_data_remove(...) \
  BKE_sim_debug_data_remove_element(SIM_DEBUG_HASH(__VA_ARGS__))

void BKE_sim_debug_data_clear(void);
void BKE_sim_debug_data_clear_category(const char *category);

#endif
