/*
   Copyright (C) 1998,1999,2000,2001
   T. Scott Dattalo and Ralf Forsberg

This file is part of gpsim.

gpsim is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

gpsim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with gpsim; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "../config.h"
#ifdef HAVE_GUI

#include <unistd.h>
#include <gtk/gtk.h>

#include <gdk/gdktypes.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>

#include "../src/gpsim_def.h"
#include "../src/gpsim_interface.h"

#include "gui.h"
#include "gui_breadboard.h"
#include "gui_interface.h"
#include "gui_processor.h"
#include "gui_profile.h"
#include "gui_register.h"
#include "gui_regwin.h"
#include "gui_scope.h"
#include "gui_src.h"
#include "gui_stack.h"
#include "gui_statusbar.h"
#include "gui_stopwatch.h"
#include "gui_symbols.h"
#include "gui_trace.h"
#include "gui_watch.h"

#ifndef _WIN32
#undef TRUE
#undef FALSE
#include "settings_exdbm.h"
#else
#include "settings_reg.h"
#endif

extern int gui_animate_delay; // in milliseconds

/*
 * --- Function prototypes
 */
void init_link_to_gpsim(GUI_Processor *gp);
void link_src_to_gpsim(GUI_Processor *gp);

/* 
 * --- Global variables
 */

GUI_Processor *gpGuiProcessor=0;
GSList *gui_processors=0;
unsigned int interface_id=0;

Settings *settings;

extern GtkWidget *dispatcher_window;
extern void dispatch_Update();

//------------------------------------------------------------------------
// 
extern bool gUsingThreads(); // in ../src/interface.cc

//------------------------------------------------------------------------
// debug
static void gtl()
{
#if GLIB_MAJOR_VERSION >= 2
  if(gUsingThreads())
    gdk_threads_leave ();
#endif
}
static void gte()
{
#if GLIB_MAJOR_VERSION >= 2
  if(gUsingThreads())
    gdk_threads_enter ();
#endif
}

//------------------------------------------------------------------------
void CrossReferenceToGUI::Update(int new_value)
{
  printf("CrossReferenceToGUI::Update shouldn't be called!\n");
}

void CrossReferenceToGUI::Remove(void)
{
  printf("CrossReferenceToGUI::Remove shouldn't be called!\n");
}

//------------------------------------------------------------------------
//

class GUI_Interface : public Interface
{
private:
  GUI_Processor *gp;

public:

  virtual void UpdateObject (gpointer xref,int new_value);
  virtual void RemoveObject (gpointer xref);
  virtual void SimulationHasStopped (gpointer object);
  virtual void NewProcessor (Processor *);
  virtual void NewModule (Module *module);
  virtual void NodeConfigurationChanged (Stimulus_Node *node);
  virtual void NewProgram  (Processor *);
  virtual void Update  (gpointer object);

  virtual ~GUI_Interface();

  GUI_Interface(GUI_Processor *_gp);

};


GUI_Interface::GUI_Interface(GUI_Processor *_gp) : Interface ( (gpointer *) _gp)
{

  gp = _gp;

}

GUI_Interface::~GUI_Interface()
{
  if(gp) {
    gp->regwin_ram->set_config();
    gp->regwin_eeprom->set_config();
    gp->program_memory->set_config();
    gp->source_browser->set_config();
    gp->watch_window->set_config();
    gp->stack_window->set_config();
    gp->breadboard_window->set_config();
    gp->trace_window->set_config();
    gp->profile_window->set_config();
    gp->stopwatch_window->set_config();
    gp->scope_window->set_config();
  }
}


/*------------------------------------------------------------------
 * UpdateObject
 *
 * Each 'thing' that the gui displays about a simulated pic has an
 * associated cross reference structure. Sometimes these 'things' 
 * displayed in more than one place (like the status register).
 * Each graphical instance has its own structure. All of the structures
 * pertaining to the same pic object (again, like the status register)
 * are stored in a singly-linked list. This routine scans through
 * this list and updates each instance of the object.
 */

