/*
* number.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 "number.h"
#include "vector2d.h"
#include "ui_list.h"
#include "multi_curve.h"
#include "tcl_interp.h"
#include <vector>
#include <fstream>

using namespace std;

extern ui_list uis;

number::number () : slw (255), slh (10), sl_red (0, slw, slh), sl_green (0, slw, slh), sl_blue (0, slw, slh) {
  name = "Number";
	uchar = 'A';
	short_ = 666;
	int_ = 1729;
	float_ = 3.141592;
	double_ = 2.71828183; 
	ucolor.r = 128; ucolor.g = 128; ucolor.b = 255;
	type = CHAR;
	make_shapeform = 1;
	y[1]=1.0f;
	y[0]=0.0f;
}

number::~number () {
  widget_save ("d_number", ctrls);
  save_params ();
}

void number::load_params () {
  ifstream f (make_fname().c_str(), ios::in);
	string ignore;
	f >> ignore >> type;
	f >> ignore;
	slider<int>* sl [] = {&sl_red, &sl_green, &sl_blue};
	int s;
	for (int i = 0; i < 3; ++i) {f >> s; sl[i]->set_val (s);}
	f >> ignore >> double_;
	f >> ignore >> float_;
	f >> ignore >> int_;
	f >> ignore >> short_;
	f >> ignore >> uchar;
	float v;
	spinner<float>* sp[] = {&sp_0equals, &sp_1equals};
	for (int i = 0; i < 2; ++i) {
		f >> ignore >> v; sp[i]->set_value (v);
		y[i] = v;
	}
	f >> ignore >> v; sp_scale.set_value (v);
	f >> ignore >> make_shapeform; cb_make_shapeform.set_state (make_shapeform);
	int w; f >> ignore >> w; cb_wrap.set_state (w);
}

void number::save_params () {
  ofstream f (make_fname().c_str(), ios::out);
	f << "type " << type << endl;
	f << "color " << (int) ucolor.r << ' ' << (int) ucolor.g << ' ' << (int) ucolor.b << endl;
	f << "double " << double_ << endl;
	f << "float " << float_ << endl;
	f << "int " << int_ << endl;
	f << "short " << short_ << endl;
	f << "char " << (char) uchar << endl;
	f << "0_equals " << (float) sp_0equals.f_value << endl;
	f << "1_equals " << (float) sp_1equals.f_value << endl;
	f << "scale " << (float) sp_scale.f_value << endl;
	f << "make_shapeform " << (int) cb_make_shapeform.state << endl;
	f << "wrap " << (int) cb_wrap.state << endl;
}

void number::setup () {

  plugin::setup ();

	ol_bitsof.set_listener (this);

	const char* const _types [] = {"char", "short", "int", "float", "double", "colour"};
	for (int i = 0, num_types = 6; i < num_types; ++i) types.push_back (_types[i]);

	widget* _ctrls [] = {&ol_bitsof, &lf_value, &sp_1equals, &sp_0equals, &sp_scale, &cb_make_shapeform, &bd_bits, &sl_red, &sl_green, &sl_blue, &b_flip, &b_left_shift, &b_right_shift, &cb_wrap};
	for (int i = 0; i < 14; ++i) ctrls.push_back (_ctrls[i]);
	num_ctrls = ctrls.size ();

	button* btn [] = {&b_flip, &b_left_shift, &b_right_shift};
	const char* const l[] = {"Flip", "<<", ">>"};
	for (int i = 0; i < 3; ++i) {
		button* bi = btn[i];
		bi->set_text (l[i]);
		bi->set_listener (this);
	}
	cb_wrap.set_text ("Rotate");
	b_left_shift.click_repeat = b_right_shift.click_repeat = 1;

	slider<int>* sl[] = {&sl_red, &sl_green, &sl_blue};
	for (int i = 0; i < 3; ++i) {
		slider<int>* si = sl[i];
		si->set_limits (0, 255);
		si->set_listener (this);
	}
	unsigned char c = 255, b = 0;
	sl_red.set_color (c, b, b);
	sl_green.set_color (b, c, b);
	sl_blue.set_color (b, b, c);

	lf_value.set_label ("Value");
	lf_value.set_listener (this);

	spinner<float>* spn [] = {&sp_1equals, &sp_0equals};
	const char* spnl [] = {"1 = ", "0 = "};
	float spnv [] = {1.0f, 0.1f};
	for (int i = 0; i < 2; ++i) {
		spinner<float>* spi = spn[i];
		spi->set (spnl[i], 0.01f, -MILLION, MILLION, this, 0);
		spi->set_value (spnv[i]);
	}
	
	cb_make_shapeform.set_text ("Make shapeform");

	sp_scale.set ("Scale", 0.01f, 0.0f, MILLION, this, 0);
	sp_scale.set_value (1.0f);

  widget_load ("d_number", ctrls);

	bd_bits.lsnr = this;

  load_params ();

	make_value_from_color ();
	set_type (type);

	cb_make_shapeform.set_listener (this);

}

void number::render () {
	shapeform = cb_make_shapeform.state;
	vector<unsigned char>& bits = bd_bits.bits;
	int num_bits = bd_bits.num_bits;
	points.clear ();
	float scale = sp_scale.f_value;
	float cx = 0.5, cy = 0;
	if (shapeform) {
		extern const float TWO_PI;
		float da = TWO_PI / num_bits, hda = da / 2.0;
		float pa = -hda, a = 0, na = hda;
		float cos_pa = cos(pa), sin_pa = sin(pa);
		float cos_na, sin_na;
		float cos_a, sin_a;
		int pbi = -1, b0 = bits[0], bl = bits[bd_bits.last_bit];
		for (int i = 0; i < num_bits; ++i) {
			int bi = bits[i];
			cos_a = cos(a);
			sin_a = sin(a);
			float R = scale * y[bi];
			float x = cx + R * cos_a;
			float y = cy + R * sin_a;
			cos_na = cos(na);
			sin_na = sin(na);
			float xn = cx + R * cos_na;
			float yn = cy + R * sin_na;
			if (bi != pbi) {
				float xp = cx + R * cos_pa;
				float yp = cy + R * sin_pa;
				points.push_back (point<float>(xp,yp));
			}
			cos_pa = cos_na;
			sin_pa = sin_na;
			points.push_back (point<float>(x, y));
			points.push_back (point<float>(xn, yn));
			a += da;
			na += da;
			pbi = bi;
		}
		if (b0 != bl) {
			point<float>& p0 = *points.begin ();
			points.push_back (p0);
		}
	} else {
		float beat_pos = 0.0f;
		float beat = scale / bd_bits.num_bits;
		float half_beat = beat / 2.0;
		point<float> p0;
		for (int i = 0, j = bd_bits.num_bits; i < j; ++i) {
			int bi = bits[i];
			points.push_back (point<float>(beat_pos, 0.0f));
			point<float> pi (beat_pos + half_beat, y[bi]);
			points.push_back (pi);
			beat_pos += beat;
		}
		points.push_back (point<float>(beat_pos, 0.0f));
	}

	ss.clear(); ss.str(""); ss << "number_" << lf_value.fld.text;
	gen_pts ();

}

void number::flip () {
	vector<unsigned char>& bits = bd_bits.bits;
	int num_bits = bd_bits.num_bits;
	for (int i = 0; i < num_bits; ++i) bits[i]=!bits[i];
	changed (bd_bits);
}

void number::shift (int i, int d) {
	vector<unsigned char>& bits = bd_bits.bits;
	int j = i;
	int n = bd_bits.last_bit;
	unsigned char w = bits[i];
	for (int m = 0; m < n; ++m) {
		int id = i + d;
		bits[i]=bits[id];
		i = id;
	}
	int wb = j + d * n;
	if (cb_wrap.state) bits[wb] = w; else bits[wb] = 0;
	changed (bd_bits);
}

void number::make_value_from_color () {
	ucolor.r = sl_red (); ucolor.g = sl_green (); ucolor.b = sl_blue ();
	if (type == COLOR) {
		ss.clear (); ss.str(""); ss << int(ucolor.r) << spc << int(ucolor.g) << spc << int(ucolor.b);
		lf_value.set_text (ss.str());
		bd_bits.set (&ucolor, 3);
	}
	bd_bits.set_color (ucolor.r, ucolor.g, ucolor.b);
}

void number::set_type (int t) {

	type = t;

	wrap<int> (CHAR, type, COLOR);

	ol_bitsof.set_text (" Bits of " + types[type]);
	
	lf_value.fld.expr = 1;
	if (type == COLOR) {
		make_value_from_color ();
		lf_value.fld.expr = 0;
	} else if (type == CHAR) {
		set_value (uchar);
		lf_value.fld.expr = 0;
	} else if (type == SHORT) {
		set_value (short_);
	} else if (type == INT) {
		set_value (int_);
	} else if (type == FLOAT) {
		set_value (float_);
	} else if (type == DOUBLE) {
		set_value (double_);
	}

	do_render ();

}

extern tcl_interp interpreter;

string eval_expression (const string& e) {
	string cmd ("expr "); cmd += e;
	interpreter (cmd);
	if (interpreter.result_status == TCL_OK) return interpreter.result; else return ("error");
}

void eval_expression (field& f) {
	f.set_text (eval_expression (f.text));
}

void number::reverse (unsigned char* reversed, unsigned char* original, int n) {
	for (int i = 0, j = n - 1; i < n; ++i, --j) reversed[i] = original[j];
}

void number::changed (slider<int>& s) {
	make_value_from_color ();
	do_render ();
}

void number::picked (label& lbl, int dir) {
	int t = type + dir;
	set_type (t);
}

void number::changed (field& f) {
	if (&f == &lf_value.fld) {
		if (type == INT) {
			eval_expression (f);
			int_ = int (f);
			int int__ = int_;
			set_bit_display (&int__);
		} else if (type == FLOAT) {
			eval_expression (f);
			float_ = float (f);
			float float__ = float_;
			set_bit_display (&float__);
		} else if (type == DOUBLE) {
			eval_expression (f);
			double_ = double (f);
			double double__ = double_;
			set_bit_display (&double__);
		} else if (type == CHAR) {
			if (f.text.length () == 0) {
				uchar = 'A'; 
				f.set_text (uchar);
			} else uchar = f.text[0];
			unsigned char uchar_ = uchar;
			set_bit_display (&uchar_);
		} else if (type == SHORT) {
			eval_expression (f);
			short_ = short (f);
			short short__ = short_;
			set_bit_display (&short__);
		} else if (type == COLOR) {
			ss.clear (); ss.str(""); ss << f.text;
			string comp[3];
			for (int i = 0; i < 3; ++i) {
				string& ci = comp[i];
				ss >> ci;
				ci = eval_expression (ci);
			}
			int sv = 0;
			slider<int>* sld[3] = {&sl_red, &sl_green, &sl_blue};
			for (int i = 0; i < 3; ++i) {
				ss.clear (); ss.str(""); ss << comp[i]; 
				ss >> sv; 
				sld[i]->set_val (sv);  // set slider
			}
			make_value_from_color ();
		}
	} else if (&f == &sp_1equals.f_value) {
		y[1] = float (f);
	} else if (&f == &sp_0equals.f_value) {
		y[0] = float (f);
	} 
	do_render ();
}


void number::changed (bit_display& bd) {
	if (type == COLOR) {
		bd.get_color (&ucolor);
		sl_red.set_val (ucolor.r); sl_green.set_val (ucolor.g); sl_blue.set_val (ucolor.b);
		make_value_from_color ();
	} else if (type == CHAR) {
		bd.get_data (&uchar);
    if (uchar > 31 && uchar < 127) set_value_ (uchar);
	} else if (type == SHORT) {
		bd.get_data (&short_);
		set_value_ (short_);
	} else if (type == INT) {
		bd.get_data (&int_);
		set_value_ (int_);
	} else if (type == FLOAT) {
		bd.get_data (&float_);
		set_value_ (float_);
	} else if (type == DOUBLE) {
		bd.get_data (&double_);
		set_value_ (double_);
	}
	do_render ();
}

void number::changed (checkbutton& cb) {
	if (&cb == &cb_make_shapeform) do_render ();
	else return plugin::changed (cb);
}

void number::clicked (button& b) {
	if (&b == &b_flip) {
		flip ();
	} else if (&b == &b_left_shift) {
		shift (0, +1);
	} else if (&b == &b_right_shift) {
		shift (bd_bits.last_bit, -1);
	} else plugin::clicked (b);
}
