
/******************************************************************************
* MODULE     : edit_aux.gen.cc
* DESCRIPTION: incorporate automatically generated data into text
* COPYRIGHT  : (C) 1999  Joris van der Hoeven
*******************************************************************************
* This software falls under the GNU general public license and comes WITHOUT
* ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
* If you don't have this file, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************/

#include <Process/edit_process.gen.h>
#include <analyze.gen.h>
#include <file.gen.h>
#include <convert.gen.h>
#include <tm_buffer.gen.h>

#module code_edit_aux
#import edit_process
#import analyze
#import file
#import convert
#import tm_buffer
#import array (string)
#import hashmap (string, tree)
#import hashmap (tree, tree)

void merge_sort (array<string>& a);

/******************************************************************************
* Constructors and destructors
******************************************************************************/

edit_process_rep::edit_process_rep ():
  math_input (FALSE), message_l (""), message_r ("") {}
edit_process_rep::~edit_process_rep () {}

/******************************************************************************
* Automatically generate a bibliography
******************************************************************************/

void
edit_process_rep::generate_bibliography (
  string bib, string style, string fname)
{
  if (debug (0))
    cout << "TeXmacs] Generating bibliography"
	 << " [" << bib << ", " << style << ", " << fname << "]\n";

  int i;
  string bib_s= "\\bibstyle{" * style * "}\n";
  tree   bib_t= buf->aux[bib];
  for (i=0; i<arity(bib_t); i++)
    bib_s << "\\citation{" << as_string (bib_t[i]) << "}\n";
  string rel_name= get_relative_file_name (buf->name, fname, FALSE);
  if ((N(rel_name) >= 4) && (rel_name (N(rel_name)-4, N(rel_name)) == ".bib"))
    rel_name= rel_name (0, N(rel_name)- 4);
  bib_s << "\\bibdata{" << rel_name << "}\n";
  save_string ("$TEXMACS_HOME_PATH/system/temp.aux", bib_s);
  system ("cd $TEXMACS_HOME_PATH/system; bibtex temp");
  string result;
  if (load_string ("$TEXMACS_HOME_PATH/system/temp.bbl", result)) {
    set_message ("Error: bibtex failed to create bibliography",
	     "compile bibliography");
    return;
  }

  int count=1;
  tree t= latex_to_tree (result, "text");
  if (is_document (t) && is_expand (t[0])) t= t[0];
  if (is_document (t) && (N(t)>1) && is_expand (t[1])) t= t[1];
  if (arity(t) == 0) return;
  if ((!is_expand (t, "thebibliography", 2)) ||
      (!is_document (t[N(t)-1])))
    return;
  t= t[N(t)-1];
  tree u (DOCUMENT);
  for (i=0; i<arity(t); i++) {
    if (is_concat (t[i]) &&
	is_func (t[i][0], APPLY) &&
	((t[i][0][0] == "bibitem") || (t[i][0][0] == "bibitem*")))
      {
	tree item= t[i][0];
	if (item[0] == "bibitem")
	  item= tree (APPLY, "bibitem*", as_string (count++), item[1]);
	t[i][0]= item;
	tree v (CONCAT, tree (APPLY, "bibitem*", item[1]));
	if (is_atomic (item[2])) v << tree (LABEL, "bib-" * item[2]->label);
	v << A (t[i] (1, N(t[i])));
	u << v;
      }
  }
  if (N(u)>0) insert_tree (u);
}

/******************************************************************************
* Automatically generate table of contents
******************************************************************************/

void
edit_process_rep::generate_table_of_contents (string toc) {
  if (debug (0))
    cout << "TeXmacs] Generating table of contents [" << toc << "]\n";
  tree toc_t= buf->aux[toc];
  if (N(toc_t)>0) insert_tree (toc_t);
}

/******************************************************************************
* Automatically generate an index
******************************************************************************/

static hashmap<string,tree> followup (TUPLE);

