/*
 * 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) 2006 Blender Foundation.
 * All rights reserved.
 */

#pragma once

/** \file
 * \ingroup bke
 */

#include "DNA_boid_types.h"       /* for #BoidData */
#include "DNA_pointcache_types.h" /* for #BPHYS_TOT_DATA */

#include <stdio.h> /* for #FILE */

#ifdef __cplusplus
extern "C" {
#endif

/* Point cache clearing option, for BKE_ptcache_id_clear, before
 * and after are non-inclusive (they won't remove the cfra) */
#define PTCACHE_CLEAR_ALL 0
#define PTCACHE_CLEAR_FRAME 1
#define PTCACHE_CLEAR_BEFORE 2
#define PTCACHE_CLEAR_AFTER 3

/* Point cache reset options */
#define PTCACHE_RESET_DEPSGRAPH 0
#define PTCACHE_RESET_BAKED 1
#define PTCACHE_RESET_OUTDATED 2
/* #define PTCACHE_RESET_FREE           3 */ /*UNUSED*/

/* Add the blend-file name after `blendcache_`. */
#define PTCACHE_EXT ".bphys"
#define PTCACHE_PATH "blendcache_"

/* File open options, for BKE_ptcache_file_open */
#define PTCACHE_FILE_READ 0
#define PTCACHE_FILE_WRITE 1
#define PTCACHE_FILE_UPDATE 2

/* PTCacheID types */
#define PTCACHE_TYPE_SOFTBODY 0
#define PTCACHE_TYPE_PARTICLES 1
#define PTCACHE_TYPE_CLOTH 2
#define PTCACHE_TYPE_SMOKE_DOMAIN 3
#define PTCACHE_TYPE_SMOKE_HIGHRES 4
#define PTCACHE_TYPE_DYNAMICPAINT 5
#define PTCACHE_TYPE_RIGIDBODY 6
#define PTCACHE_TYPE_SIM_PARTICLES 7

/* high bits reserved for flags that need to be stored in file */
#define PTCACHE_TYPEFLAG_COMPRESS (1 << 16)
#define PTCACHE_TYPEFLAG_EXTRADATA (1 << 17)

#define PTCACHE_TYPEFLAG_TYPEMASK 0x0000FFFF
#define PTCACHE_TYPEFLAG_FLAGMASK 0xFFFF0000

/* PTCache read return code */
#define PTCACHE_READ_EXACT 1
#define PTCACHE_READ_INTERPOLATED 2
#define PTCACHE_READ_OLD 3

/* Structs */
struct BlendDataReader;
struct BlendWriter;
struct ClothModifierData;
struct DynamicPaintSurface;
struct FluidModifierData;
struct ListBase;
struct Main;
struct Object;
struct ParticleKey;
struct ParticleSystem;
struct PointCache;
struct RigidBodyWorld;
struct Scene;
struct SoftBody;
struct ViewLayer;

/* temp structure for read/write */
typedef struct PTCacheData {
  unsigned int index;
  float loc[3];
  float vel[3];
  float rot[4];
  float ave[3];
  float size;
  float times[3];
  struct BoidData boids;
} PTCacheData;

typedef struct PTCacheFile {
  FILE *fp;

  int frame, old_format;
  unsigned int totpoint, type;
  unsigned int data_types, flag;

  struct PTCacheData data;
  void *cur[BPHYS_TOT_DATA];
} PTCacheFile;

#define PTCACHE_VEL_PER_SEC 1

enum {
  PTCACHE_FILE_PTCACHE = 0,
};

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

  struct Scene *scene;
  struct ID *owner_id;
  void *calldata;
  unsigned int type, file_type;
  unsigned int stack_index;
  unsigned int flag;

  unsigned int default_step;
  unsigned int max_step;

  /* flags defined in DNA_object_force_types.h */
  unsigned int data_types, info_types;

  /* copies point data to cache data */
  int (*write_point)(int index, void *calldata, void **data, int cfra);
  /* copies cache cata to point data */
  void (*read_point)(int index, void *calldata, void **data, float cfra, const float *old_data);
  /* interpolated between previously read point data and cache data */
  void (*interpolate_point)(int index,
                            void *calldata,
                            void **data,
                            float cfra,
                            float cfra1,
                            float cfra2,
                            const float *old_data);

  /* copies point data to cache data */
  int (*write_stream)(PTCacheFile *pf, void *calldata);
  /* copies cache cata to point data */
  int (*read_stream)(PTCacheFile *pf, void *calldata);

  /* copies custom extradata to cache data */
  void (*write_extra_data)(void *calldata, struct PTCacheMem *pm, int cfra);
  /* copies custom extradata to cache data */
  void (*read_extra_data)(void *calldata, struct PTCacheMem *pm, float cfra);
  /* copies custom extradata to cache data */
  void (*interpolate_extra_data)(
      void *calldata, struct PTCacheMem *pm, float cfra, float cfra1, float cfra2);

  /* Total number of simulated points
   * (the cfra parameter is just for using same function pointer with totwrite). */
  int (*totpoint)(void *calldata, int cfra);
  /* report error if number of points does not match */
  void (*error)(const struct ID *owner_id, void *calldata, const char *message);
  /* number of points written for current cache frame */
  int (*totwrite)(void *calldata, int cfra);

  int (*write_header)(PTCacheFile *pf);
  int (*read_header)(PTCacheFile *pf);

  struct PointCache *cache;
  /* used for setting the current cache from ptcaches list */
  struct PointCache **cache_ptr;
  struct ListBase *ptcaches;
} PTCacheID;

