
/******************************************************************************
* MODULE     : bridge_compound.cpp
* DESCRIPTION: Bridge between logical and physical long macro expansions
* 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 "bridge.hpp"

tree insert_at (tree, path, tree);
tree remove_at (tree, path, int);

class bridge_compound_rep: public bridge_rep {
protected:
  bool   valid;
  bridge body;
  int    delta;
  tree   fun;

public:
  bridge_compound_rep (typesetter ttt, tree st, path ip);
  void initialize (tree body_t, int delta, tree fun);

  void notify_assign (path p, tree u);
  void notify_insert (path p, tree u);
  void notify_remove (path p, int nr);
  bool notify_macro  (int type, string var, int level, path p, tree u);
  void notify_change ();

  bool my_typeset_will_be_complete ();
  void my_typeset (int desired_status);
};

bridge_compound_rep::bridge_compound_rep (typesetter ttt, tree st, path ip):
  bridge_rep (ttt, st, ip)
{
  valid= false;
}

void
bridge_compound_rep::initialize (tree body_t, int delta2, tree fun2) {
  if ((!valid) || (body->st != body_t) || (delta != delta2) || (fun != fun2)) {
    valid= true;
    if (nil (body)) body= make_bridge (ttt, attach_right (body_t, ip));
    else replace_bridge (body, attach_right (body_t, ip));
    delta= delta2;
    fun  = fun2;
  }
}

bridge
bridge_compound (typesetter ttt, tree st, path ip) {
  return new bridge_compound_rep (ttt, st, ip);
}

/******************************************************************************
* Event notification
******************************************************************************/

void
bridge_compound_rep::notify_assign (path p, tree u) {
  // cout << "Assign " << p << ", " << u << " in " << st << "\n";
  if (nil (p) && (L(u) < START_EXTENSIONS))
    fatal_error ("Nil path", "bridge_compound_rep::notify_assign");
  if (nil (p) || (p->item == 0) || nil (body)) {
    st= substitute (st, p, u);
    valid= false;
  }
  else {
    // bool mp_flag= is_multi_paragraph (st);
    if (is_func (fun, XMACRO, 2))
      notify_macro (MACRO_ASSIGN, fun[0]->label, -1, p, u);
    else if (is_applicable (fun) && (p->item < N(fun)))
      notify_macro (MACRO_ASSIGN, fun[p->item-delta]->label, -1, p->next, u);
    st= substitute (st, p, u);
    // if (mp_flag != is_multi_paragraph (st)) valid= false;
  }
  status= CORRUPTED;
}

void
bridge_compound_rep::notify_insert (path p, tree u) {
  // cout << "Insert " << p << ", " << u << " in " << st << "\n";
  if (nil (p)) fatal_error ("Nil path", "bridge_compound_rep::notify_insert");
  if (atom (p) || nil (body)) bridge_rep::notify_insert (p, u);
  else {
    // bool mp_flag= is_multi_paragraph (st);
    if (is_func (fun, XMACRO, 2))
      notify_macro (MACRO_INSERT, fun[0]->label, -1, p, u);
    else if (is_applicable (fun) && (p->item < N(fun)))
      notify_macro (MACRO_INSERT, fun[p->item-delta]->label, -1, p->next, u);
    st= insert_at (st, p, u);
    // if (mp_flag != is_multi_paragraph (st)) valid= false;
  }
  status= CORRUPTED;
}

void
bridge_compound_rep::notify_remove (path p, int nr) {
  // cout << "Remove " << p << ", " << nr << " in " << st << "\n";
  if (nil (p)) fatal_error ("Nil path", "bridge_compound_rep::notify_remove");
  if (atom (p) || nil (body)) bridge_rep::notify_remove (p, nr);
  else {
    // bool mp_flag= is_multi_paragraph (st);
    if (is_func (fun, XMACRO, 2))
      notify_macro (MACRO_REMOVE, fun[0]->label, -1, p, tree (as_string (nr)));
    else if (is_applicable (fun) && (p->item < N(fun)))
      notify_macro (MACRO_REMOVE, fun[p->item-delta]->label, -1, p->next,
		    tree (as_string (nr)));
    st= remove_at (st, p, nr);
    // if (mp_flag != is_multi_paragraph (st)) valid= false;
  }
  status= CORRUPTED;
}