static string
index_name_sub (tree t, bool all) {
  if (is_atomic (t)) {
    string s= t->label, r;
    int i, n= N(s);
    for (i=0; i<n; i++)
      if (is_iso_alpha (s[i]) || is_digit (s[i]) || (s[i] == ' ') ||
	  (all && (s[i] >= ' '))) r << s[i];
    return r;
  }
  else if (is_concat (t)) {
    string r;
    int i, n= N(t);
    for (i=0; i<n; i++)
      r << index_name_sub (t[i], all);
    return r;
  }
  else if (is_tuple (t)) {
    string r;
    int i, j, n= N(t);
    for (i=0; i<n; i++) {
      if (i!=0) r << "\t";
      string s= index_name_sub (t[i], FALSE);
      if (s == "") s= index_name_sub (t[i], TRUE);
      tree u= copy (followup [s]);
      for (j=0; j<N(u); j++)
	if (u[j] == t[i]) break;
      if (j == N(u)) { u << t[i]; followup (s)= u; }
      r << s;
      if (j != 0) r << "\n" << as_string (j);
    }
    return r;
  }
  else if (all && is_func (t, WITH))
    return index_name_sub (t[N(t)-1], all);
  else return "";
}

static string
index_name (tree t) {
  if (is_func (t, TUPLE, 2)) t= t[0];
  else if (is_func (t, TUPLE, 3)) t= t[0];
  else if (is_func (t, TUPLE, 5)) {
    if (t[0] == "") t= t[3];
    else t= t[0];
  }
  if (!is_tuple (t)) t= tuple (t);
  return index_name_sub (t, FALSE);
}

static tree
index_value (tree t) {
  if (is_func (t, TUPLE, 2)) return t;
  else if (is_func (t, TUPLE, 3)) return tuple (t[2]);
  else if (is_func (t, TUPLE, 5)) {
    tree l= t[3], r= t[4];
    if (!is_tuple (l)) l= tuple (l);
    if (t[1] == "strong") r= tree (EXPAND, "strong", r);
    if (t[2] != "") r= tuple ("range", t[2], r);
    return tuple (l, r);
  }
  return "";
}

static void
insert_recursively (array<string>& a, string s, hashmap<string,tree>& h) {
  // cout << "Insert recursively \t" << s << "\n";
  int i= search_backwards ("\t", s);
  if (i != -1) {
    string r= s (0, i);
    if (!h->contains (r)) {
      tree u= h[s][0][0];
      h (r)= tuple (copy (u (0, N(u)-1)));
      insert_recursively (a, s (0, i), h);
    }
  }
  a << s;
}

static void
make_entry (tree& D, tree t) {
  // cout << "Make entry " << t << "\n";
  int i, j, n= N(t);
  for (i=0; i<n; i++)
    if (is_func (t[i], TUPLE, 1)) {
      bool flag= TRUE;
      for (j=0; j<n; j++)
	if (is_func (t[j], TUPLE, 2) && (t[i][0] == t[j][0]))
	  flag= FALSE;
      if (flag) D << t[i][0];
    }

  for (i=0; i<n; i++)
    if (is_func (t[i], TUPLE, 2) && is_tuple (t[i][1], "range", 2)) {
      bool flag= TRUE;
      for (j=i+1; j<n; j++)
	if (is_func (t[j], TUPLE, 2) && is_tuple (t[j][1], "range", 2))
	  if ((t[i][0] == t[j][0]) && (t[i][1][1] == t[j][1][1])) {
	    t[i][1]= tree (CONCAT, t[i][1][2], "--", t[j][1][2]);
	    t[j]= "";
	    flag= FALSE;
	    break;
	  }
      if (flag) t[i][1]= tree (CONCAT, t[i][1][2], "--?");
    }

  hashmap<tree,tree> h ("");
  for (i=0; i<n; i++)
    if (is_func (t[i], TUPLE, 2)) {
      tree l= t[i][0], r= t[i][1];
      if (!h->contains (l)) h (l)= r;
      else {
	tree rr= h[l];
	if (!is_concat (rr)) rr= tree (CONCAT, rr);
	rr << ", " << r;
	h (l)= rr;
      }
    }

  for (i=0; i<n; i++)
    if (is_func (t[i], TUPLE, 2)) {
      tree l= t[i][0];
      if (h->contains (l)) {
	int k= N(l);
	tree e (EXPAND, "index-" * as_string (k), copy (l[k-1]), h[l]);
	D << e;
	h->reset (l);
      }
    }
}