typedef struct PTCacheBaker {
  struct Main *bmain;
  struct Scene *scene;
  struct ViewLayer *view_layer;
  struct Depsgraph *depsgraph;
  int bake;
  int render;
  int anim_init;
  int quick_step;
  struct PTCacheID pid;

  void (*update_progress)(void *data, float progress, int *cancel);
  void *bake_job;
} PTCacheBaker;

/* PTCacheEditKey->flag */
#define PEK_SELECT 1
#define PEK_TAG 2
#define PEK_HIDE 4
#define PEK_USE_WCO 8

typedef struct PTCacheEditKey {
  float *co;
  float *vel;
  float *rot;
  float *time;

  float world_co[3];
  float ftime;
  float length;
  short flag;
} PTCacheEditKey;

/* PTCacheEditPoint->flag */
#define PEP_TAG 1
#define PEP_EDIT_RECALC 2
#define PEP_TRANSFORM 4
#define PEP_HIDE 8

typedef struct PTCacheEditPoint {
  struct PTCacheEditKey *keys;
  int totkey;
  short flag;
} PTCacheEditPoint;

typedef struct PTCacheUndo {
  struct PTCacheEditPoint *points;

  /* particles stuff */
  struct ParticleData *particles;
  struct KDTree_3d *emitter_field;
  float *emitter_cosnos;
  int psys_flag;

  /* cache stuff */
  struct ListBase mem_cache;

  int totpoint;

  size_t undo_size;
} PTCacheUndo;

enum {
  /* Modifier stack got evaluated during particle edit mode, need to copy
   * new evaluated particles to the edit struct.
   */
  PT_CACHE_EDIT_UPDATE_PARTICLE_FROM_EVAL = (1 << 0),
};

typedef struct PTCacheEdit {
  int flags;

  PTCacheEditPoint *points;

  struct PTCacheID pid;

  /* particles stuff */
  struct ParticleSystem *psys;
  struct ParticleSystem *psys_eval;
  struct ParticleSystemModifierData *psmd;
  struct ParticleSystemModifierData *psmd_eval;
  struct KDTree_3d *emitter_field;
  /* Localspace face centers and normals (average of its verts), from the derived mesh. */
  float *emitter_cosnos;
  int *mirror_cache;

  struct ParticleCacheKey **pathcache; /* path cache (runtime) */
  ListBase pathcachebufs;

  int totpoint, totframes, totcached, edited;
} PTCacheEdit;

void BKE_ptcache_make_particle_key(struct ParticleKey *key, int index, void **data, float time);

/**************** Creating ID's ****************************/

void BKE_ptcache_id_from_softbody(PTCacheID *pid, struct Object *ob, struct SoftBody *sb);
void BKE_ptcache_id_from_particles(PTCacheID *pid, struct Object *ob, struct ParticleSystem *psys);
void BKE_ptcache_id_from_cloth(PTCacheID *pid, struct Object *ob, struct ClothModifierData *clmd);
/**
 * The fluid modifier does not actually use this anymore, but some parts of Blender expect that it
 * still has a point cache currently. For example, the fluid modifier uses
 * #DEG_add_collision_relations, which internally creates relations with the point cache.
 */
void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct FluidModifierData *fmd);
void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid,
                                      struct Object *ob,
                                      struct DynamicPaintSurface *surface);
void BKE_ptcache_id_from_rigidbody(PTCacheID *pid, struct Object *ob, struct RigidBodyWorld *rbw);

/**
 * \param ob: Optional, may be NULL.
 * \param scene: Optional may be NULL.
 */
