#ifndef INFER_DISP_H
#define INFER_DISP_H

#include "mdispatch.hpp"


//The inferentail version of the dispatcher just replaces
//the add function of the disp class with a version of
//add that updates the inheritance graph to include
//information about whatever argument types are passed to it.
template<class B1, class B2, class R>
class infer_disp : public disp<B1,B2,R> {
  
  template<class A, class B>
  int insert_into_tree(igraph<B> &g) {
    int i,j;

    //check to make sure we haven't seen this A before
    string type_name=typeid(A).name();
    for(i=0;i<g.data.size();i++) {
      if(g.data[i]->class_name()==type_name) return i;
    }

    vector<int> found,remaining;
    A a;
    B *b = NULL;
    int a_id = g.template vertex_add<A>();
    int temp, num_kids, num_parents, child_id, parent_id;
    

    for(i = 0; i < g.num_leaves(); i++)
      remaining.push_back(g.leaf(i));

    for(i = 0; i < remaining.size(); i++) {
      parent_id = remaining[i];
      for(j = 0; j < found.size(); j++)
	if(parent_id == found[j])
	  goto end_of_loop;

      if(g[parent_id]->isa(&a)) {
	//find insertions to the middle of the tree
	num_kids = g[parent_id]->num_children();
	for(j = 0; j < num_kids; j++) {
	  child_id = g[parent_id]->child(j);
	  b = g[child_id]->me(); 
	  if(dynamic_cast<A*>(b)) {
	    //break child and connect to new parent
	    g.edge_remove(parent_id, child_id);
	    g.edge_add(a_id, child_id);
	  }
	  delete b;
	}
	//connect new parent
	g.edge_add(parent_id, a_id);
	num_parents = g[parent_id]->num_parents();
	for(j = 0; j < num_parents; j++)
	  found.push_back(g[parent_id]->parent(j));
      } else {
	num_parents = g[parent_id]->num_parents();
	for(j = 0; j < num_parents; j++)
	  remaining.push_back(g[parent_id]->parent(j));
      }
      found.push_back(parent_id);
    end_of_loop:;
      
    }
    return a_id;
  }

public:
  infer_disp(base_func f) : disp<B1,B2,R>(NULL,NULL,f) { }

  template<class A1, class A2>
  void add(R (*f)(A1 *, A2*)) {
    int a1 = this->template insert_into_tree<A1, B1>(g1);
    //cout << "g1:\n" << g1 << endl;
    int a2 = this->template insert_into_tree<A2, B2>(g2);
    //cout << "g2:\n" << g2 << endl;
    int2 test(1,2);
    fun_hash[int2(a1,a2)] = new disp<B1,B2,R>::fun<A1,A2>(f);
    build_leaf_list();
  }
  
};


#endif
