/****************************************************************************
    
    main.cpp - the main file for the Oscilloscope GUI
    
    Copyright (C) 2005  Lars Luthman <larsl@users.sourceforge.net>
    
    This program 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 of the License, or
    (at your option) any later version.
    
    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307  USA

****************************************************************************/

#include <algorithm>
#include <iostream>
#include <iterator>
#include <deque>
#include <set>
#include <string>

#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>

#include <gtkmm.h>
#include <libglademm.h>
#include <ladspa.h>

#include "dssiuiclient.hpp"
#include "ringbuffer.h"
#include "scopewidget.hpp"
#include "ll-scope.h"


using namespace std;
using namespace Gdk;
using namespace Gtk;
using namespace Gnome::Glade;
using namespace Glib;


Gtk::Window* main_win;

Gtk::Window* init_gui(ScopeWidget& scope_canvas);
void toggle_controls(Widget* notoggle);


int main(int argc, char** argv) {
  
  // create the DSSI UI client
  DSSIUIClient dssi(argc, argv);
  if (!dssi.is_valid()) {
    cerr<<"Could not start OSC listener. You are not running the "<<endl
	<<"program manually, are you? It's supposed to be started by "<<endl
	<<"the plugin host."<<endl;
    return 1;
  }
  
  // allocate shared memory for the ringbuffer
  int shm_size = sizeof(shared_data_t);
  shared_data_t* shared_data = 
    static_cast<shared_data_t*>(dssi.allocate_shared_memory(shm_size));
  Main kit(0, NULL);
  for (int i = 0; i < 1000; ++i) {
    if (dssi.plugin_has_attached())
      break;
    if (i == 999) {
      cerr<<"Waited 10 seconds for the plugin to attach to the shared memory, "
	  <<"nothing "<<endl
	  <<"happened. Exiting now."<<endl;
      return 1;
    }
    Glib::usleep(10000);
  }
  
  // load the GUI
  ScopeWidget scope_widget(shared_data);
  scope_widget.middle_button_pressed.connect(bind(&toggle_controls, 
						  &scope_widget));
  main_win = init_gui(scope_widget);
  if (!main_win) {
    cerr<<"Could not load the GUI from the GLADE file!"<<endl;
    return 1;
  }
  main_win->set_title(dssi.get_identifier());

  // run
  dssi.show_received.connect(mem_fun(*main_win, &Gtk::Window::show));
  dssi.hide_received.connect(mem_fun(*main_win, &Gtk::Window::hide));
  dssi.quit_received.connect(&Main::quit);
  main_win->signal_delete_event().connect(bind_return(hide(&Main::quit), true));
  Main::run();
  
  return 0;
}


Gtk::Window* init_gui(ScopeWidget& scope_canvas) {

  // get everything from the Glade file
  RefPtr<Xml> xml = Xml::create(INSTALL_DIR "/ll-scope/ll-scope.glade");
  Gtk::Window* main_win = 
    dynamic_cast<Gtk::Window*>(xml->get_widget("main_win"));
  Box* canvas_box = dynamic_cast<Box*>(xml->get_widget("canvas_box"));
  SpinButton* trigger_spin = 
    dynamic_cast<SpinButton*>(xml->get_widget("trigger_spin"));
  SpinButton* y1_scale_spin = 
    dynamic_cast<SpinButton*>(xml->get_widget("y1_scale_spin"));
  SpinButton* y2_scale_spin = 
    dynamic_cast<SpinButton*>(xml->get_widget("y2_scale_spin"));
  SpinButton* time_scale_spin = 
    dynamic_cast<SpinButton*>(xml->get_widget("time_scale_spin"));
  Range* y1_offset_range =
    dynamic_cast<Range*>(xml->get_widget("y1_offset_range"));
  Range* y2_offset_range =
    dynamic_cast<Range*>(xml->get_widget("y2_offset_range"));
  Range* x_offset_range =
    dynamic_cast<Range*>(xml->get_widget("x_offset_range"));
  CheckButton* trigger_up =
    dynamic_cast<CheckButton*>(xml->get_widget("trigger_up"));
  if (!(main_win && canvas_box && trigger_spin && y1_scale_spin && 
	y2_scale_spin && time_scale_spin && y1_offset_range && 
	y2_offset_range && x_offset_range && trigger_up))
    return NULL;
  
  // insert the scope widget
  canvas_box->pack_start(scope_canvas);
  scope_canvas.set_size_request(300, 200);
  canvas_box->show_all();
  
  // connect widgets to the scope
  trigger_spin->signal_value_changed().
    connect(compose(mem_fun(scope_canvas, &ScopeWidget::set_trigger_level),
		    mem_fun(*trigger_spin, &SpinButton::get_value)));
  y1_scale_spin->signal_value_changed().
    connect(compose(mem_fun(scope_canvas, &ScopeWidget::set_y1_scale),
		    mem_fun(*y1_scale_spin, &SpinButton::get_value)));
  y2_scale_spin->signal_value_changed().
    connect(compose(mem_fun(scope_canvas, &ScopeWidget::set_y2_scale),
		    mem_fun(*y2_scale_spin, &SpinButton::get_value)));
  time_scale_spin->signal_value_changed().
    connect(compose(mem_fun(scope_canvas, &ScopeWidget::set_time_scale),
		    mem_fun(*time_scale_spin, &SpinButton::get_value)));
  y1_offset_range->signal_value_changed().
    connect(compose(mem_fun(scope_canvas, &ScopeWidget::set_y1_offset),
		    mem_fun(*y1_offset_range, &Range::get_value)));
  y2_offset_range->signal_value_changed().
    connect(compose(mem_fun(scope_canvas, &ScopeWidget::set_y2_offset),
		    mem_fun(*y2_offset_range, &Range::get_value)));
  x_offset_range->signal_value_changed().
    connect(compose(mem_fun(scope_canvas, &ScopeWidget::set_x_offset),
		    mem_fun(*x_offset_range, &Range::get_value)));
  trigger_up->signal_toggled().
    connect(compose(mem_fun(scope_canvas, &ScopeWidget::set_trigger_direction),
		    mem_fun(*trigger_up, &CheckButton::get_active)));
  
  // and set the initial values (from the Glade file)
  scope_canvas.set_trigger_level(trigger_spin->get_value());
  scope_canvas.set_y1_scale(y1_scale_spin->get_value());
  scope_canvas.set_y2_scale(y2_scale_spin->get_value());
  scope_canvas.set_time_scale(time_scale_spin->get_value());
  scope_canvas.set_y1_offset(y1_offset_range->get_value());
  scope_canvas.set_y2_offset(y2_offset_range->get_value());
  scope_canvas.set_x_offset(x_offset_range->get_value());
  scope_canvas.set_trigger_direction(trigger_up->get_active());
    
  return main_win;
}


void toggle_controls(Widget* notoggle) {
  static bool hidden = false;
  
  if (hidden)
    main_win->show_all();
  else {
    main_win->get_child()->hide_all();
    Widget* w = notoggle;
    while (w) {
      w->show();
      w = w->get_parent();
    }
  }
  
  hidden = !hidden;
}
