/*
* recorder.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 "main.h"
#include "recorder.h"
#include "wav.h"
#include "audio.h"
#include "checkbutton.h"
#include "chrono.h"
#include "label.h"
#include "console.h"
#include "ui_list.h"
#include "menu.h"

#ifdef __WINDOWS_DS__
  #include <windows.h>
  #include <direct.h>
  #include <shlobj.h>
  #include <tchar.h>
#endif

#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;

extern ofstream dlog;
extern char BUFFER [];

recorder::recorder () : fname ("din.wav") {
  buffer_size = 0;
  saved = 1;
  samples_buffer = 0;
  stop_rec = stop_mins = stop_secs = 0;
}

void recorder::add (float* sample_buffer, int sample_buffer_size, int num_samples, checkbutton& cb_record1, checkbutton& cb_record2) {
	if (saving_started) return;
  float* recbuf = new float [num_samples];
	if (recbuf == 0) {
		cons << console::red << "Recorder ran out of memory!" << eol;
		return;
	}
  memcpy (recbuf, sample_buffer, sample_buffer_size);
  record_buffers.push_back (recbuf);
  buffer_size += sample_buffer_size;
	rec_time_add += clk.delta_secs;
	sprintf (BUFFER, "Recorded %02d:%02d", rec_time_add.mins, rec_time_add.secs);
	cb_record1.set_text (BUFFER);
	cb_record2.set_text (BUFFER);
  if (stop_rec) {
    if (rec_time_add.mins >= stop_mins && rec_time_add.secs >= stop_secs) stop ();
  }
  saved = 0;
}

void recorder::start_saving () {

	if (saving_started || (buffer_size == 0)) return;

#ifdef __WINDOWS_DS__
	TCHAR desktop[MAX_PATH]={0};
  if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY | CSIDL_FLAG_CREATE, NULL, 0, desktop)));
  std::basic_ostringstream<TCHAR> file_path;
  file_path << desktop << _TEXT("\\") << fname.c_str();
  file.open(file_path.str().c_str(), ios::binary);
  cons << file_path.str().c_str() << eol;
#else
  string home = getenv ("HOME");
	string desktop ("/Desktop/");
	folder = home + desktop;
  string full_fname (folder + fname);
  file.open (full_fname.c_str(), ios::binary);
#endif
  write_wav_header<float> (file, SAMPLE_RATE, aout.num_channels, buffer_size);
  iter = record_buffers.begin ();
  jter = record_buffers.end ();
  samples_buffer = new float [aout.samples_per_buffer]; 
	saving_started = 1;

}

int recorder::save_some (checkbutton& cb) {
  if (iter != jter) {
    float* recbuf = *iter;
    float* L = recbuf, *R = recbuf + aout.samples_per_channel; // separate L and R channels
    for (int i = 0, j = aout.samples_per_channel, k = 0; i < j; ++i) { // interleave L and R channels
      samples_buffer[k++] = L[i];
      samples_buffer[k++] = R[i];
    }
    file.write ((const char*) samples_buffer, aout.samples_buffer_size); // write to disk!
    ++iter;
		rec_time_save += clk.delta_secs;
    sprintf (BUFFER, "Saved %02d:%02d of %02d:%02d", rec_time_save.mins, rec_time_save.secs, rec_time_add.mins, rec_time_add.secs);
		cb.set_text (BUFFER);
    return 1;
  } else {
    delete [] samples_buffer;
		file.close ();
    samples_buffer = 0;
    saved = 1;
		saving_started = 0;
		rec_time_save.reset ();
    return 0;
  }
}

void recorder::clear () {
	if (buffer_size && !saving_started) {
		for (list<float*>::iterator citer = record_buffers.begin (), cjter = record_buffers.end (); citer != cjter; ++citer) delete[] *citer;
		record_buffers.clear ();
		buffer_size = 0;
		rec_time_add.reset ();
    saved = 1;
	}
  if (samples_buffer) delete[] samples_buffer;
}

recorder::~recorder () {
  clear ();
  dlog << "-- destroyed recorder --" << endl;
}

void recorder::set_fname (const string& _fname) {
  fname = _fname;
}

void recorder::set_stop_at (int m, int s) {
  stop_rec = 1;
  stop_mins = m;
  stop_secs = s;
}

void recorder::start () {
  uis.cb_record.turn_on ();
  MENU.cb_record.turn_on ();
}

void recorder::stop () {
  uis.cb_record.turn_off ();
  MENU.cb_record.turn_off ();
  cons ("stopped-recording");
}

void recorder::normalise () {
  float lmin, lmax;
  float rmin, rmax;
  lmin = rmin = lmax = rmax = 0;
  for (list<float*>::iterator start = record_buffers.begin (), end = record_buffers.end (); start != end; ++start) {
    float* recbuf = *start;
    float* L = recbuf, *R = recbuf + aout.samples_per_channel;
    for (int i = 0, j = aout.samples_per_channel; i < j; ++i) {
      float li = L[i], ri = R[i];
      if (li > lmax) lmax = li; else if (li < lmin) lmin = li;
      if (ri > rmax) rmax = ri; else if (ri < rmin) rmin = ri;
    }
  }
  float ldco = -(lmax + lmin)/2, rdco = -(rmin+rmax)/2;
  cons << "lmin/max: " << lmin <<'/'<<lmax << " rmin/rmax: " << rmin <<'/'<<rmax<<eol;
  cons <<"ldco="<<ldco<<",rdco="<<rdco<<eol;
  lmin += ldco; rmin += rdco;
  lmax += ldco; rmax += rdco;
  float dl = lmax - lmin, dr = rmax - rmin;
  float nmin = -1, nmax = 1, dn = nmax - nmin, dn_dl = dn / dl, dn_dr = dn / dr;
  for (list<float*>::iterator start = record_buffers.begin (), end = record_buffers.end (); start != end; ++start) {
    float* recbuf = *start;
    float* L = recbuf, *R = recbuf + aout.samples_per_channel;
    for (int i = 0, j = aout.samples_per_channel; i < j; ++i) {
      float li = L[i]+ldco, ri = R[i]+rdco;
      float lo = nmin + (li - lmin) * dn_dl, ro = nmin + (ri - rmin) * dn_dr;
      L[i] = lo;
      R[i] = ro;
    }
  }
}