bool
bridge_compound_rep::notify_macro (
  int type, string var, int l, path p, tree u)
{
  /*
  cout << "Macro argument " << var << " [action=" << type
       << ", level=" << l << "] " << p << ", " << u << " in " << st << "\n";
  */

  bool flag;
  if (valid) {
    int i, n=N(fun)-1, m=N(st);
    env->macro_arg= list<hashmap<string,tree> > (
      hashmap<string,tree> (UNINIT), env->macro_arg);
    env->macro_src= list<hashmap<string,path> > (
      hashmap<string,path> (path (DECORATION)), env->macro_src);
    if (L(fun) == XMACRO) {
      if (is_atomic (fun[0])) {
	string var= fun[0]->label;
	env->macro_arg->item (var)= st;
	env->macro_src->item (var)= ip;
      }
    }
    else for (i=0; i<n; i++)
      if (is_atomic (fun[i])) {
	string var= fun[i]->label;
	env->macro_arg->item (var)=
	  i<m? st[i+delta]: attach_dip (tree (UNINIT), decorate_right (ip));
	env->macro_src->item (var)=
	  i<m? descend (ip,i+delta): decorate_right(ip);
      }
    flag= body->notify_macro (type, var, l+1, p, u);
    env->macro_arg= env->macro_arg->next;
    env->macro_src= env->macro_src->next;
  }
  else flag= env->depends (st, var, l);
  if (flag) status= CORRUPTED;
  return flag;
}

void
bridge_compound_rep::notify_change () {
  status= CORRUPTED;
  if (!nil (body)) body->notify_change ();
}

/******************************************************************************
* Typesetting
******************************************************************************/

bool
bridge_compound_rep::my_typeset_will_be_complete () {
  return !valid;
}

void
bridge_compound_rep::my_typeset (int desired_status) {
  int d; tree f;
  if (L(st) == COMPOUND) {
    d= 1;
    f= st[0];
    if (is_compound (f)) f= env->exec (f);
    if (is_atomic (f)) {
      string var= f->label;
      if (env->provides (var)) f= env->read (var);
      else f= tree (ERROR, st);
    }
  }
  else {
    string var= as_string (L(st));
    if (env->provides (var)) f= env->read (var);
    else f= tree (ERROR, st);
    d= 0;
  }

  if (is_applicable (f)) {
    int i, n=N(f)-1, m=N(st)-d;
    env->macro_arg= list<hashmap<string,tree> > (
      hashmap<string,tree> (UNINIT), env->macro_arg);
    env->macro_src= list<hashmap<string,path> > (
      hashmap<string,path> (path (DECORATION)), env->macro_src);
    if (L(f) == XMACRO) {
      if (is_atomic (f[0])) {
	string var= f[0]->label;
	env->macro_arg->item (var)= st;
	env->macro_src->item (var)= ip;
      }
    }
    else for (i=0; i<n; i++)
      if (is_atomic (f[i])) {
	string var= f[i]->label;
	env->macro_arg->item (var)=
	  i<m? st[i+d]: attach_dip (tree (UNINIT), decorate_right (ip));
	env->macro_src->item (var)=
	  i<m? descend (ip,i+d): decorate_right(ip);
      }
    initialize (f[n], d, f);
    /*IF_NON_CHILD_ENFORCING(st)*/ ttt->insert_marker (st, ip);
    body->typeset (desired_status);
    env->macro_arg= env->macro_arg->next;
    env->macro_src= env->macro_src->next;
  }
  else {
    initialize (f, d, f);
    /*IF_NON_CHILD_ENFORCING(st)*/ ttt->insert_marker (st, ip);
    body->typeset (desired_status);
  }
}
