/*
* morse_code.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 "morse_code.h"
#include "tokenizer.h"
#include "command.h"
#include "ui_list.h"
#include <algorithm>

using namespace std;

#define result cmdlst.result

extern cmdlist cmdlst;
extern ui_list uis;
extern string user_data_dir;


morse_code::morse_code (const string& ln, const string& sn) : command (ln, sn), dot ("dot.crv"), dash ("dash.crv"), inner_space ("inner.crv"), letter_space ("letter.crv"), word_space ("word.crv"), ed ("morse-code.ed") {

  load ("i8n_morse_code");

  ed.add (&dot, &lis);
  ed.add (&dash, &lis);
  ed.add (&inner_space, &lis);
  ed.add (&letter_space, &lis);
  ed.add (&word_space, &lis);

  add_first_vertex = 1; // of pattern for first letter of sentence. otherwise continue pattern from last added vertex

}

morse_code::~morse_code() {
  dot.save ("dot.crv");
  dash.save ("dash.crv");
  inner_space.save ("inner_space.crv");
  letter_space.save ("letter_space.crv");
  word_space.save ("word_space.crv");
}

int morse_code::load (const string& fname) {

  ifstream file ((user_data_dir + fname).c_str(), ios::in);
  ncodes = 0;
  if (file) {
    char c;
    string dnd;
    string ignore;
    file >> ignore >> ncodes;
    code.clear ();
    for (int i = 0; i < ncodes; ++i) {
      file >> c >> dnd;
      code[c] = dnd;
    }
    return 1;
  } else return 0;

}

int morse_code::operator() (tokenizer& tz) { // called when user executes morse-code <text> [nbeats]

    float nbeats;
    string text; tz >> text >> nbeats;
    if (nbeats <= 0) nbeats = 1;

    std::transform (text.begin(), text.end(), text.begin(), (int(*)(int)) toupper); // morse is upper case
    return create_pattern (text, nbeats);
}

int morse_code::create_pattern (const string& text, float tox) {

  multi_curve& morse = curve_editor::copy;
  morse.clear ();
  char prev_let = 0;

	string outs;
  for (int i = 0, j = text.length(); i < j; ++i) {
    char cur_let = text[i];
    if (cur_let == ' ') {
      append (morse, word_space); // word complete
      prev_let = 0;
			outs += spc;
    } else {
      if (prev_let != 0) {
				append (morse, letter_space); // letter complete
				outs += spc;
			}
      prev_let = cur_let;
      string dnd (code[cur_let]);
			outs += dnd;
      if (dnd != "") {
        char prev_in = 0, cur_in = 0;
        for (int m = 0, n = dnd.length(); m < n; ++m) {
            cur_in = dnd[m];
            if (prev_in != 0) append (morse, inner_space); 
						prev_in = cur_in;
            if (cur_in == '.') append(morse, dot); else append(morse, dash);
        }
      }
    }
  }

  scale_tox (morse, tox);

  morse.evaluate ();
  result = "morse code for " + text + " (" + outs + ") on copy curve. go paste!";

  add_first_vertex = 1; // prep for next time

  return 1;

}

void morse_code::append (multi_curve& m, multi_curve& p) {

  int n = p.num_vertices;
  if (!n) return;

  point<float> v0 (p.vertices[0]); // first vertex of morse code pattern (dot, dash, letter, word or inner spacings)

  int start = 1;
  if (add_first_vertex) { // only when we start conversion of text -> morse code.
    org = v0;
    start = 0;
    add_first_vertex = 0;
  } else { // continue pattern from last added vertex in conversion
    org = vi;
    point<float> rt0 (p.right_tangents[0]);
    m.set_right_tangent (m.last_vertex, vi.x + (rt0.x - v0.x), vi.y + (rt0.y - v0.y));
  }

  // position the vertices & tangents of the pattern
  for (int i = start; i < n; ++i) {
    vi = p.vertices[i];
    point<float>& lt = p.left_tangents[i];
    point<float>& rt = p.right_tangents[i];
    point<float> ltv (lt.x - vi.x, lt.y - vi.y);
    point<float> rtv (rt.x - vi.x, rt.y - vi.y);
    vi.x = org.x + vi.x - v0.x;
    vi.y = org.y + vi.y - v0.y;
    m.add_vertex (vi.x, vi.y);
    m.add_left_tangent (vi.x + ltv.x, vi.y + ltv.y);
    m.add_right_tangent (vi.x + rtv.x, vi.y + rtv.y);
  }

}

void morse_code::scale_tox (multi_curve& m, float tox) {

  // assumes x is increasing & first vertex x = 0
  //
  // suitable when input curve is from waveform, gater, FM/AM and octave shift
  //

  int n = m.num_vertices;

  if (!n) return;

  int last = n - 1;
  point<float> vlast (m.vertices[last]);

  float factor = 1.0f;
  if (vlast.x != 0) ; else factor = tox / vlast.x;

  for (int i = 0; i < n; ++i) {

    vi = m.vertices[i];
    point<float>& lt = m.left_tangents[i];
    point<float>& rt = m.right_tangents[i];
    m.set_vertex (i, vi.x * factor, vi.y);
    m.set_left_tangent (i, lt.x * factor, lt.y);
    m.set_right_tangent (i, rt.x * factor, rt.y);

  }

}
