// Copyright (C) 2001 Jean-Marc Valin

#include <typeinfo>
#include <pthread.h>
#include <sstream>

#include "vflow.h"
#include "GUIDocument.h"
#include "GUINetwork.h"
#include "ParameterSet.h"
#include "Network.h"
#include "rc_ptrs.h"
#include "object_param.h"
#include "UserException.h"

#include "misc_gui.h"

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

using namespace std;

namespace FD {

bool GUIDocument::isRunning=false;
pthread_t GUIDocument::runThread;
Network * GUIDocument::runningNet=NULL;
pthread_mutex_t GUIDocument::del_lock = PTHREAD_MUTEX_INITIALIZER;


#define GLADE_HOOKUP_OBJECT(component,widget,name) \
  g_object_set_data_full (G_OBJECT (component), name, \
    gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref)

#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \
  g_object_set_data (G_OBJECT (component), name, widget)



static GtkWidget *create_view (GnomeMDIChild *child, GUIDocument *doc)
{
   //GUIDocument *doc = gtk_object_get_data (GTK_OBJECT (child), "doc");
   return doc->getView();
}


GUIDocument::GUIDocument(string _name)
   : UIDocument(_name)
   , docproperty(NULL)
   , vbox2(NULL)
   , document_notebook(NULL)
   , less2(NULL)
//   , untitled(true)
//   , modified(false)
{

  //GtkWidget *vflow_notebook = vflowGUI::instance()->get_notebook();
  //cerr<<"GUIDocument getting notebook ptr (vflow app): "<<vflow_notebook<<endl;

  vbox2 = gtk_vpaned_new ();
  //gtk_widget_set_usize(vbox2, -1, 400);
  gtk_widget_ref (vbox2);

  //gtk_object_set_data_full (GTK_OBJECT (mdi), "vbox2", vbox2,
  //                          (GtkDestroyNotify) gtk_widget_unref);

  //cerr<<"GUIDocument creating document : "<<_name<<endl;
  //cerr<<"GUIDocument setting document data ""doc"" to vbox2 : "<<this<<endl;
  gtk_object_set_data(GTK_OBJECT(vbox2), "doc", this);
  gtk_widget_show (vbox2);
  
  
  //gtk_container_add (GTK_CONTAINER (vbox2), vbox2);
  
  

  document_notebook = gtk_notebook_new ();
  //cerr<<"GUIDocument creating document notebook: "<<document_notebook<<endl;
  

  gtk_widget_set_usize(document_notebook, -1, 320);
  gtk_notebook_set_scrollable(GTK_NOTEBOOK(document_notebook), TRUE);
  gtk_notebook_popup_enable (GTK_NOTEBOOK(document_notebook));

  gtk_widget_ref (document_notebook);
  //gtk_object_set_data_full (GTK_OBJECT (mdi), "document_notebook", document_notebook,
  //                          (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (document_notebook);

  gtk_paned_pack1 (GTK_PANED(vbox2), document_notebook, TRUE, TRUE);
  //gtk_container_add (GTK_CONTAINER (vbox2), document_notebook);
  //gtk_box_pack_start (GTK_BOX (vbox2), document_notebook, TRUE, TRUE, 0);

  
  scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
  gtk_widget_show (scrolledwindow1);
  gtk_widget_set_size_request(GTK_WIDGET(scrolledwindow1), -1, 100);
  gtk_paned_pack2 (GTK_PANED(vbox2), scrolledwindow1, FALSE, TRUE);
  //gnome_app_set_contents (GNOME_APP (), scrolledwindow1);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);


  less2 = gtk_text_view_new ();
  gtk_widget_show (less2);
  gtk_container_add (GTK_CONTAINER (scrolledwindow1), less2);
  gtk_text_view_set_editable (GTK_TEXT_VIEW (less2), FALSE);
  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (less2), GTK_WRAP_WORD);
  gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (less2), FALSE);
  /*gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (less2)),
        _("FlowDesigner by Jean-Marc Valin & Dominic Letourneau"), -1);
  */

   
  
  gtk_widget_show(vbox2);

  //add document to the vflow application notebook
  vflowGUI::instance()->add_notebook_document(this, vbox2);


  //gtk_container_add (GTK_CONTAINER (notebook), vbox2);
  //label1 = gtk_label_new ((gchar *)docName.c_str());
  //gtk_widget_show (label1);
  //gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 0), label1);
  //gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
  //gtk_notebook_append_page(GTK_NOTEBOOK(vflow_notebook), vbox2, label1);
  //gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), vbox2, label1);
  //if negative, last page will be used.
  //gtk_notebook_set_current_page (GTK_NOTEBOOK(vflow_notebook), -1);  


  less_print("FlowDesigner " VERSION " by Jean-Marc Valin & Dominic Letourneau");
  less_print("--");


}