void GUI_Interface::UpdateObject(gpointer gui_xref,int new_value)
{

  gte ();
  CrossReferenceToGUI  *xref = (CrossReferenceToGUI *)gui_xref;
  xref->Update(new_value);
  gtl ();

}

/*------------------------------------------------------------------
 * RemoveObject
 *
 */

void GUI_Interface::RemoveObject(gpointer gui_xref)
{

  CrossReferenceToGUI  *xref = (CrossReferenceToGUI *)gui_xref;
  xref->Remove();

}

#if GLIB_MAJOR_VERSION >= 2
// thread variables.
static GMutex *muSimStopMutex=0;
static GCond  *cvSimStopCondition=0;
#endif

extern int gui_animate_delay; // in milliseconds

static GUI_Processor *lgp=0;

static void *SimulationHasStopped( void *ptr )
{

#if GLIB_MAJOR_VERSION >= 2
  while(1) {
    if(gUsingThreads()) {
      g_mutex_lock(muSimStopMutex);
      g_cond_wait(cvSimStopCondition, muSimStopMutex);
    }
#endif
    if(lgp) {
      GTKWAIT;

      lgp->regwin_ram->Update();
      lgp->regwin_eeprom->Update();
      lgp->program_memory->Update();
      lgp->source_browser->Update();
      lgp->watch_window->Update();
      lgp->stack_window->Update();
      lgp->breadboard_window->Update();
      lgp->trace_window->Update();
      lgp->profile_window->Update();
      lgp->stopwatch_window->Update();
      lgp->scope_window->Update();


      
    }
    
    if(gui_animate_delay!=0)
          usleep(1000*gui_animate_delay);

    dispatch_Update();

#if GLIB_MAJOR_VERSION >= 2
    if(gUsingThreads()) 
      g_mutex_unlock(muSimStopMutex);
    else
      return 0;
  }
#endif

}


/*------------------------------------------------------------------
 * SimulationHasStopped
 *
 */

void GUI_Interface::SimulationHasStopped(gpointer callback_data)
{
  if(callback_data) {
    
    lgp = (GUI_Processor *) callback_data;
#if GLIB_MAJOR_VERSION >= 2

    if(gUsingThreads()) {
      g_mutex_lock(muSimStopMutex);
      g_cond_signal(cvSimStopCondition);
      g_mutex_unlock(muSimStopMutex);
    } else
#endif
    ::SimulationHasStopped(0);

  }
}


/*------------------------------------------------------------------
 * NewProcessor - Add a new processor 
 *
 * This routine adds another processor to the list of currently
 * simulated processors (as of 0.0.14 though, you're still limited
 * to a list of one). It then notifies each child window. Finally
 * a communication link between the gui and the simulator is established.
 */

void GUI_Interface::NewProcessor (Processor *new_cpu)
{

  // Create a gui representation of the new processor

  if(gp) {
    gte ();

    gp->SetCPU(new_cpu);

    gui_processors = g_slist_append(gui_processors,gp);

    gp->regwin_ram->NewProcessor(gp);
    gp->program_memory->NewProcessor(gp);
    gp->source_browser->CloseSource();
    gp->source_browser->NewProcessor(gp);
    gp->symbol_window->NewSymbols();
    //gp->watch_window->NewProcessor(gp);
    gp->breadboard_window->NewProcessor(gp);
    gp->stack_window->NewProcessor(gp);
    gp->trace_window->NewProcessor(gp);
    gp->profile_window->NewProcessor(gp);
    gp->stopwatch_window->NewProcessor(gp);
    //gp->scope_window->NewProcessor(gp);

    Dprintf((" New processor has been added"));
    gtl ();
  }

}
/*------------------------------------------------------------------
 *
 */
void GUI_Interface::NewModule (Module *module)
{

  // FIX ME - need to search for *p in the gp list...
  if(gp)
    gp->breadboard_window->NewModule(module);
}

