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

#include "dingl.h"
#include "oscilloscope.h"
#include "font.h"
#include "viewwin.h"
#include "input.h"
#include "log.h"
#include "console.h"
#include <string>
#include <algorithm>
using namespace std;

extern string user_data_dir;
extern int FPS;
extern int CURRENT_INSTRUMENT;
extern const int NUM_INSTRUMENTS;
extern int lmb;
extern int mousex, mouseyy;
extern viewport view;
extern int line_height;

oscilloscope::oscilloscope (const string& _settingsf) : win (0, 0, 2 * MAX_SAMPLES, 250) {
  vertex_array = new int [8 * MAX_SAMPLES];
  color_array = new float [16 * MAX_SAMPLES];
  viewr = 0;
  addr = 1;
	settingsf = _settingsf;
  label_counter = MILLION;
  fld.set_listener (this);
  load ();
	px = py = -1;
	lmb_clicked = move = stop_move = 0;
	sample_t::lmin = sample_t::lmax = sample_t::rmin = sample_t::rmax = 0; // ok for oscilloscope.
  sprintf (lbuf1, "min %2.2f, max %2.2f", sample_t::lmin, sample_t::lmax);
  sprintf (rbuf1, "min %2.2f, max %2.2f", sample_t::rmin, sample_t::rmax);
	lr = lg = lb = 0.0f;
	rr = rg = rb = 0.0f;
}

int oscilloscope::load () {
  ifstream file ((user_data_dir + settingsf).c_str(), ios::in);
  if (!file) return 0;
  string ignore;
  for (int i = 0; i < NUM_INSTRUMENTS; ++i) {
    file >> ignore >> left;
    file >> ignore >> base;
    file >> ignore >> height; 
    file >> ignore >> num_samples;
    file >> ignore >> visible;
    file >> ignore >> folded;
    params.push_back (oscilloscope_params_t (left, base, height, num_samples, visible, folded));
  }
  return 1;
}

int oscilloscope::save () {
  ofstream file ((user_data_dir + settingsf).c_str(), ios::out);
  if (!file) return false;
  for (int i = 0; i < NUM_INSTRUMENTS; ++i) {
    oscilloscope_params_t& op = params[i];
    file << "left " << op.left << endl;
    file << "base " << op.base << endl;
    file << "height " << op.height << endl;
    file << "num_samples " << op.num_samples << endl;
    file << "visible " << op.visible << endl;
    file << "folded " << op.folded << endl;
  }
  return 1;
}

void oscilloscope::load_current_instrument () {
  oscilloscope_params_t& op = params[CURRENT_INSTRUMENT];
  left = op.left;
  base = op.base;
  height = op.height;
  num_samples = op.num_samples;
  visible = op.visible;
  folded = op.folded;
  win (left, base - height, left + 2 * num_samples, base + height);
  set_folded (folded);
  set_num_samples (num_samples); // calls calc_draw_params
  // if (visible) show (); else hide ();
}

void oscilloscope::save_current_instrument () {
  oscilloscope_params_t& op = params[CURRENT_INSTRUMENT];
  op.left = win.left;
  op.base = base;
  op.height = height;
  op.num_samples = num_samples;
  op.visible = visible;
  op.folded = folded;
}

oscilloscope::~oscilloscope () {
  save ();
  delete[] vertex_array;
  delete[] color_array;
}

void oscilloscope::calc_draw_params () {
  base = win.midy;
  leftx = win.left;
  rightx = leftx + num_samples;
  endx = rightx + num_samples - 1;
  ndraw = 4 * num_samples;
  win (leftx, base - height, endx, base + height);
  int dheight = 5;
  pick_win (win.left, base - dheight, win.right, base + dheight);
  lh = line_height;
  lly = base - lh;
  lry = lly;
  fld.set_pos (leftx - 1.5 * fld.size, base - fld.size);
}

void oscilloscope::set_height (int h) {
  height = h;
  calc_draw_params ();
}

void oscilloscope::set_num_samples (int n) {
  num_samples = min (n, MAX_SAMPLES);
  viewr = 0;
  addr = 1;
  calc_draw_params ();
}

int oscilloscope::handle_input () {

  int result = fld.handle_input ();
  if (result) return result;

	int x = mousex;
	int y = mouseyy;

  if (is_lmb (this)) {
    if (lmb_clicked == 0) {
			lmb_clicked = 1;
			if (move) {
				move = 0;
				stop_move = 1;
			} else if (inbox (pick_win, x, y)) { // pick_win is small window along zero line of oscilloscope
        move = 1;
				is_lmb.tie = this;
      }
		} 
  } else {
		if (move) {
			int dx = x - px, dy = y - py;
			win.move (dx, dy);
			calc_draw_params ();
		} else if (stop_move) {
			stop_move = 0;
			is_lmb.clear (this);
		}
		lmb_clicked = 0;
  }

  px = x;
  py = y;
	result = move;
  return result;
}