/**********************************************************************************************************
This function is called when a GUINetwork is created and needs to add the document notebook page
Dominic Letourneau (13/09/2003)
**********************************************************************************************************/
void GUIDocument::add_notebook_network(GUINetwork *net, GtkWidget *child) {


  string tabName = net->getName();

  switch(net->getType()) {
  case UINetwork::subnet:
    tabName = tabName + " (subnet)";
    break;
  case UINetwork::iterator:
    tabName = tabName + " (iterator)";
    break;
  case UINetwork::threaded:
    tabName = tabName + " (threaded iterator)";
    break;
  default:
    tabName = tabName + " (unknown)";
  }

  //creating label for the notebook page
  GtkWidget *label1 = gtk_label_new ((gchar *)tabName.c_str());
  gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
  gtk_widget_show (label1);
  
  //append page to the notebook
  gtk_notebook_append_page(GTK_NOTEBOOK(getNotebook()), child, label1);

  //if negative, last page will be used.
  gtk_notebook_set_current_page (GTK_NOTEBOOK(getNotebook()), -1);

}

/**********************************************************************************************************
This function is called when a GUINetwork is deleted and needs to remove the document notebook page
Dominic Letourneau (13/09/2003)
**********************************************************************************************************/
void GUIDocument::remove_notebook_network(GUINetwork *net, GtkWidget *child) {

  int page_num = gtk_notebook_page_num(GTK_NOTEBOOK(getNotebook()),child);

  if (page_num != -1) {
    gtk_notebook_remove_page(GTK_NOTEBOOK(getNotebook()),page_num);
  }
  else {
    cerr<<"**ERROR** GUIDocument Widget not in notebook : "<<child<<" doc "<<net->getName()<<endl;
  }
}




GUIDocument::~GUIDocument()
{

  //cerr<<"GUI Document destroyed : "<<destroyed<<endl;
  //cerr<<"number of networks : "<<networks.size()<<endl;
   //Copy of the UIDocument destructor
   if (!destroyed)
   {
      for (unsigned int i=0;i<networks.size();i++)
      {
         delete networks[i];
         networks[i]=NULL;
      }

      for (unsigned int i=0;i<textParams.size();i++)
         delete textParams[i];
      
      for (unsigned int i=0;i<docInputs.size();i++)
         delete docInputs[i];
      
      for (unsigned int i=0;i<docOutputs.size();i++)
         delete docOutputs[i];
      
      for (unsigned int i=0;i<docParams.size();i++)
         delete docParams[i];
      
      vflowGUI::instance()->remove_notebook_document(this,vbox2);
      
      gtk_widget_destroy(vbox2);
      
      destroyed=true;
   }

}

GtkWidget *create_close_dialog (const char *close_str)
{
  GtkWidget *dialog1;
  GtkWidget *dialog_vbox1;
  GtkWidget *hbox1;
  GtkWidget *image1;
  GtkWidget *label2;
  GtkWidget *dialog_action_area1;
  GtkWidget *cancelbutton1;
  GtkWidget *okbutton1;

  dialog1 = gtk_dialog_new ();
  gtk_window_set_title (GTK_WINDOW (dialog1), _("dialog1"));

  dialog_vbox1 = GTK_DIALOG (dialog1)->vbox;
  gtk_widget_show (dialog_vbox1);

  hbox1 = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (hbox1);
  gtk_box_pack_start (GTK_BOX (dialog_vbox1), hbox1, TRUE, TRUE, 0);

  image1 = gtk_image_new_from_stock ("gtk-dialog-warning", GTK_ICON_SIZE_BUTTON);
  gtk_widget_show (image1);
  gtk_box_pack_start (GTK_BOX (hbox1), image1, TRUE, TRUE, 0);

  label2 = gtk_label_new (close_str);
  gtk_widget_show (label2);
  gtk_box_pack_start (GTK_BOX (hbox1), label2, FALSE, FALSE, 0);
  gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);

  dialog_action_area1 = GTK_DIALOG (dialog1)->action_area;
  gtk_widget_show (dialog_action_area1);
  gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1), GTK_BUTTONBOX_END);

  cancelbutton1 = gtk_button_new_from_stock ("gtk-cancel");
  gtk_widget_show (cancelbutton1);
  gtk_dialog_add_action_widget (GTK_DIALOG (dialog1), cancelbutton1, GTK_RESPONSE_CANCEL);
  GTK_WIDGET_SET_FLAGS (cancelbutton1, GTK_CAN_DEFAULT);

  okbutton1 = gtk_button_new_from_stock ("gtk-ok");
  gtk_widget_show (okbutton1);
  gtk_dialog_add_action_widget (GTK_DIALOG (dialog1), okbutton1, GTK_RESPONSE_OK);
  GTK_WIDGET_SET_FLAGS (okbutton1, GTK_CAN_DEFAULT);

  /* Store pointers to all widgets, for use by lookup_widget(). */
  GLADE_HOOKUP_OBJECT_NO_REF (dialog1, dialog1, "dialog1");
  GLADE_HOOKUP_OBJECT_NO_REF (dialog1, dialog_vbox1, "dialog_vbox1");
  GLADE_HOOKUP_OBJECT (dialog1, hbox1, "hbox1");
  GLADE_HOOKUP_OBJECT (dialog1, image1, "image1");
  GLADE_HOOKUP_OBJECT (dialog1, label2, "label2");
  GLADE_HOOKUP_OBJECT_NO_REF (dialog1, dialog_action_area1, "dialog_action_area1");
  GLADE_HOOKUP_OBJECT (dialog1, cancelbutton1, "cancelbutton1");
  GLADE_HOOKUP_OBJECT (dialog1, okbutton1, "okbutton1");

  gtk_widget_show(dialog1);
  return dialog1;
}