PTCacheID BKE_ptcache_id_find(struct Object *ob, struct Scene *scene, struct PointCache *cache);
void BKE_ptcache_ids_from_object(struct ListBase *lb,
                                 struct Object *ob,
                                 struct Scene *scene,
                                 int duplis);

/****************** Query funcs ****************************/

/**
 * Check whether object has a point cache.
 */
bool BKE_ptcache_object_has(struct Scene *scene, struct Object *ob, int duplis);

/************ ID specific functions ************************/
void BKE_ptcache_id_clear(PTCacheID *id, int mode, unsigned int cfra);
bool BKE_ptcache_id_exist(PTCacheID *id, int cfra);
int BKE_ptcache_id_reset(struct Scene *scene, PTCacheID *id, int mode);
void BKE_ptcache_id_time(PTCacheID *pid,
                         struct Scene *scene,
                         float cfra,
                         int *startframe,
                         int *endframe,
                         float *timescale);
int BKE_ptcache_object_reset(struct Scene *scene, struct Object *ob, int mode);

void BKE_ptcache_update_info(PTCacheID *pid);

/*********** General cache reading/writing ******************/

/**
 * Size of cache data type.
 */
int BKE_ptcache_data_size(int data_type);

/**
 * Is point with index in memory cache?
 * Check to see if point number "index" is in `pm` (uses binary search for index data).
 */
int BKE_ptcache_mem_index_find(struct PTCacheMem *pm, unsigned int index);

/* Memory cache read/write helpers. */

void BKE_ptcache_mem_pointers_init(struct PTCacheMem *pm, void *cur[BPHYS_TOT_DATA]);
void BKE_ptcache_mem_pointers_incr(void *cur[BPHYS_TOT_DATA]);
int BKE_ptcache_mem_pointers_seek(int point_index,
                                  struct PTCacheMem *pm,
                                  void *cur[BPHYS_TOT_DATA]);

/**
 * Main cache reading call.
 * Possible to get old or interpolated result.
 */
int BKE_ptcache_read(PTCacheID *pid, float cfra, bool no_extrapolate_old);

/**
 * Main cache writing call.
 * Writes cache to disk or memory.
 */
int BKE_ptcache_write(PTCacheID *pid, unsigned int cfra);

/******************* Allocate & free ***************/
struct PointCache *BKE_ptcache_add(struct ListBase *ptcaches);
void BKE_ptcache_free_mem(struct ListBase *mem_cache);
void BKE_ptcache_free(struct PointCache *cache);
void BKE_ptcache_free_list(struct ListBase *ptcaches);
/* returns first point cache */
struct PointCache *BKE_ptcache_copy_list(struct ListBase *ptcaches_new,
                                         const struct ListBase *ptcaches_old,
                                         int flag);

/********************** Baking *********************/

/**
 * Bakes cache with cache_step sized jumps in time, not accurate but very fast.
 */
void BKE_ptcache_quick_cache_all(struct Main *bmain,
                                 struct Scene *scene,
                                 struct ViewLayer *view_layer);

/**
 * Bake cache or simulate to current frame with settings defined in the baker.
 * if bake is not given run simulations to current frame.
 */
void BKE_ptcache_bake(struct PTCacheBaker *baker);

/**
 * Convert disk cache to memory cache.
 */
void BKE_ptcache_disk_to_mem(struct PTCacheID *pid);
/**
 * Convert memory cache to disk cache.
 */
void BKE_ptcache_mem_to_disk(struct PTCacheID *pid);
/**
 * Convert disk cache to memory cache and vice versa. Clears the cache that was converted.
 */
void BKE_ptcache_toggle_disk_cache(struct PTCacheID *pid);
/**
 * Rename all disk cache files with a new name. Doesn't touch the actual content of the files.
 */
void BKE_ptcache_disk_cache_rename(struct PTCacheID *pid,
                                   const char *name_src,
                                   const char *name_dst);

/**
 * Loads simulation from external (disk) cache files.
 */
void BKE_ptcache_load_external(struct PTCacheID *pid);
/**
 * Set correct flags after successful simulation step.
 */
void BKE_ptcache_validate(struct PointCache *cache, int framenr);
/**
 * Set correct flags after unsuccessful simulation step.
 */
void BKE_ptcache_invalidate(struct PointCache *cache);

/********************** .blend File I/O *********************/

void BKE_ptcache_blend_write(struct BlendWriter *writer, struct ListBase *ptcaches);
void BKE_ptcache_blend_read_data(struct BlendDataReader *reader,
                                 struct ListBase *ptcaches,
                                 struct PointCache **ocache,
                                 int force_disk);

#ifdef __cplusplus
}
#endif
