/*
   Copyright (C) 2004 Chris Emerson
   (Based on the push_button Copyright (C) 2002 Carlos Ghirardelli)

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.  */

/*

  encoder.cc


*/

/* IN_MODULE should be defined for modules */
#define IN_MODULE

#include <time.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <errno.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <assert.h>

#include "../config.h"    // get the definition for HAVE_GUI
#ifdef HAVE_GUI

#include <gtk/gtk.h>
#include "../src/gpsim_time.h"
#include "../src/packages.h"

#include "encoder.h"

#define PIN_A	(0x01)
#define PIN_B	(0x02)

//--------------------------------------------------------------
// create_iopin_map 
//
//  This is where the information for the Module's package is defined.
// Specifically, the I/O pins of the module are created.

void Encoder::create_iopin_map(void)
{


  // Define the physical package.
  //   The Package class, which is a parent of all of the modules,
  //   is responsible for allocating memory for the I/O pins.
  //


  create_pkg(2);


  // Define the I/O pins and assign them to the package.
  //   There are two things happening here. First, there is
  //   a new I/O pin that is being created. For the binary
  //   indicator, both pins are inputs. The second thing is
  //   that the pins are "assigned" to the package. If we
  //   need to reference these newly created I/O pins (like
  //   below) then we can call the member function 'get_pin'.

  a_pin = new IO_bi_directional((name() + ".a").c_str());
  assign_pin(1, a_pin);
  package->set_pin_position(1,(float)0.0);
  b_pin = new IO_bi_directional((name() + ".b").c_str());
  assign_pin(1, b_pin);
  package->set_pin_position(2,(float)0.9999);

  // Create an entry in the symbol table for the new I/O pins.
  // This is how the pins are accessed at the higher levels (like
  // in the CLI).

  if(a_pin) {
    get_symbol_table().add_stimulus(a_pin);
    a_pin->update_direction(1,true);
    if(a_pin->snode)
      a_pin->snode->update();
  }
  if(b_pin) {
    get_symbol_table().add_stimulus(b_pin);
    b_pin->update_direction(1,true);
    if(b_pin->snode)
      b_pin->snode->update();
  }
}

//--------------------------------------------------------------
// GUI
static void
cw_cb (GtkButton *button, Encoder *enc)

{
    enc->send_cw();
}

static void
ccw_cb (GtkButton *button, Encoder *enc)

{
    enc->send_ccw();
}

void Encoder::create_widget(Encoder *enc)
{

  GtkWidget	*box1;
  GtkWidget	*buttonl, *buttonr;
	
  box1 = gtk_hbox_new (FALSE, 0);

  buttonl = gtk_button_new_with_label ("ccw");
  buttonr = gtk_button_new_with_label (" cw");
  gtk_container_set_border_width (GTK_CONTAINER (buttonl), 5);
  gtk_container_set_border_width (GTK_CONTAINER (buttonr), 5);

  gtk_signal_connect (GTK_OBJECT (buttonl), "pressed",			    
		      GTK_SIGNAL_FUNC (ccw_cb), (gpointer)enc);
  gtk_signal_connect (GTK_OBJECT (buttonr), "pressed",			    
		      GTK_SIGNAL_FUNC (cw_cb), (gpointer)enc);
	
		
  gtk_widget_show(buttonl);
  gtk_widget_show(buttonr);
  gtk_box_pack_start (GTK_BOX (box1), buttonl, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (box1), buttonr, FALSE, FALSE, 0);
	
	
  // Tell gpsim which widget to use in breadboard.
  enc->set_widget(box1);
}

//--------------------------------------------------------------
// construct
Module * Encoder::construct(const char *_new_name=NULL)
{

  Encoder *enc_p = new Encoder ;
  enc_p->new_name(_new_name);
  enc_p->create_iopin_map();

  enc_p->create_widget(enc_p);

  return enc_p;

}

Encoder::Encoder(void)
    : rs(rot_detent)
{
  name_str = strdup("Encoder");
}

Encoder::~Encoder(void)
{
  delete a_pin;
  delete b_pin;
}

void
Encoder::send_cw(void)
{
    switch (rs) {
	case rot_detent:
	    rs = rot_moving_cw;
	    toggle_a(); /* A toggles before B going clockwise */
	    schedule_tick();
	    break;
	default:
	    // XXX: ought to do something here as well
	    break;
    }
}

void
Encoder::send_ccw(void)
{
    switch (rs) {
	case rot_detent:
	    rs = rot_moving_ccw;
	    toggle_b();
	    schedule_tick();
	    break;
	default:
	    // XXX: ought to do something here as well
	    break;
    }
}

void
Encoder::toggle_a()
{
  a_pin->toggle();
}

void
Encoder::toggle_b()
{
  b_pin->toggle();
}

void
Encoder::schedule_tick()
{
    /* XXX: make the time delay configurable */
    if (!get_cycles().set_break_delta(100, this)) {
	std::cerr << "Encoder: error setting breakpoint." << std::endl;
    }
}

void
Encoder::callback()
{
    switch (rs) {
	case rot_detent:
	    assert(false);
	    break;
	case rot_moving_cw:
	    toggle_b();
	    rs = rot_detent;
	    break;
	case rot_moving_ccw:
	    toggle_a();
	    rs = rot_detent;
	    break;
	default:
	    abort();
    }
}

#endif // HAVE_GUI


syntax highlighted by Code2HTML, v. 0.9.1