int GUIDocument::closeRequest(bool force)
{
   bool close;
   if (modified && !force)
   {
      string mesg = string(_("Do you really want to close ")) + docName + "?";
      GtkWidget *dialog = create_close_dialog(mesg.c_str());
      gint result = gtk_dialog_run (GTK_DIALOG (dialog));
      switch (result)
      {
      case GTK_RESPONSE_OK:
         close = true;
         break;
      default:
         close = false;
         break;
      }
      gtk_widget_destroy (dialog);
   } else {
      close = true;
   }
   
   if (close)
   {
      //Commit suicide, it's not really clean but I think it's OK
      delete this;
   }

   return close;
}

void GUIDocument::save()
{
   less_print(docName + " saved");
   UIDocument::save();
}

void GUIDocument::load()
{
   //cerr << "GUIDocument::load\n";

   UIDocument::load();
	
   for (int i=0;i<networks.size();i++)
   {
      dynamic_cast<GUINetwork *> (networks[i])->updateScroll();
   }

   //resizing parameters
   params.resize(textParams.size());
   
}



void GUIDocument::removeCurrentNet() {


  GUINetwork *current_net = dynamic_cast<GUINetwork*>(getCurrentNet());
  
  if (current_net) {
  
    vector<UINetwork*> new_vect;
   
    for (int i = 0; i < networks.size(); i++) {
      if (networks[i]->getName() == current_net->getName()) {
	
	//cerr<<"remove current net is : "<<current_net->getName()<<endl;
	
	int page_num = gtk_notebook_get_current_page(GTK_NOTEBOOK(document_notebook));

	//cerr<<"page num is : "<<page_num<<endl;

	//remove notebook page
	if (page_num != -1) {
	  //cerr<<"removing notebook page"<<endl;
	  gtk_notebook_remove_page(GTK_NOTEBOOK(document_notebook),page_num);
	  
	  //delete network
	  //cerr<<"deleting network"<<endl;
	  delete networks[i];
	}
      }
      else {
	//rebuilding networks
	new_vect.push_back(networks[i]);
      }     
    }//for

    //update network list
    networks = new_vect;
  
  }
}


void GUIDocument::renameCurrentNet() {

  int netID = gtk_notebook_get_current_page (GTK_NOTEBOOK(document_notebook));
  GUINetwork *net = dynamic_cast<GUINetwork*>(getCurrentNet());
 
  if (net && netID != -1) {
  
    string message;
    message = string("Rename network ") + net->getName();
   
    string newName = ask_string_dialog(message.c_str(), "");
    if (newName != "") {
     
      //update network name
      net->rename(newName);

      //updating text    
      string tabName=newName;
      
      switch (net->getType()) {
      case UINetwork::subnet:
	tabName = tabName + " (subnet)";
	break;
      case UINetwork::iterator:
	tabName = tabName + " (iterator)";
	break;
      case UINetwork::threaded:
	tabName = tabName + " (threaded iterator)";
	break;
      default:
	tabName = tabName + " (unknown)";  
      }

      //update notebook label
      gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(document_notebook), net->getView(), tabName.c_str());

      //SHOULD RENAME ALL SUBNET NODES ?
      
    }
  }   
}

