/*
* drone.h
* DIN Is Noise is copyright (c) 2006-2024 Jagannathan Sampath
* DIN Is Noise is released under GNU Public License 2.0
* For more information, please visit https://dinisnoise.org/
*/

#ifndef __drone__
#define __drone__

#include "fader.h"
#include "solver.h"
#include "play.h"
#include "trail.h"
#include "alarm.h"
#include "modulator.h"
#include "noiser.h"
#include "container.h"
#include "vector2d.h"
#include "angle.h"
#include "autorotator.h"
#include "tokenizer.h"

#include <string>
#include <map>
#include <ostream>

CURVE_LISTENER (modv_lis)

#define RESETCHUCKTRAILS(d) if (drone::chuckt::autoresettrails) resetchucktrails(d);

extern int TRAILSIZE;
extern int BOTTOM;
extern float DELTA_VOLUME;
extern float ARROW_U, ARROW_V;

struct din;
struct drone;

typedef std::list<drone*>::iterator drone_iterator;

struct nnmaxt {
  int n;
  int max;
  nnmaxt () : n(0), max(1) {}
};
std::ostream& operator<< (std::ostream&, nnmaxt&);
std::istream& operator>> (std::istream&, nnmaxt&);

#define MAKE_NNMAXER(name,var) struct name : nnmaxt { name (); } var;

struct attractee {
	int id;
	drone* d;
	attractee (int _id = -1, drone* _d = 0) {id = _id; d = _d;}
  bool operator== (const attractee& ae) {return (d == ae.d);}
};

struct drone {

  static float mastervolume;

  static int UID; // unique id
	int id; // this drone id

  play player; 
  solver sol; 

  float step; // determines pitch (see note.h)

  float vol; // actual volume
	
  int range; // range number on microtonal-keyboard
  float pos; // position in range (0 - left, 1 - right, can go < 0 & > 1 too)

  int sel; // selected?

  float cx, cy; // unmodulated position
  float x, y; // current position

  int snap; // snap to range edge?
  float sx; // snapped x (used for note snap)
  int note_num; // 0 = left note, 1 = right note of range

  float dy; // height from microtonal-keyboard bottom

  float r, g, b; // color
  void setcolor ();
  void setcolor (drone& d, float r);

  // visuals
  //
	trail_t trail; // trail points

  static int HANDLESIZE; // default size
	box<float> handle; // for picking
	int handle_size;

	struct arrowt {
    static float U, V, K, CAP;
		float u, v, t;
    int cap;
		arrowt () {reset ();}
    void reset ();
	} arrow;

	void calc_handle ();

  struct posafxvelt {
    int yes; // drone position affects velocity vector?
    point<float> pt;
    static double minmag;
    posafxvelt() : yes(0), pt (0.0f,0.0f) {}
  } posafxvel;

  // connections
  static float STIFFNESS;
  static std::map<drone*,bool> proc_conn;
	int nconn;
	std::list<drone*> connections;
	std::list<double> mags;
	enum {LINK=0, CHAIN=1};
	int type;
	drone* eval_conns ();
  void remagconns ();

	// states
  enum {DEAD=0, RISING, FALLING, ACTIVE}; 
  int state; // current 
  fader fdr; // for rising/falling -> fade in/out

	int frozen; // frozen?
  double froze_at; // time when frozen
	int freeze ();
	int thaw ();
  void handle_time_pass ();

  //
  // mute/unmute ; drone <> noise
  fader gab;
  float tovol;
  static double gabt;

  // modulation
  modulator mod;

	// orbitals & rocketry
	//
  int orbiting; // orbiting?
	int attractor; // attractor?
	std::list<attractee> attractees; // list of drones attracted by this drone

	float V; // speed 
  struct modvt { // speed modulation
    drone* d;
    solver sol;
    float val;
    double _1life;
    modvt (drone* dd) : d (dd), sol (&crv), val (1.0f), _1life (0.0f) {}
    float operator()() {
      float x = (ui_clk () - d->birth) * _1life; 
      val = sol(x) * d->V;
      return val;
    }
    static multi_curve crv;
    static curve_editor ed;
    static modv_lis lis;
    static void init ();
  } modv;

  float vx, vy; // current velocity vector
  int vrev;

	float A; // acceleration
	float ax, ay; // acceleration unit vector
	float xi, yi; // interim position
	double insert; // time to insert into orbit

  float xv, yv; // position of velocity vector tip
  float xa, ya; // position of acceleration vector tip

  // auto rotation
  static defvelaccel v0, a0;
  struct autorotva {
    autorotator v, a;
    autorotator* arr[2];
    autorotva () : v (v0), a (a0) {
      arr[0] = &v; 
      arr[1] = &a;
    }
    void calc (double dt) {
      v.calc (dt);
      a.calc (dt);
    }
  } autorot;