/*------------------------------------------------------------------
 *
 */
void GUI_Interface::NodeConfigurationChanged (Stimulus_Node *node)
{

  // FIX ME - need to search for *p in the gp list...
  if(gp)
    gp->breadboard_window->NodeConfigurationChanged(node);
}

/*------------------------------------------------------------------
 *
 */
void GUI_Interface::NewProgram (Processor *new_cpu)
{

  if(gp) {
    gte ();

    gp->regwin_eeprom->NewProcessor(gp);
      
    gp->source_browser->CloseSource();
    gp->source_browser->NewSource(gp);
    gp->symbol_window->NewSymbols();
    gp->program_memory->NewSource(gp);
    gp->profile_window->NewProgram(gp);
    gp->watch_window->NewProcessor(gp);
    link_src_to_gpsim( gp);
    gtl ();

  }
}


void GUI_Interface::Update(gpointer object)
{
  SimulationHasStopped(object);
}



/*------------------------------------------------------------------
 * 
 */
#if GTK_MAJOR_VERSION >= 2
int gStringWidth(PangoFontDescription *font, const char *str)
{
  return (font && str) ? gdk_string_width (gdk_font_from_description(font),str) : 0;
}
int gStringHeight(PangoFontDescription *font, const char *str)
{
  return (font && str) ? gdk_string_height (gdk_font_from_description(font),str) : 0;
}
GdkFont *gFontFromDescription(PangoFontDescription *font)
{
  return (font ? gdk_font_from_description(font) : 0);
}
#else
int gStringWidth(GdkFont *font, const char *str)
{
  return (font && str) ? gdk_string_width (font,str) : 0;
}
int gStringHeight(GdkFont *font, const char *str)
{
  return (font && str) ? gdk_string_height (font,str) : 0;
}
#endif


/*------------------------------------------------------------------
 * quit_gui
 *
 */
void quit_gui(void)
{
  if(!get_interface().bUsingGUI())
    return;

    int x,y,width,height;

    gdk_window_get_root_origin(dispatcher_window->window,&x,&y);
    gdk_window_get_size(dispatcher_window->window,&width,&height);

    config_set_variable("dispatcher", "enable", 1);
    config_set_variable("dispatcher", "x", x);
    config_set_variable("dispatcher", "y", y);
    config_set_variable("dispatcher", "width", width);
    config_set_variable("dispatcher", "height", height);

    get_interface().remove_interface(interface_id);

    gtk_main_quit();
}

/*------------------------------------------------------------------
 * gui_init
 *
 */

int gui_init (int argc, char **argv)
{
#ifndef _WIN32
  settings = new SettingsEXdbm("gpsim");
#else
  settings = new SettingsReg("gpsim");
#endif

#if GLIB_MAJOR_VERSION >= 2
  if(gUsingThreads()) {

    GThread          *Thread1;
    GError           *err1 = NULL ;
  
    muSimStopMutex     = g_mutex_new ();
    cvSimStopCondition = g_cond_new ();
    g_mutex_lock(muSimStopMutex);

    if( (Thread1 = g_thread_create((GThreadFunc)SimulationHasStopped, 
				   (void *)0, 
				   TRUE, 
				   &err1)) == NULL)
      {
	printf("Thread create failed: %s!!\n", err1->message );
	g_error_free ( err1 ) ;
      }
    g_mutex_unlock(muSimStopMutex);
  }
#endif

  if (!gtk_init_check (&argc, &argv))
  {
      return -1;
  }


  gte();
  gpGuiProcessor = new GUI_Processor();
  interface_id = get_interface().add_interface(new GUI_Interface(gpGuiProcessor));
  gtl();

  return(0);
}

void gui_main(void)
{
  gte ();
  gtk_main ();
  gtl ();
}

#endif //HAVE_GUI


syntax highlighted by Code2HTML, v. 0.9.1