UINetwork* GUIDocument::getCurrentNet() {

  int netID = gtk_notebook_get_current_page (GTK_NOTEBOOK(document_notebook));

  if (netID == -1) {
    return NULL;
  }
  else {
    GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK(document_notebook), netID);
    return (GUINetwork*)gtk_object_get_data(GTK_OBJECT(page), "net");
  }

}


UINetwork *GUIDocument::newNetwork(const string &_name, UINetwork::Type type)
{
   return new GUINetwork(this, _name, type);
}

UINetwork *GUIDocument::newNetwork(xmlNodePtr _net)
{
   return new GUINetwork(this, _net);
}


void GUIDocument::setFullPath(const string &fullpath)
{
   // call the non-gui code in UIDocument
   UIDocument::setFullPath(fullpath);
}

static void param_apply (GnomePropertyBox *propertybox, gint arg1, gpointer user_data)
{
   ((GUIDocument *)(user_data))->applyParams();
}

static void entry_changed (GtkEntry *entry, gpointer user_data)
{
   ((GUIDocument *)(user_data))->changedParams();
}

static void type_changed (GnomePropertyBox *propertybox, gpointer user_data)
{
   ((GUIDocument *)(user_data))->changedParams();
}

void GUIDocument::showParams() {

   UINetwork *uinet = getNetworkNamed("MAIN");
   
   GUINetwork *net = dynamic_cast<GUINetwork*>(uinet);


  //FIXME : FIND A BETTER IMPLEMENTATION !
  if (net) {

    vector<ItemInfo *> my_params;
    
    net->insertNetParams(my_params);
    
    //cerr<<"params size "<<my_params.size()<<endl;
    
    //looking for non existing parameters
    for (int i = 0; i < my_params.size(); i++) {
      
      bool found = false;
      
      for (int j = 0; j < textParams.size(); j++) {
	if (textParams[j]->name == my_params[i]->name) {
	  found = true;
	  break;
	}
      }
      if (!found) {
	//adding parameter
	addParameterText(my_params[i]->name,my_params[i]->value,my_params[i]->type);   
      }
    }
    
    list<DocParameterDataText*> tmp_list;

    for (int i = 0; i < textParams.size(); i++) {

      bool found = false;
 
      for (int j = 0; j < my_params.size(); j++) {
	if (textParams[i]->name == my_params[j]->name) {
	  found = true;
	  break;
	}
      }
      if (!found) {
	//removing parameter
	delete textParams[i];
	textParams[i] = NULL;
      }
      else {
	//keeping the parameter
	tmp_list.push_back(textParams[i]);
      }
    }
    
    textParams.resize(tmp_list.size());

    int i = 0; 
    list<DocParameterDataText*>::iterator iter = tmp_list.begin();

    for ( ;iter != tmp_list.end(); iter++,i++) {
      textParams[i] = (*iter);
    }

    createParamDialog();  
    
    gtk_widget_show (docproperty);
  }
  else {
    cerr<<"MAIN subnet does not exist..."<<endl;
  }

}

void GUIDocument::changedParams()
{
   gnome_property_box_changed(GNOME_PROPERTY_BOX(docproperty));
}

void GUIDocument::applyParams()
{
   for (int i=0;i<params.size();i++)
   {
      //GtkWidget *gtk_option_menu_get_menu(params[i].optionmenu);
      GtkWidget *menu = gtk_menu_get_active (GTK_MENU(params[i].optionmenu_menu));
      textParams[i]->type = (char *)gtk_object_get_user_data (GTK_OBJECT(menu));

      GtkWidget *gtkentr = gnome_entry_gtk_entry(GNOME_ENTRY(params[i].entry));
      textParams[i]->value = gtk_entry_get_text(GTK_ENTRY(gtkentr));
      
      //cerr << "<param: " << params[i].name << ", " << params[i].type << ":" << params[i].value << ">\n";
   }

   //updating category
   GtkCombo *combo = GTK_COMBO(gtk_object_get_data(GTK_OBJECT(docproperty), "category_combo"));
   string entry_text(gtk_entry_get_text(GTK_ENTRY(combo->entry))); //get entry text
   setCategory(entry_text);


   //updating comments
   GtkTextView *text1 = GTK_TEXT_VIEW(gtk_object_get_data(GTK_OBJECT(docproperty), "text1"));
   GtkTextBuffer* buffer = gtk_text_view_get_buffer(text1);
   GtkTextIter start, end;
   gtk_text_buffer_get_bounds(buffer, &start, &end);   
   setComments(gtk_text_buffer_get_text (buffer, &start, &end, TRUE));

   setModified();
}