void oscilloscope::add_samples (float* outl, float* outr, int n) {
  for (int i = 0; i < n; ++i) {
    sample_t& sa = samples[addr];
    float l = outl[i], r = outr[i];
    sa.left = l; sa.right = r;
    sample_t::lmin = min (sa.left, sample_t::lmin);
    sample_t::lmax = max (sa.left, sample_t::lmax);
    sample_t::rmin = min (sa.right, sample_t::rmin);
    sample_t::rmax = max (sa.right, sample_t::rmax);
		if (sample_t::lmin < -1 || sample_t::rmin < -1 || sample_t::lmax > 1 || sample_t::rmax > 1) limit = 1; else limit = 0;
    if (++addr >= num_samples) addr = 0;
    if (addr == viewr) {if (++viewr >= num_samples) viewr = 0;}
  }
}

void oscilloscope::draw () {

  // calc left & right labels
  if (++label_counter >= FPS) { // update labels once a second
		char lb = '.', rb = '.';
		float lsum = sample_t::lmin * sample_t::lmin + sample_t::lmax * sample_t::lmax;
		float rsum = sample_t::rmin * sample_t::rmin + sample_t::rmax * sample_t::rmax;
		if (lsum > rsum) lb = '@';
		else if (rsum > lsum) rb = '@';
		sprintf (lbuf1, "min %2.2f, max %2.2f %c", sample_t::lmin, sample_t::lmax, lb);
		sprintf (rbuf1, "min %2.2f, max %2.2f %c", sample_t::rmin, sample_t::rmax, rb);
		sample_t::lmin = sample_t::lmax = sample_t::rmin = sample_t::rmax = 0; // ok for oscilloscope.
		label_counter = 0;
  }

  if (folded == 0) {

    // draw left & right channels
    int x1 = leftx, x2 = rightx, y0 = base;
    for (int i = 0, j = viewr, vi = 0, ci = 0; i < num_samples; ++i, vi += 8, ci += 16) {

      float l = samples[j].left;
			float r = samples[j].right;

      int ly = (int)(l * win.height + 0.5);
			int ry = (int)(r * win.height + 0.5);

			// distortion is red
			//
      float lrr = lr, lgg = lg, lbb = lb; 
			if (l > 1 || l < -1) {lrr = 1; lgg = 0; lbb = 0;}
      float rrr = rr, rgg = rg, rbb = rb; 
			if (r > 1 || r < -1) {rrr = 1; rgg = 0; rbb = 0;}

      vertex_array [vi] = x1;
      vertex_array [vi+1] = y0;
      vertex_array [vi+2] = x1;
      vertex_array [vi+3] = y0 + ly;
      vertex_array [vi+4] = x2;
      vertex_array [vi+5] = y0;
      vertex_array [vi+6] = x2;
      vertex_array [vi+7] = y0 + ry;

      color_array [ci] = color_array[ci+4] = lrr;
      color_array [ci+1] = color_array[ci+5] = lgg;
      color_array [ci+2] = color_array[ci+6] = lbb;
      color_array [ci+3] = color_array[ci+7] = 1;
      color_array [ci+8] = color_array[ci+12] = rrr;
      color_array [ci+9] = color_array[ci+13] = rgg;
      color_array [ci+10] = color_array[ci+14] = rbb;
      color_array [ci+11] = color_array[ci+15] = 1;

      if (++j >= num_samples) j = 0;
      ++x1; ++x2;
    }
		glEnableClientState (GL_COLOR_ARRAY);
		glColorPointer (4, GL_FLOAT, 0, color_array);
		glVertexPointer (2, GL_INT, 0, vertex_array);
    glDrawArrays (GL_LINES, 0, ndraw);
    glDisableClientState (GL_COLOR_ARRAY);
  }

  glColor3f (lr , lg, lb);
  draw_string (lbuf1, leftx, lly, 0);
	vertex_array[0]=leftx;vertex_array[1]=win.midy;
	vertex_array[2]=rightx;vertex_array[3]=win.midy;
	glVertexPointer (2, GL_INT, 0, vertex_array);
	glDrawArrays (GL_LINES, 0, 2);

	glColor3f (rr, rg, rb);
	draw_string (rbuf1, rightx, lry, 0);
  vertex_array[0]=rightx;vertex_array[1]=win.midy;
  vertex_array[2]=endx;vertex_array[3]=win.midy;
	glVertexPointer (2, GL_INT, 0, vertex_array);
  glDrawArrays (GL_LINES, 0, 2);

  fld.draw ();

}

void oscilloscope::set_folded (int f) {
  folded = f;
  if (folded) {
    fld.set_dir (arrow_button::right); 
  } else {
    fld.set_dir (arrow_button::up); 
  }
}

void oscilloscope::clicked (button& b) {
  set_folded (!folded);
}

oscilloscope_params_t::oscilloscope_params_t (int l, int b, int h, int ns, int v, int f) {
  left = l;
  base = b;
  height = h;
  num_samples = ns;
  visible = v;
  folded = f;
}



