/*
* din.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 __din__
#define __din__

#define RED_PSD RED << din::PSD
#define RED_A2D RED << din::A2D
#define TOGO " to go!"

#define CLEAR_SELECTED_DRONES if (SHIFT || CTRL) ; else clear_selected_drones ();
#define SELECTED_DRONES_EXIST if (num_selected_drones == 0) { cons << RED_PSD << eol; return; }
#define _SELECTED_DRONES_EXIST_ if (din0.num_selected_drones == 0) { cons << RED_PSD << eol; return; }

#define RESETALLCHUCKTRAILS if (drone::chuckt::autoresettrails) din0.resetallchucktrails ();

#include "curve_editor.h"
#include "listeners.h"
#include "range.h"
#include "ui.h"
#include "audio.h"
#include "play.h"
#include "oscilloscope.h"
#include "curve_library.h"
#include "beat2value.h"
#include "help.h"
#include "phrasor.h"
#include "input.h"
#include "command.h"
#include "globals.h"
#include "random.h"
#include "drone.h"
#include "gravity.h"
#include "mesh.h"
#include "scale_info.h"
#include "instrument.h"
#include "alarm.h"
#include "textboard.h"
#include "spinner.h"
#include "din_info.h"
#include <list>

extern int BOTTOM;
extern float VOLUME;
extern float DELTA_VOLUME;
extern char BUFFER[];

struct checkbutton;

struct din : instrument, scale_listener, region_listener, pointgotlis {

  // voice
  multi_curve wave;
  curve_editor waved;
  wave_listener wavlis;
  solver wavsol;
  play wavplay;
	float step; // represents pitch (see note.h)
	float delta; // 0 - left of range, 1 - right of range
	std::string pitch_volume_info;

	// noise
	noiser nsr;

  // ranges
  std::vector<range> ranges;
  int num_ranges, last_range, current_range;
  range *firstr, *lastr;
  int visl, visr;
  void find_visible_ranges (int dir = 0);
  int load_ranges ();
	void save_ranges ();
  void create_ranges (int n);
  void setup_ranges (int last_num_octaves, int load = 1);
  void calc_all_range_notes ();
	void calc_added_range_notes (int last_num_octaves, int first_added_range);
	void reset_all_ranges ();
  void delselran ();
  void ranchkdro ();
  void initranpar (int n);

	// range ops
  //
  int range_right_changed (int r, int dx, int mr);
  int range_left_changed (int r, int dx, int mr);

  struct adjustrangest {
    int active;
    int others;
    adjustrangest () {unset ();}
    void set () {active = 1; others = 1;}
    void unset () {active = 0; others = 1;}
  } adjustranges;

  void set_range_width (int r, int w);
  void set_range_width (int s, int t, int w);
	void set_range_height (int r, int h);
	void set_range_height (int s, int t, int h);
  void default_range_to_all (int h = 0);
  void default_range_to_selected (int h = 0);
  void selected_range_to_all (int h = 0);
	int find_range (float x, int r);
	void find_current_range ();
	void notate_all_ranges ();
	void change_range_note (int i, int d);

	// range modulation
	void init_range_mod (int s, int t);
	void pause_resume_ran_mod ();
	void set_ran_mod (int w);
	void toggle_ran_mod ();
	void update_range_mod_solvers (int type, multi_curve& mix);
	void update_drone_mod_solvers (int type, multi_curve& mix);
	void all_ranges_width_changed ();
	void all_ranges_height_changed ();
	void modulate_ranges ();
	void browse_range (int dr);

  int find_tone_and_volume ();
  float get_note_value (const std::string& s);
  void tuning_changed ();

  // microtonal_keyboard
  //
  box<int> win;
  void scroll (int dx, int dy, int warp_mouse = 1);
  int win_mousex, win_mousey;
	int prev_win_mousex, prev_win_mousey;
	int tonex, toney;
  int delta_mousex, delta_mousey;
  void calc_win_mouse ();
  void set_key_to_pitch_at_cursor ();
  void mouse2tonic ();
  void height_changed (int r, int dh);
	void window_resized (int w, int h);

  //
  // drones
  //

  int num_drones;
  std::string num_drones_info;
  std::list<drone*> drones;

  std::vector<drone*> selected_drones;
	int num_selected_drones;
	void load_selected_drones (ifstream& f);
	void save_selected_drones (ofstream& f);

	std::vector<drone*> browsed_drones;
	int num_browsed_drones;
	int browsed_drone;
	int last_browseable_drone;
	void select_all_browsed_drones (int bd);

  std::list<drone*> risers; // rising drones
  std::list<drone*> fallers; // falling drones
  std::list<drone*> launchers; // launch new drones
  std::list<drone*> trackers; // drones that track other drones
  std::list<drone*> attractors; // drones that attract other drones
	std::list<drone*> satellites; // launched drones due to orbit another drone
	std::list<drone*> gravitated; // drones that move under gravity

  // for mute/unmute drones and for drone <> noise conversions
  struct gabber {
    std::string what;
    std::list<drone*> drones;
    int n;
    gabber() : n(0) {}
    void set (din* dd, float t, const string& w, float tdiv = 1.0f, int fid = 0);
    void eval ();
    void erase (drone* d);
    void setgabt ();
    void abort ();
  } gab;

  void drone2noise ();
  void noise2drone ();

  void setdronemastervolume (float d);
  void setdronevol (spinner<float>& s);

  // drone waveform
  multi_curve drone_wave; 
  curve_editor droneed;
  wave_listener dronelis;

  // drone add
  int adding;
  void toggle_adding_drones ();
  drone* add_drone (float wx, float wy, int fromlauncher = 0);

  // drone wand
  int wanding;
  point<int> wand;
  int stopwanding ();

  // drone move
  int moving_drones;
  void start_moving_drones ();
  void toggle_moving_drones ();
  void set_moving_drones (int what);

  // drone set
  void set_drone (drone& d, float wx, float wy);
	void movedrone (drone& d);

	// drone delete
	void delete_drone (drone& ds);
	void delete_selected_drones ();
  int delete_all_drones ();

	// drone load & save
	void load_drones ();
	void save_drones ();

  // drone selection and browsing
  //
	void clear_selected_drones ();
  int select_all_drones ();
  void invert_selected_drones ();
  void add_drone_to_selection (drone* d);
  void remove_drone_from_selection (drone* d);
  std::string get_selected_drones ();
	void browse_drone (int db);
  int is_drone_hit (drone& di, const box<float>& _rgn);
  void calc_selector_range (const box<float>& _rgn, int& sell, int& selr);
	void find_selected_drones (const box<float>& _rgn);
  void print_selected_drones ();
  inline void print_num_drones () {
    sprintf (BUFFER, "%d drones", num_drones);
    num_drones_info = BUFFER;
  }
	int select_launchers ();
  void seloncre (drone* d);

	// scale/rotate drones
	//

	std::vector< point<float> > rvec, svec; // rotation, scale vectors
	float angle; // rotate by angle
	point<float> scl; // scale x, y factors
  void gotpoint (); // for center of scaling/rotation
	void calc_drones_centroid ();
	void calc_xform_vectors (std::vector<point<float> >& V, int n);
  void calc_xform_vectors ();
	int prep_rotate_drones ();
	void rotate_drones (float da);
	void rotate_drones ();
	int prep_scale_drones ();
	void scale_drones (float ds);
	void scale_drones ();
	enum {NONE, SCALE, ROTATE};
	int xforming;
	void resize_xform_vectors ();
  void rail (int dir, double scl);
  void strafe (int dir, double scl);

  // drone tone
  void update_drone_tone ();
  void update_drone_players ();
  void update_drone_solvers (multi_curve& crv);
  void update_drone_modv_solvers ();
  void update_drone_ranges ();
  void update_drone_anchors ();
	void refresh_all_drones ();
	void refresh_drones (int r1, int r2);
	void refresh_drones (int r);

  // drone orbit
  void orbit_selected_drones ();
  void attract_drones ();
  void select_attractors ();
  void select_attractees ();
  void remove_attractee (drone* d);
	void change_drone_vel (spinner<float>& s);
	void rotate_drone_vec (int which, spinner<float>& s, int dir);
  void change_drone_accel (spinner<float>& s);
  void flip_drone_motion ();

	// drone modulation
  enum {AM, FM};
  void change_drone_bpm (int what, float delta);
	void change_drone_bpm (int what, spinner<float>& s);
  void change_drone_depth (int what, float delta);
	void change_drone_depth (int what, spinner<float>& s);
  void change_drone_modpos (int what, spinner<float>& s);
  void set_drone_am2fm2am_bpm (int what);
	void modulate_drones ();
  void reverse_drone_modulation ();

  // auto rotation of velocity/acceleration vectors used as AM/FM direction
  void setautorot (int which, int state, int tog = 0); // velocity/acceleration, yes/no, toggle?
  void setautorotparam (int which, int what);
  void setautorotdir (int which, int dir);
  void setautoflip (int which, int state, int tog = 0); 
  void setautoflipangle (int which);
  void setautopause (int which, int state, int tog = 0); 
  void setautopauseparam (int what, int which); 
  void setrndflipause (int what, int which, int state, int tog = 0);


  // drone death
  void mortalize_drones (int reincarnate = 0);
  void immortalize_drones ();

	// drone visuals
  void change_drone_trail_points (spinner<int>& s);
  void change_drone_handle_size (spinner<int>& s);
  void change_drone_arrow (spinner<float>& s, int w);
  void capdronearrows (int c);
	void reset_drone_arrows ();
	void trail_drones ();

	// drone ops
	//
  int rising, falling;
  void rise_drones ();
  void fall_drones ();

	void sync_drones ();
	int freeze_drones ();
	int thaw_drones ();
	int freeze_orbiters ();
	int thaw_orbiters ();
	int toggle_freeze_drones ();

	void snap_drones (int v);
	void pos_afx_vel (int v);

	// drone connections
	vector<int> stepz;
	int nstepz;
	void calc_stepz (const std::string& fld);
	int connect_drones ();
	int disconnect_drones ();
	void remove_connections (drone* d);
	int can_connect (drone* d1, drone* d2);
	drone *ec;

	// connection drawing
	int con_size;
	float* con_clr;
	float* con_pts;
	int totcon, _2totcon;
	void alloc_conns ();
	void draw_connections ();

  // drone > noise > drone
	void noise_interpolator_changed ();

  // launchers
  //
  void launch_drones ();
	void make_launcher (drone* pd, int l8r = 0);
  void make_launchers ();
  void destroy_launchers ();
  void toggle_launchers ();
  void change_drones_per_min (spinner<float>&);
  void change_drones_per_launch (spinner<int>&);
  void balloon (drone* d);


  void make_trackers ();
  void remove_tracker (drone* ptd);
  void track_drones ();
  void select_tracked_drones ();

	void set_targets ();
	void clear_targets ();
	void remove_drone_from_targets (drone* T);
	void carry_satellites_to_orbit ();
	void kill_old_drones ();
	void change_drone_lifetime (spinner<float>& s);
	void change_orbit_insertion_time (spinner<float>& s);

  // gravity
  //
  void set_drones_under_gravity ();
  void move_drones_under_gravity ();
  void evalgravity ();

  // drone mesh
  //
	struct _meshh { // settings
		int create;
		int draw;
		int num;
		_meshh () : create (0), draw (1), num (0) {}
	} meshh;
  void toggle_create_mesh ();
  void create_drone_mesh ();
	void stop_creating_mesh ();
  std::list<mesh> meshes;
  void remove_drone_from_mesh (drone* pd);
	void remove_drone_from_pre_mesh (drone* d);
	add_mesh_drone amd;

	// drone pendulum
	//
	std::vector<drone_pendulum_group*> drone_pendulums;
	int create_drone_pend;
	void create_drone_pendulum ();
	void toggle_create_drone_pendulum ();
	void stop_creating_drone_pendulum ();
	void update_drone_pendulums ();
	void update_drone_pendulum (drone_pendulum_group& g);
	void remove_from_groups (drone* d);
	void load_drone_pendulum_groups (ifstream& f);
	void save_drone_pendulum_groups (ofstream& f);

	void toggle_create_this (); // mesh or pendulum?

	// visuals
	textboard tb_hz_vol;
  void draw_drones ();
	void draw_vol_dist ();
	void draw_pitch_dist ();
  void set_drone_volume (int i, float v);
	void color_selected_drones ();

	// utils
  drone* get_drone (int id);
	int get_selected_drone_id (drone* d);

  // modulation
  //

	// voice
  //
  beat2value fm, am;
	float fm_step, fm_depth;
  float am_depth, am_vol;
	void change_depth (int type, float d);
	void change_bpm (beat2value& which, float amt);

  // am & fm modulation editors
  curve_editor moded;
  beat2value_listener fmlis, amlis;

	// gater
	//
	beat2value gatr;
  int calc_am_fm_gater ();

  // gater editor
  curve_editor gated;
  curve_library gatlib;
  beat2value_listener gatrlis;

	float fdr_gater_prev_amount;

  // phrasor - record/play/scratch a phrase
  phrasor phrasor0;
  void do_phrase_recording ();
  int finish_phrase_recording ();
  void clear_all_phrases ();

  din (cmdlist& cmdlst);
  ~din ();

  void setup ();
  int handle_input ();

	// scale handling
	void tonic_changed ();
	void scale_changed ();
	void scale_loaded ();
  void load_scale (int _load_drones = 0, int _load_ranges = 1);
  void save ();

	// audio handling
  void sample_rate_changed ();
  void samples_per_channel_changed ();
  int render_audio (float* out0, float* out1);

  // OpenGL 
  float* dvap; int n_dvap;
  float* dcol; int n_dcol;
  float* dap; int n_dap;
  int gl_pts[8];

  // ui
  void toggle_this (int& what, checkbutton& cb);
	box<float> rgn;
	void region_begin ();
	void region_end ();
	void region_abort ();
	const box<float>& region_update ();

	static const std::string PSD, A2D;

	// din saved info
	din_info dinfo;

	// help
	help helptext;

	scale_info* ptr_scaleinfo;

	void enter ();
	void bg (); // background exec
	void draw ();

  rnd<float> rndr, rndg, rndb;
  void set_random_color ();

#ifdef __SVG__
  void write_trail ();
#endif

  void chuck ();
  void evalchuck ();
  void changechucklength (spinner<float>&);
  void changechuckspeed (spinner<float>&);
  void flipchuckspeed ();
  void togchuckspeed ();
  void resetchucktrails (drone& d);
  void resetallchucktrails ();
  void drawchuck ();

  void setmoddir (int w, int d);

  void setvelaccel (int w, int id, int neg, int perp);

};

#define NUM_ORDERERS 7
#define LAST_ORDERER (NUM_ORDERERS - 1) 

struct mkb_selector_t : box_selector, mesh_data {

	ascending_rows_orderer of_asc;
	ascending_cols_orderer of_asc_cols;
	descending_rows_orderer of_desc;
	descending_cols_orderer of_desc_cols;
	random_orderer of_rnd;
	proximity_orderer of_prox_near, of_prox_far;
	orderer_t* orderers [NUM_ORDERERS];
	mkb_selector_t ();

  int lmb_clicked;
  int handle_input ();
	void draw (const box<float>& rgn);
  drone* get_mesh_drone (unsigned int i) {
    if (i < meshd.size ()) return meshd[i];
    return 0;
  }

};

extern mkb_selector_t mkb_selector; 
extern din din0;

void make_arrow (float* A, int k, int cap, int& n, float x, float y, float ux, float uy, float vx, float vy, float u, float v, float t = 1.0f);

#endif