void GUIDocument::insertLoadedParam(DocParameterData *param, string type, string value)
{

/*
   const vector<string> &types=ObjectParam::allTypes(false);
   for (int i=0;i<types.size();i++)
      if (types[i] == type)
         gtk_option_menu_set_history (GTK_OPTION_MENU (param->optionmenu), i);


   GtkWidget *gtkentr = gnome_entry_gtk_entry(GNOME_ENTRY(param->entry));
   gtk_entry_set_text(GTK_ENTRY(gtkentr),(gchar *)value.c_str());
*/

}

void document_category_changed_event (GtkEntry *entry, GUIDocument *document) {

  //get the entry text
  string entry_text(gtk_entry_get_text(entry));   

  //change the category
  //will be changed when apply is pressed
  //document->setCategory(entry_text);
  
  gnome_property_box_set_modified (GNOME_PROPERTY_BOX(document->docproperty),TRUE);
  
}

void document_comments_changed_event (GtkTextBuffer *textbuffer, GUIDocument *document) {  
  gnome_property_box_set_modified (GNOME_PROPERTY_BOX(document->docproperty),TRUE);
}

void GUIDocument::createParamDialog()
{
   int i;


  GtkWidget *notebook2;
  GtkWidget *table2;
  GtkWidget *label14;
  GtkWidget *label15;
  GtkWidget *label16;
  GtkWidget *hseparator4;
  GtkWidget *hseparator5;
  GtkWidget *hseparator6;
  GtkWidget *glade_menuitem;
  

  GtkWidget *label12;
  GtkWidget *scrolledwindow2;
  GtkWidget *text1;
  GtkWidget *label13;

  docproperty = gnome_property_box_new ();
  //gnome_dialog_close_hides (GNOME_DIALOG(docproperty), TRUE);

  gtk_object_set_data (GTK_OBJECT (docproperty), "docproperty", docproperty);

  notebook2 = GNOME_PROPERTY_BOX (docproperty)->notebook;
  gtk_object_set_data (GTK_OBJECT (docproperty), "notebook2", notebook2);
  gtk_widget_show (notebook2);

  table2 = gtk_table_new (2+params.size(), 3, FALSE);
  gtk_widget_ref (table2);
  gtk_object_set_data_full (GTK_OBJECT (docproperty), "table2", table2,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (table2);
  gtk_container_add (GTK_CONTAINER (notebook2), table2);

  label14 = gtk_label_new (_("Name"));
  gtk_widget_ref (label14);
  gtk_object_set_data_full (GTK_OBJECT (docproperty), "label14", label14,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (label14);
  gtk_table_attach (GTK_TABLE (table2), label14, 0, 1, 0, 1,
                    (GtkAttachOptions) (0),
                    (GtkAttachOptions) (0), 0, 0);

  label15 = gtk_label_new (_("Type"));
  gtk_widget_ref (label15);
  gtk_object_set_data_full (GTK_OBJECT (docproperty), "label15", label15,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (label15);
  gtk_table_attach (GTK_TABLE (table2), label15, 1, 2, 0, 1,
                    (GtkAttachOptions) (0),
                    (GtkAttachOptions) (0), 0, 0);

  label16 = gtk_label_new (_("Value"));
  gtk_widget_ref (label16);
  gtk_object_set_data_full (GTK_OBJECT (docproperty), "label16", label16,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (label16);
  gtk_table_attach (GTK_TABLE (table2), label16, 2, 3, 0, 1,
                    (GtkAttachOptions) (0),
                    (GtkAttachOptions) (0), 0, 0);

  hseparator4 = gtk_hseparator_new ();
  gtk_widget_ref (hseparator4);
  gtk_object_set_data_full (GTK_OBJECT (docproperty), "hseparator4", hseparator4,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (hseparator4);
  gtk_table_attach (GTK_TABLE (table2), hseparator4, 0, 1, 1, 2,
                    (GtkAttachOptions) (GTK_FILL),
                    (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);

  hseparator5 = gtk_hseparator_new ();
  gtk_widget_ref (hseparator5);
  gtk_object_set_data_full (GTK_OBJECT (docproperty), "hseparator5", hseparator5,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (hseparator5);
  gtk_table_attach (GTK_TABLE (table2), hseparator5, 1, 2, 1, 2,
                    (GtkAttachOptions) (GTK_FILL),
                    (GtkAttachOptions) (GTK_FILL), 0, 0);

  hseparator6 = gtk_hseparator_new ();
  gtk_widget_ref (hseparator6);
  gtk_object_set_data_full (GTK_OBJECT (docproperty), "hseparator6", hseparator6,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (hseparator6);
  gtk_table_attach (GTK_TABLE (table2), hseparator6, 2, 3, 1, 2,
                    (GtkAttachOptions) (GTK_FILL),
                    (GtkAttachOptions) (GTK_FILL), 0, 0);


  //making sure we have allocated enough GUI widgets for all parameters
  params.resize(textParams.size());
  
  for (i=0; i < textParams.size(); i++) {

    //creating label
    params[i].label = gtk_label_new (textParams[i]->name.c_str());
    gtk_widget_ref (params[i].label);
    gtk_object_set_data_full (GTK_OBJECT (docproperty), "label", params[i].label,
			      (GtkDestroyNotify) gtk_widget_unref);
    gtk_widget_show (params[i].label);
    gtk_table_attach (GTK_TABLE (table2), params[i].label, 0, 1, 2+i, 3+i,
		      (GtkAttachOptions) (0),
		      (GtkAttachOptions) (0), 0, 0);
    
    
    //creating option menu
    params[i].optionmenu = gtk_option_menu_new ();
    gtk_widget_ref (params[i].optionmenu);
    gtk_object_set_data_full (GTK_OBJECT (docproperty), "optionmenu", params[i].optionmenu,
			      (GtkDestroyNotify) gtk_widget_unref);
    gtk_widget_show (params[i].optionmenu);
    gtk_table_attach (GTK_TABLE (table2), params[i].optionmenu, 1, 2, 2+i, 3+i,
		      (GtkAttachOptions) (0),
		      (GtkAttachOptions) (0), 0, 0);
    params[i].optionmenu_menu = gtk_menu_new ();
    
    const vector<string> &types=ObjectParam::allTypes(false);
    for (int j=0;j<types.size();j++)
      {
        glade_menuitem = gtk_menu_item_new_with_label ((const gchar *)types[j].c_str());
        gtk_object_set_user_data(GTK_OBJECT(glade_menuitem), (void *)types[j].c_str());
        gtk_widget_show (glade_menuitem);
        gtk_menu_append (GTK_MENU (params[i].optionmenu_menu), glade_menuitem);
      }
    
    gtk_option_menu_set_menu (GTK_OPTION_MENU (params[i].optionmenu), params[i].optionmenu_menu);
    
    //setting history according to the type
    for (int j=0;j<types.size();j++) {
      if (types[j] == textParams[i]->type) {
	gtk_option_menu_set_history (GTK_OPTION_MENU (params[i].optionmenu), j);
      }
    }        
    
    gtk_signal_connect (GTK_OBJECT ( params[i].optionmenu_menu ), "selection-done",
                        GTK_SIGNAL_FUNC( type_changed), this);
    
    
    
    //entry
    params[i].entry = gnome_entry_new (NULL);
    gtk_widget_ref (params[i].entry);
    gtk_object_set_data_full (GTK_OBJECT (docproperty), "entry", params[i].entry,
			      (GtkDestroyNotify) gtk_widget_unref);

    gtk_widget_show (params[i].entry);
	

    gtk_table_attach (GTK_TABLE (table2), params[i].entry, 2, 3, 2+i, 3+i,
		      (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
		      (GtkAttachOptions) (0), 0, 0);
    
    params[i].combo_entry = gnome_entry_gtk_entry (GNOME_ENTRY (params[i].entry));
    gtk_widget_ref (params[i].combo_entry);
    gtk_object_set_data_full (GTK_OBJECT (docproperty), "combo_entry", params[i].combo_entry,
			      (GtkDestroyNotify) gtk_widget_unref);
    gtk_widget_show (params[i].combo_entry);

    //setting combo entry text
    gtk_entry_set_text (GTK_ENTRY(params[i].combo_entry), textParams[i]->value.c_str());  

    
    GtkWidget *gtkentr = gnome_entry_gtk_entry(GNOME_ENTRY(params[i].entry));
    gtk_signal_connect (GTK_OBJECT ( gtkentr  ), "changed",
			GTK_SIGNAL_FUNC(entry_changed), this);
  }
  
  label12 = gtk_label_new (_("Parameters"));
  gtk_widget_ref (label12);
  gtk_object_set_data_full (GTK_OBJECT (docproperty), "label12", label12,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (label12);
  gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook2), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook2), 0), label12);


  GtkWidget *vbox_comments = gtk_vbox_new(false,1); //not homogenous, 0 spacing
  gtk_widget_ref(vbox_comments);
  gtk_object_set_data_full(GTK_OBJECT(docproperty), "vbox_comments", vbox_comments,
			   (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show(vbox_comments);
  gtk_container_add(GTK_CONTAINER(notebook2),vbox_comments);

  //category hbox
  GtkWidget *hbox_category = gtk_hbox_new(false,1);
  gtk_widget_ref(hbox_category);
  gtk_object_set_data_full(GTK_OBJECT(docproperty), "hbox_category", hbox_category,
			   (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show(hbox_category);
  gtk_box_pack_start_defaults(GTK_BOX(vbox_comments), GTK_WIDGET(hbox_category));
  gtk_widget_show(hbox_category);

  //category label
  GtkWidget *category_label = gtk_label_new("Category");
  gtk_widget_ref(category_label);
  gtk_object_set_data_full(GTK_OBJECT(docproperty), "category_label", category_label,
			   (GtkDestroyNotify) gtk_widget_unref);
  gtk_box_pack_start_defaults(GTK_BOX(hbox_category), GTK_WIDGET(category_label));
  gtk_widget_show(category_label);


  //category combo box
  GtkWidget *category_combo = gtk_combo_new();
  gtk_widget_ref(category_combo);
  gtk_object_set_data_full(GTK_OBJECT(docproperty),"category_combo",category_combo,
			   (GtkDestroyNotify) gtk_widget_unref);
  gtk_box_pack_end_defaults(GTK_BOX(hbox_category),  GTK_WIDGET(category_combo));

  //write category in the combo box
  GList *combo_list = NULL;
  combo_list = g_list_append(combo_list, (char*) getCategory().c_str() );
  gtk_combo_set_popdown_strings   (GTK_COMBO(category_combo),combo_list);

  //destroy list
  g_list_free(combo_list);

  gtk_widget_show(category_combo);

  //signal for entry in the combo box
  gtk_signal_connect (GTK_OBJECT (GTK_COMBO(category_combo)->entry), "changed",
			GTK_SIGNAL_FUNC(document_category_changed_event), this);

  //comments text area
  scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL);
  gtk_widget_ref (scrolledwindow2);
  gtk_object_set_data_full (GTK_OBJECT (docproperty), "scrolledwindow2", scrolledwindow2,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (scrolledwindow2);
  gtk_box_pack_end_defaults(GTK_BOX(vbox_comments),  GTK_WIDGET(scrolledwindow2));

  //gtk_container_add (GTK_CONTAINER (notebook2),  scrolledwindow2);

  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);

  text1 = gtk_text_view_new ();
  gtk_widget_ref (text1);

  //text view properties
  gtk_text_view_set_editable (GTK_TEXT_VIEW (text1), TRUE);
  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text1), GTK_WRAP_WORD);
  gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (text1), TRUE);	 

  //set the comments		   
  gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text1)),
			    getComments().c_str(), -1);

  gtk_object_set_data_full (GTK_OBJECT (docproperty), "text1", text1,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (text1);
  gtk_container_add (GTK_CONTAINER (scrolledwindow2), text1);
  gtk_text_view_set_editable(GTK_TEXT_VIEW(text1),TRUE);

  //comments signal handler
  g_object_connect (G_OBJECT (gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1))), "signal::changed",
		    GTK_SIGNAL_FUNC(document_comments_changed_event), this,NULL);

  label13 = gtk_label_new (_("Comments"));
  gtk_widget_ref (label13);
  gtk_object_set_data_full (GTK_OBJECT (docproperty), "label13", label13,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (label13);
  gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook2), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook2), 1), label13);

  //gnome_property_box_changed(GNOME_PROPERTY_BOX(docproperty));
  gtk_signal_connect (GTK_OBJECT (docproperty), "apply",
                      GTK_SIGNAL_FUNC(param_apply), this);

}