void
edit_process_rep::generate_index (string idx) {
  if (debug (0))
    cout << "TeXmacs] Generating index [" << idx << "]\n";
  tree I= copy (buf->aux[idx]);
  if (N(I)>0) {
    followup= hashmap<string,tree> (TUPLE);
    int i, n= N(I);
    array<string> entry (n);
    for (i=0; i<n; i++)
      entry[i]= index_name (I[i]);
    merge_sort (entry);

    hashmap<string,tree> h (TUPLE);
    for (i=0; i<n; i++) {
      string name = index_name  (I[i]);
      tree   value= index_value (I[i]);
      if (!h->contains (name)) h (name)= tuple (value);
      else h (name) << value;
    }

    array<string> new_entry;
    for (i=0; i<n; i++) {
      if ((i>0) && (entry[i] == entry[i-1])) continue;
      insert_recursively (new_entry, entry[i], h);
    }
    entry= new_entry;
    n= N(entry);

    tree D (DOCUMENT);
    for (i=0; i<n; i++)
      make_entry (D, h (entry[i]));
    insert_tree (D);
  }
}

/******************************************************************************
* Automatically generate a glossary
******************************************************************************/

void
edit_process_rep::generate_glossary (string gly) {
  if (debug (0))
    cout << "TeXmacs] Generating glossary [" << gly << "]\n";
  tree G= copy (buf->aux[gly]);
  if (N(G)>0) {
    int i, n= N(G);
    tree D (DOCUMENT);
    for (i=0; i<n; i++)
      if (is_func (G[i], TUPLE, 1)) D << G[i][0];
      else if (is_func (G[i], TUPLE, 3) && (G[i][0] == "normal")) {
	tree L (EXPAND, "glossary-1", G[i][1], G[i][2]);
	D << L;
      }
      else if (is_func (G[i], TUPLE, 4) && (G[i][0] == "normal")) {
	tree L (EXPAND, "glossary-2", G[i][1], G[i][2], G[i][3]);
	D << L;
      }
      else if (is_func (G[i], TUPLE, 3) && (G[i][0] == "dup")) {
	int j;
	for (j=0; j<N(D); j++)
	  if (is_expand (D[j]) && (D[j][1] == G[i][1]) &&
	      ((D[j][0] == "glossary-1") || (D[j][0] == "glossary-2")))
	    {
	      tree C= D[j][N(D[j])-1];
	      if (!is_concat (C)) C= tree (CONCAT, C);
	      C << ", ";
	      C << G[i][2];
	      D[j][N(D[j])-1]= C;
	    }
      }
    insert_tree (D);
  }
}

/******************************************************************************
* Automatically generate auxiliairy data and replace in text
******************************************************************************/

static bool
is_aux (tree t) {
  return
    is_expand (t, "bibliography", 4) ||
    is_expand (t, "table-of-contents", 2) ||
    is_expand (t, "the-index", 2) ||
    is_expand (t, "the-glossary", 2);
}

void
edit_process_rep::generate_aux_recursively (string which, tree st, path p) {
  int i, n= N(st);
  for (i=0; i<n; i++)
    if (!is_aux (st[i])) {
      if (is_compound (st[i]))
	generate_aux_recursively (which, st[i], p * i);
    }
    else {
      tree t= st[i];
      path doc_p= p * path (i, N(t)-1);
      assign (doc_p, tree (DOCUMENT, ""));
      go_to (doc_p * path (0, 0));

      /*
	cout << "et= " << et << "\n";
	cout << "tp= " << tp << "\n";
	cout << "------------------------------------------------------\n";
      */
      if ((arity(t)>=4) && (t[0] == "bibliography") &&
	  ((which == "") || (t[0] == which)))
	generate_bibliography (as_string (t[1]), as_string (t[2]),
			       as_string (t[3]));
      if ((arity(t)>=2) && (t[0] == "table-of-contents") &&
	  ((which == "") || (t[0] == which)))
	generate_table_of_contents (as_string (t[1]));
      if ((arity(t)>=2) && (t[0] == "the-index") &&
	  ((which == "") || (t[0] == which)))
	generate_index (as_string (t[1]));
      if ((arity(t)>=2) && (t[0] == "the-glossary") &&
	  ((which == "") || (t[0] == which)))
	generate_glossary (as_string (t[1]));
      /*
	cout << "et= " << et << "\n";
	cout << "tp= " << tp << "\n";
	cout << "------------------------------------------------------\n\n\n";
      */
    }
}

void
edit_process_rep::generate_aux (string which) {
  // path saved_path= tp;
  generate_aux_recursively (which, et, path());
  // if (which == "") go_to (saved_path);
  // ... may be problematic if cursor was inside regenerated content
}

#endmodule // code_edit_aux
