/*
* morpher.cc
* 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/
*/


#include "morpher.h"
#include "ui_list.h"
#include "console.h"
#include <fstream>
using namespace std;

morpher::morpher () : sl_amount (1, 192, 12), sl_src_start (0, 64, 12), sl_tar_start (0, 64, 12)  {
  name = "Morpher";
	amount = 0.0f;
  src.crv.load ("morph_source.crv");
  tar.crv.load ("morph_target.crv");
  load_params ();
}

morpher::~morpher () {
  save_params ();
  widget_save ("d_morpher", ctrls);
	src.crv.save ("morph_source.crv");
	tar.crv.save ("morph_target.crv");
}

void morpher::load_params () {
  ifstream f (make_fname().c_str(), ios::in);
	string ignore;
	f >> ignore >> num_points;
}

void morpher::save_params () {
  ofstream f (make_fname().c_str(), ios::out);
	f << "num_points " << num_points << endl;
}

void morpher::setup () {

  plugin::setup ();

  /*widget* _ctrls [] = {
    &l_source, &l_target, &cd_source, &cd_target, &b_source, &b_target, &sl_amount, &sp_points, &sl_src_start, &sl_tar_start};
  */
  widget* _ctrls [] = {
    &src.lbl, &tar.lbl, &src.cd, &tar.cd, &src.get, &tar.get, &sl_amount, &sp_points, &sl_src_start, &sl_tar_start
  };

  for (int i = 0; i < 10; ++i) {
		widget* ci = _ctrls[i];
		ctrls.push_back (ci);
	}
  num_ctrls = ctrls.size ();

  src.lbl.set_text ("From");
	tar.lbl.set_text ("To");
	src.get.set_text ("Get");
	tar.get.set_text ("Get");
	src.get.set_listener (this);
	tar.get.set_listener (this);

	/*slider<float>* slr [] = {&sl_amount, &sl_src_start, &sl_tar_start};
	for (int i = 0; i < 3; ++i) {
		slider<float>* sl = slr[i];
		sl->set_val (0.5f);
		sl->set_limits (0.0f, 1.0f);
		sl->set_listener (this);
	}*/

  sl_amount.set_limits (0.0f, 1.0f);
  sl_amount.set_listener (this);
  sl_src_start.set_limits (0.0f, 1.0f);
  sl_tar_start.set_limits (0.0f, 1.0f);

	sp_points.set ("Points", 1, 1, MILLION, this, 0);
	sp_points.set_value (num_points);

	const int sx = 64, sy = 64;
	src.cd.set_size (sx, sy);
	tar.cd.set_size (sx, sy);
  sl_src_start.set_size (sx);
  sl_tar_start.set_size (sx);
  
  widget_load ("d_morpher", ctrls);

	assign_curve (src.crv, src.sol, src.cd);
  assign_curve (tar.crv, tar.sol, tar.cd);
	make_points ();
	render ();

}

void morpher::make_classic_pts (vector< point<float> >& pts, solver& sol) {
	pts.clear ();
	int last_point = num_points - 1;
	float x = 0.0f;
	float dx = 1.0f / last_point;
	for (int i = 0; i < num_points; ++i) {
		float y = sol (x);
		pts.push_back (point<float>(x, y));
		x += dx;
	}
}

void morpher::make_shapeform_pts (vector< point<float> >& pts, solver& sol, multi_curve& crv) {
	float px, py, dummy;
	pts.clear ();
	int last_point = num_points - 1;
	float x = 0.0f;
	float dx = 1.0f / last_point;
	for (int i = 0; i < num_points; ++i) {
		py = sol (x);
		crv.get_xy (x, px, dummy);
		pts.push_back (point<float>(px, py));
		x += dx;
	}
}

void morpher::render () {
	amount = sl_amount ();
	if (equals(amount, 0.0f) || equals(amount, 1.0f)) return;
	shapeform = src.crv.shapeform;
	int last_point = num_points - 1;
	src.start = sl_src_start () * last_point;
	tar.start = sl_tar_start () * last_point;
	src.cur = src.start;
  tar.cur = tar.start;
	points.resize (num_points);
	for (int i = 0; i < num_points; ++i) {
		point<float>& psrc = src.pts[src.cur];
		point<float>& ptar = tar.pts[tar.cur];
		if (++src.cur > last_point) src.cur = 0; 
    if (++tar.cur > last_point) tar.cur = 0;
		point<float> pblend ( (1 - amount) * psrc.x + amount * ptar.x, (1 - amount) * psrc.y + amount * ptar.y);
		points[i] = pblend;
	}
	gen_pts ();
}

int morpher::apply (multi_curve& crv) {
	if (equals(amount, 0.0f)) {
		crv = src.crv;
	} else if (equals(amount, 1.0f)) {
		crv = tar.crv;
	} else 
  return 
    plugin::apply (crv);
  return 1;
}

void morpher::make_points () {
	src.total = src.crv.get_total_points ();
	tar.total = tar.crv.get_total_points ();
	if (src.total < 2 || tar.total < 2) return; 
	num_points = max (max (src.total, tar.total), int (sp_points.f_value));
	if (src.crv.shapeform) make_shapeform_pts (src.pts, src.sol, src.crv); else make_classic_pts (src.pts, src.sol);
	if (tar.crv.shapeform) make_shapeform_pts (tar.pts, tar.sol, tar.crv); else make_classic_pts (tar.pts, tar.sol);
}

void morpher::assign_curve (multi_curve& crv, solver& sol, curve_display& crvdis) {
	sol (&crv);
	crvdis.crv = &crv;
	crvdis.calc_bbox ();
}

void morpher::clicked (button& b) {
	if (&b == &src.get) {
		src.crv = *uis.crved->get_picked_curve ();
		assign_curve (src.crv, src.sol, src.cd); 
		sl_amount.set_val (0.0f);
		changed (sp_points.f_value);
	} else if (&b == &tar.get) {
		tar.crv = *uis.crved->get_picked_curve ();
    assign_curve (tar.crv, tar.sol, tar.cd); 
		sl_amount.set_val (1.0f);
		changed (sp_points.f_value);
	} else plugin::clicked (b);
}

void morpher::changed (slider<float>& s) {
	plugin::changed (sp_points.f_value);
}

void morpher::changed (field& f) {
	make_points ();
	plugin::changed (f);
}