static void threadFunct(GUIDocument *doc)
{
   doc->run();
}

void GUIDocument::threadRun()
{
   less_print("Running " + docName + "...");
   if (!isRunning)
   {
      isRunning=true;

      //Let's create the thread already in a detached state
      pthread_attr_t tattr;
      pthread_attr_init(&tattr);
      //pthread_attr_setdetachstate(&tattr,PTHREAD_CREATE_DETACHED);
      pthread_create(&runThread, &tattr, (void * (*)(void *))threadFunct, this);
      //pthread_detach(runThread); //Dominic

      
   }
}

//extern void set_run_mode (bool isRuning);

void GUIDocument::threadStop()
{   
   gdk_threads_leave();
   if (isRunning) {
      pthread_mutex_lock(&del_lock);
      isRunning=false;
      gdk_threads_enter();
      runningNet->stop();
      gdk_threads_leave();
      pthread_mutex_unlock(&del_lock);
      pthread_join(runThread, NULL);
   }
   gdk_threads_enter();
}

//Run with a GUI
void GUIDocument::run()
{
   //Network *net;
   try{
      ParameterSet parameters;
      {
	 //cerr << "there are " << params.size() << " params\n";
	 for (int i=0;i<params.size();i++)
	 {
	    DocParameterDataText *curr = textParams[i];
	    ParameterSet dummy;
	    ObjectRef value = ObjectParam::stringParam(curr->type, curr->value, dummy);
	    if (!value.isNil())
	       parameters.add(curr->name,value);
	 }

      }
      //cerr << "building net...\n";
      //parameters.print();
      
      bool buildError = false;

      runningNet = NULL;
      Network *net=NULL;
      //try {
	 net = build("MAIN", parameters);

      {
      if (net->getInputNode())
	 throw new GeneralException ("main network has input node", __FILE__, __LINE__);
      runningNet = net;
      //verifyConnect done by UIDocument
      //net->verifyConnect();
      
      //processing buffer requests
      for (int i = 0; ;i++) {
	 if (!net->hasOutput(i)) break;

	 ParameterSet req;
	 net->request(i,req);
      }
      
      //initializing
      net->initialize();

      //cerr << "running...\n";
      
      // Getting all the network outputs.
      for (int k=0; ;k++) {
	if (net->hasOutput(k)) {
	   stringstream execOut;
	   execOut << *net->getOutput(k,0);
	   

	   gdk_threads_enter();
	   less_print(execOut.str());
	   gdk_threads_leave();

	}
	else {
	  break;
	}
      }
      
      gdk_threads_enter();
      less_print("Exited normally");
      gdk_threads_leave();
      //delete net;
      //ask for params and desired output
      }  
      //run in a window in a separated thread
   } catch (BaseException *e)
   {
      stringstream excOut;

      e->print (excOut);
      gdk_threads_enter();
      less_print(excOut.str());
      gdk_threads_leave();
      //cerr << "exception caught\n";
      //e->print();
      //cerr << "---\n";

      delete e;
   } catch (UserException *e)
   {
      less_print("User stop");
      delete e;
   }
   catch (...)
   {
      gdk_threads_enter();
      less_print("Unknown exception caught");
      gdk_threads_leave();
   }
     
   gdk_threads_enter();
   vflowGUI::instance()->set_run_mode(false);
   gdk_threads_leave();

   pthread_mutex_lock(&del_lock);
   if (runningNet)
   {
      runningNet->cleanupNotify();
      delete runningNet;
      runningNet=NULL;
   }
   isRunning=false;
   pthread_mutex_unlock(&del_lock);

}


