/*
* solver.h
* DIN Is Noise is copyright (c) 2006-2024 Jagannathan Sampath
* For more information, please visit http://dinisnoise.org/
*/


#ifndef __SOLVER__
#define __SOLVER__

#include "crvpt.h"
#include "multi_curve.h"
#include <vector>

struct solver;

struct xhandler {
  virtual void operator() (solver& s, float& x, float& dx) = 0;
  virtual ~xhandler () {}
};

struct atmin: xhandler {
  void operator() (solver& s, float& x, float& dx);
};

struct tomax: xhandler {
	void operator() (solver& s, float& x, float& dx);
};

struct atmax: xhandler {
  void operator() (solver& s, float& x, float& dx);
};

struct tomin: xhandler {
  void operator() (solver& s, float& x, float& dx);
};

struct loopmin : atmin {
  void operator() (solver& s, float& x, float& dx);
};

struct loopmax : tomin {
  void operator() (solver& s, float& x, float& dx);
};

struct pongmax: atmax {
  void operator() (solver& s, float& x, float& dx);
};

struct pongmin: atmin {
  void operator() (solver& s, float& x, float& dx);
};

struct curve_editor;

struct gotog : xhandler {
  float g;
  curve_editor* ed;
  gotog (float gg, curve_editor* e); 
  void operator() (solver& s, float& x, float& dx);
  void set (float gg);
};

extern atmin _atmin;
extern tomax _tomax;
extern atmax _atmax;
extern tomin _tomin;
extern loopmin _loopmin;
extern loopmax _loopmax;
extern pongmin _pongmin;
extern pongmax _pongmax;
extern gotog _gotog;

struct solver {
  //
  // given x return y
  //
  // assumes x such that
  // x0 <= x1 <= x2 <= ...... <= xlast
  //

  // solved curve
  multi_curve* mcrv;

  int ncurvs, icurv, last_curv;
  int iseg;

  // line segment on curve expected to contain x
  float startx, starty;
  float endx, endy;
  float m;
  int inf;
  float ycomp;

  // first and last points
  float firstx, firsty;
  float lastx, lasty;
	float deltax;
  float pinx;

  float result; // last result
  
  int seg_lte_right (float x, int c, int i);
	int seg_gte_left (float x, int c, int i);
  void setseg (int c, int i);
 
  inline int lastseg (int c) {
    std::vector<crvpt>& vpts = mcrv->get_profile_points (c);
    return vpts.size() - 2;
  }

  inline int numsegs (int c) {
    return (mcrv->get_profile_points(c).size () - 1);
  }

  int searchleft (float x);
  int searchright (float x);
  int findseg (float x);
  
  solver ();
  solver (multi_curve* crv);


  void operator() (multi_curve* crv);

	void check (float& x, float& dx, xhandler& xmin, xhandler& xmax);

  // given x, solve y
  float operator() (float x);
  float operator() (float &x, float& dx, xhandler& xmin, xhandler& xmax);

  // fast solver when x is incremented by dx n times, solution stored in y
  void operator() (float& x, float& dx, int n, float* y, xhandler& xmin = _tomax, xhandler& xmax = _tomin); // dx as constant value
  void operator() (float& x, float* pdx, int n, float* y, xhandler& xmin = _tomax, xhandler& xmax = _tomin); // dx as variable value array

  // same as ^^^ solver but x is also modulated by mod.
  void operator() (float& x, float& dx, int n, float* mod, float* soln, xhandler& xmin = _tomax, xhandler& xmax = _tomin); // dx as value
  void operator() (float& x, float* pdx, int n, float* mod, float* y, xhandler& xmin = _tomax, xhandler& xmax = _tomin); // dx as array

  // given an array of x, store solution in same array; used by compressor (see compressor.cc/.h)
  void operator() (float* ax, int n, xhandler& xmin = _atmin, xhandler& xmax = _atmax);

  void init ();
  
  void update (); // when multi curve has changed

};

extern solver warp_pitch, warp_vol;
extern solver warp_depth, warp_bpm;

#endif