  static int anchored; 

  int gravity; // accelerated by gravity?

  int launcher; // launches drones?
  alarm_t launch_every; // alarm time in seconds
  float lpm; // launches per minute
  int dpl; // drones per launch
  drone* launched_by; 

  MAKE_NNMAXER (bouncet, bounces)
  MAKE_NNMAXER (genn, gen)

  // targets for launchers to launch drones to
  std::vector<drone*> targets; 
  int cur_target;
  int num_targets;
  void clear_targets ();
  drone* target;

	static double LIFETIME; 
	double birth; // time at birth
	double life; // lifetime
	void setlife (float l);
  int reincarnate;

  static double INSERTTIME; // time for insert into orbit

  enum {NONE, POINT, USE};
  int tracking; 
  drone* tracked_drone; // the tracked drone

  // wanding
  static struct wandt {
    int dist;
    int dist2;
    wandt (int d) {set (d);}
    void set (int d) {
      dist = d;
      dist2 = d * d;
    }
  } wand;

  enum {UNSET=0, INTERPOLATE, EMPLACE};
  int update_pv;
  void update_pitch_volume ();

	drone (float bottom = 0.0f);

  static int ref;
	~drone();

  void set_pos (float x, float y);
	void set_center (float _cx, float _cy, int e = 1);
  void move_center ();

  void change_modpos (int i, int what, float delta);
	void change_depth (int i, int what, float delta);
  void change_bpm (int i, int what, float delta);
	void change_bpm (mod_params& mp, float delta);

  enum {DRONE, NOISE, DRONE_OR_NOISE};
  static int IS;
	int is;
  void setis ();

	noiser nsr;
  void setnoise ();

  enum {IMMORTAL, MORTAL, REINCARNATE};
  static int ARE;

  // for drone <> noise conversions
  struct fin {
    virtual void finished (din& d, drone* pd) = 0;
  } *finl;

  static struct drone2noise : fin {
    void finished (din& mkb, drone* pd);
  } dnl;

  static struct noise2drone : fin {
    void finished (din& mkb, drone* pd);
  } ndl;

  static fin* fins [3]; // 0 = null, 1 = drone2noise, 2 = noise2drone, init @ main.cc

  struct chuckt { // virtual geometric chuck

    static anglet apt; // angle per turn 
    static int outline; // draw outline?
    static int autoresettrails; // auto reset trails when chuck params changed?

    int yes;
    drone* sun; // this drone aka planet orbits around sun
    drone* sat; // planet's satellite
    float ux, uy; // unit vector of sun -> planet vector
    float len; // length of sun -> planet vector
    float speed; // angular speed, scales apt
    float speed0; // for toggling speed, 0
    int dir; // -1 = clockwise; +1 = clockwise
    float cx, cy; // center b4 chucked

    chuckt () {
      yes = 0;
      sun = sat = 0;
      ux = uy = 0.0f;
      len = 0.0f;
      speed = 1.0f;
      speed0 = 0.0f;
      dir = 1;
      cx = cy = 0.0f;
    }

    void set (int y, drone* n) {
      yes = y;
      sun = n;
    }

    void calc (drone* planet) {
      len = unit_vector (ux, uy, sun->cx, sun->cy, planet->cx, planet->cy);
      if (sat) sat->chuck.calc (sat);
    }

    void de () {
      if (sat) {
        sat->chuck.sun = sun;
        if (sun) {
          sun->chuck.sat = sat;
          sat->chuck.calc (sat);
        }
      } else {
        if (sun) sun->chuck.sat = 0;
      }
      yes = 0;
    }

    void re (drone& p);

    void print ();

    void resettrail (drone* p);

  } chuck;

};

std::ostream& operator<< (std::ostream& f, drone::arrowt& aw);
std::istream& operator>> (std::istream&, drone::arrowt& aw);
std::ostream& operator<< (std::ostream&, drone::chuckt& ch);
std::istream& operator>> (std::istream&, drone::chuckt& ch);

struct group {
	int n;
	std::vector<drone*> drones;
	group () {n = 0;}
	group (std::vector<drone*> mem, int n);
	int member (drone* d) {
		std::vector<drone*>::iterator db = drones.begin (), de = drones.end();
		if (find (db, de, d) == de) return 0; else return 1; 
	}
	int remove (drone* d) {
		if (erase (drones, d)) {
			--n;
			return 1;
		}
		return 0;
	}
};

struct drone_pendulum_group : group {
	int orient;
	int depth;
	drone_pendulum_group (int o = 0, int d = 0) { orient = o; depth = d;}
	drone_pendulum_group (int o, int d, std::vector<drone*> mem, int n) : group (mem, n), orient(o), depth(d)  {}
};

#endif