void GUIDocument::less_print(const string &message) 
{
  less_text += message + string("\n");
  
  if (less2) 
  {
     gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (less2)),
                               less_text.c_str(), -1);

     GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment
                                   (GTK_SCROLLED_WINDOW(scrolledwindow1));
     gtk_adjustment_set_value(adj, adj->upper);
     gtk_adjustment_value_changed(adj);
  }
  
}

void GUIDocument::less_print(const char *message) 
{
   /*less_text += string(message) + string("\n");

  if (less2) 
  {
     gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (less2)),
                               less_text.c_str(), -1);
  }
   */
   less_print(string(message));
}

void GUIDocument::less_clear() 
{
   less_text = string();
   if (less2) 
   {
      gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (less2)),
                                less_text.c_str(), -1);
   }
}

string GUIDocument::getNewNetName(UINetwork::Type type)
{
   string baseName;
   if (type == UINetwork::subnet)
      baseName = "SUBNET";
   else if (type == UINetwork::iterator)
      baseName = "LOOP";
   else if (type == UINetwork::threaded)
      baseName = "THREAD";
   bool OK=false;
   int nb=0;
   string newName;
   while (!OK)
   {
      OK=true;
      stringstream st;
      st << nb;
      newName = baseName + st.str();
      for (int i=0;i<networks.size();i++)
	 if (networks[i]->getName() == newName)
	 {
	    OK=false;
	    nb++;
	    break;
	 }
   }
   return newName;
}

void error_dismiss(GtkWidget *widget, gpointer user_data)
{
   gtk_widget_destroy(widget);
   //cerr << "called\n";
}

void GUIDocument::error(char *err)
{
   GtkWidget *errDialog = gnome_error_dialog (err);

   gtk_signal_connect (GTK_OBJECT ( errDialog ), "close",
		       GTK_SIGNAL_FUNC( error_dismiss), NULL);
}

void GUIDocument::updateSubnet() {

  for (int j = 0; j < networks.size(); j++) {
    for (int i= 0; i < networks.size(); i++) {
      networks[i]->newNetNotify("Subnet",networks[j]->getName());
    }
  }
}

}//namespace FD



syntax highlighted by Code2HTML, v. 0.9.1