#include "vrpn_Analog_Output.h"
#include <stdio.h>

vrpn_Analog_Output::vrpn_Analog_Output(const char* name, vrpn_Connection * c) :
	vrpn_BaseClass(name, c),
	o_num_channel(0)
{
   int	i;

   // Call the base class' init routine
   vrpn_BaseClass::init();

   // Set the time to 0 just to have something there.
   o_timestamp.tv_usec = o_timestamp.tv_sec = 0;
   // Initialize the values in the channels, 
   // gets rid of uninitialized memory read error in Purify
   // and makes sure any initial value change gets reported. 
   for (i=0; i< vrpn_CHANNEL_MAX; i++) {
       o_channel[i] = 0;
   }
}

int vrpn_Analog_Output::register_types(void)
{
    request_m_id 
         = d_connection->register_message_type("vrpn_Analog_Output Change_request");
    request_channels_m_id 
         = d_connection->register_message_type("vrpn_Analog_Output Change_Channels_request");
    report_num_channels_m_id
         = d_connection->register_message_type("vrpn_Analog_Output Num_Channels_report");
    got_connection_m_id
	    = d_connection->register_message_type( vrpn_got_connection );
    if (    (request_m_id == -1) 
         || (request_channels_m_id == -1) 
         || (report_num_channels_m_id == -1) 
	    || (got_connection_m_id == -1 ) ) 
    {  return -1;  } 
    else {  return 0;  }
}

void vrpn_Analog_Output::o_print(void ) {
    printf("Analog_Output Report: ");
    for (vrpn_int32 i = 0; i < o_num_channel; i++) {
        //printf("Channel[%d]= %f\t", i, o_channel[i]);
        printf("%f\t", o_channel[i]);
    }
    printf("\n");
}


vrpn_Analog_Output_Server::vrpn_Analog_Output_Server(const char* name, vrpn_Connection * c, 
                                                     vrpn_int32 numChannels) :
	vrpn_Analog_Output(name, c)
{
	this->setNumChannels( numChannels );
	
	// Check if we have a connection
	if (d_connection == NULL) {
		fprintf(stderr, "vrpn_Analog_Output: Can't get connection!\n");
	}
	
	// Register a handler for the request channel change message
	if (register_autodeleted_handler(request_m_id,
		handle_request_message, this, d_sender_id)) {
		fprintf(stderr,"vrpn_Analog_Output_Server: can't register change channel request handler\n");
		d_connection = NULL;
	}
	
	// Register a handler for the request channels change message
	if (register_autodeleted_handler(request_channels_m_id,
		handle_request_channels_message, this, d_sender_id)) {
		fprintf(stderr,"vrpn_Analog_Output_Server: can't register change channels request handler\n");
		d_connection = NULL;
	}

	// Register a handler for vrpn_got_connection, so we can tell the
	// client how many channels are active
	if( register_autodeleted_handler( got_connection_m_id, handle_got_connection, this ) ) {
		fprintf( stderr, "vrpn_Analog_Output_Server: can't register new connection handler\n");
		d_connection = NULL;
	}	
}

// virtual
vrpn_Analog_Output_Server::~vrpn_Analog_Output_Server(void) {}

vrpn_int32 vrpn_Analog_Output_Server::setNumChannels (vrpn_int32 sizeRequested) {
  if (sizeRequested < 0) sizeRequested = 0;
  if (sizeRequested > vrpn_CHANNEL_MAX) sizeRequested = vrpn_CHANNEL_MAX;

  o_num_channel = sizeRequested;

  return o_num_channel;
}

/* static */
int vrpn_Analog_Output_Server::handle_request_message(void *userdata,
	vrpn_HANDLERPARAM p)
{
    const char* bufptr = p.buffer;
    vrpn_int32 chan_num;
    vrpn_int32 pad;
    vrpn_float64 value;
    vrpn_Analog_Output_Server* me = (vrpn_Analog_Output_Server*)userdata;

    // Read the parameters from the buffer
    vrpn_unbuffer(&bufptr, &chan_num);
    vrpn_unbuffer(&bufptr, &pad);
    vrpn_unbuffer(&bufptr, &value);

    // Set the appropriate value, if the channel number is in the
    // range of the ones we have.
    if ( (chan_num < 0) || (chan_num >= me->o_num_channel) ) {
      fprintf(stderr,"vrpn_Analog_Output_Server::handle_request_message(): Index out of bounds\n");
      char msg[1024];
      sprintf( msg, "Error:  (handle_request_message):  channel %d is not active.  Squelching.", chan_num );
      me->send_text_message( msg, p.msg_time, vrpn_TEXT_ERROR );
      return 0;
    }
    me->o_channel[chan_num] = value;

    return 0;
}

/* static */
int vrpn_Analog_Output_Server::handle_request_channels_message(void* userdata,
    vrpn_HANDLERPARAM p)
{
    int i;
    const char* bufptr = p.buffer;
    vrpn_int32 num;
    vrpn_int32 pad;
    vrpn_Analog_Output_Server* me = (vrpn_Analog_Output_Server*)userdata;

    // Read the values from the buffer
    vrpn_unbuffer(&bufptr, &num);
    vrpn_unbuffer(&bufptr, &pad);
    if (num > me->o_num_channel) 
    {
         char msg[1024];
         sprintf( msg, "Error:  (handle_request_channels_message):  channels above %d not active; "
              "bad request up to channel %d.  Squelching.", me->o_num_channel, num );
         me->send_text_message( msg, p.msg_time, vrpn_TEXT_ERROR );
         num = me->o_num_channel;
    }
    if (num < 0) 
    {
         char msg[1024];
         sprintf( msg, "Error:  (handle_request_channels_message):  invalid channel %d.  Squelching.", num );
         me->send_text_message( msg, p.msg_time, vrpn_TEXT_ERROR );
         return 0;
    }
    for (i = 0; i < num; i++) {
         vrpn_unbuffer(&bufptr, &(me->o_channel[i]));
    }
    
    return 0;
}


/* static */
int vrpn_Analog_Output_Server::handle_got_connection( void* userdata, vrpn_HANDLERPARAM )
{
	vrpn_Analog_Output_Server* me = (vrpn_Analog_Output_Server*) userdata;
	if( me->report_num_channels( ) == false )
	{
		fprintf( stderr, "Error:  failed sending active channels to client.\n" );
	}
	return 0;
}


bool vrpn_Analog_Output_Server::report_num_channels( vrpn_uint32 class_of_service )
{
	char msgbuf[ sizeof( vrpn_int32) ];
	vrpn_int32 len = sizeof( vrpn_int32 );;
	
	encode_num_channels_to( msgbuf, this->o_num_channel );
	vrpn_gettimeofday( &o_timestamp, NULL );
	if( d_connection && 
	    d_connection->pack_message( len, o_timestamp,	report_num_channels_m_id, 
							  d_sender_id, msgbuf, class_of_service ) ) 
	{
		fprintf(stderr, "vrpn_Analog_Output_Server (report_num_channels): cannot write message: tossing\n");
		return false;
	}
	return true;
}


vrpn_int32 vrpn_Analog_Output_Server::
encode_num_channels_to( char* buf, vrpn_int32 num )
{
    // Message includes: int32 number of active channels
    int	buflen = sizeof(vrpn_int32);

    vrpn_buffer(&buf, &buflen, num);
    return sizeof(vrpn_int32);
}

vrpn_Analog_Output_Callback_Server::vrpn_Analog_Output_Callback_Server(const char* name, vrpn_Connection * c, 
                                                     vrpn_int32 numChannels) :
	vrpn_Analog_Output_Server(name, c, numChannels)
{
  // Register a handler for the request channel change message.  This will go
  // in the list AFTER the one for the base class, so the values will already
  // have been filled in.  So we just need to call the user-level callbacks
  // and pass them a pointer to the data values.
  if (register_autodeleted_handler(request_m_id,
	  handle_change_message, this, d_sender_id)) {
	  fprintf(stderr,"vrpn_Analog_Output_Callback_Server: can't register change channel request handler\n");
	  d_connection = NULL;
  }

  // Register a handler for the request channels change message.  This will go
  // in the list AFTER the one for the base class, so the values will already
  // have been filled in.  So we just need to call the user-level callbacks
  // and pass them a pointer to the data values.
  if (register_autodeleted_handler(request_channels_m_id,
	  handle_change_message, this, d_sender_id)) {
	  fprintf(stderr,"vrpn_Analog_Output_Callback_Server: can't register change channels request handler\n");
	  d_connection = NULL;
  }
}

// virtual
vrpn_Analog_Output_Callback_Server::~vrpn_Analog_Output_Callback_Server(void) {}

/* static */
// This is inserted into the list AFTER the 
int vrpn_Analog_Output_Callback_Server::handle_change_message(void *userdata,
	vrpn_HANDLERPARAM p)
{
    vrpn_Analog_Output_Callback_Server* me = (vrpn_Analog_Output_Callback_Server*)userdata;
    vrpn_ANALOGOUTPUTCB callback_data;

    callback_data.msg_time = p.msg_time;
    callback_data.num_channel = me->getNumChannels();
    callback_data.channel = me->o_channels();
    me->d_callback_list.call_handlers(callback_data);

    return 0;
}


vrpn_Analog_Output_Remote::vrpn_Analog_Output_Remote(const char* name, vrpn_Connection * c) :
    vrpn_Analog_Output(name, c)
{
    vrpn_int32 i;

    o_num_channel = vrpn_CHANNEL_MAX;
    for (i = 0; i < vrpn_CHANNEL_MAX; i++) {
	o_channel[i] = 0;
    }
    vrpn_gettimeofday(&o_timestamp, NULL);

     // Register a handler for the report number of active channels message
    if (register_autodeleted_handler(report_num_channels_m_id,
		    handle_report_num_channels, this, d_sender_id)) 
    {
	fprintf(stderr,"vrpn_Analog_Output_Remote: can't register active channel report handler\n");
	d_connection = NULL;
    }
}
    
    
// virtual
vrpn_Analog_Output_Remote::~vrpn_Analog_Output_Remote(void) {}
    

void vrpn_Analog_Output_Remote::mainloop()
{
    if (d_connection) {
        d_connection->mainloop();
        client_mainloop();
    }
}


/* static */
int vrpn_Analog_Output_Remote::handle_report_num_channels( void *userdata, vrpn_HANDLERPARAM p )
{
    const char* bufptr = p.buffer;
    vrpn_int32 num;
    vrpn_Analog_Output_Remote* me = (vrpn_Analog_Output_Remote*)userdata;

    // Read the parameters from the buffer
    vrpn_unbuffer(&bufptr, &num);

    if( num >=0 && num <= vrpn_CHANNEL_MAX )
    {
         me->o_num_channel = num;
    }
    else
    {
         fprintf( stderr, "vrpn_Analog_Output_Remote::handle_report_num_channels_message:  "
                          "Someone sent us a bogus number of channels:  %d.\n", num );
    }
    return 0;
}


bool vrpn_Analog_Output_Remote::request_change_channel_value(unsigned int chan, vrpn_float64 val,
						      vrpn_uint32 class_of_service)
{
    // msgbuf must be float64-aligned!
    vrpn_float64 fbuf [2];
    char * msgbuf = (char*) fbuf;

    vrpn_int32  len;

    vrpn_gettimeofday(&o_timestamp, NULL);
    len = encode_change_to(msgbuf, chan, val);
    if (d_connection && d_connection->pack_message(len, o_timestamp,
                                 request_m_id, d_sender_id, msgbuf,
                                 class_of_service)) {
        fprintf(stderr,"vrpn_Analog_Output_Remote: cannot write message: tossing\n");
        return false;
    }

    return true;
}

bool vrpn_Analog_Output_Remote::request_change_channels(int num, vrpn_float64* vals, vrpn_uint32 class_of_service)
{
    if (num < 0 || num > vrpn_CHANNEL_MAX) {
      fprintf(stderr, "vrpn_Analog_Output_Remote: cannot change channels: number of channels out of range\n");
      return false;
    }

    vrpn_float64 fbuf[ 1 + vrpn_CHANNEL_MAX ];
    char* msgbuf = (char*) fbuf;
    vrpn_int32 len;
    
    vrpn_gettimeofday(&o_timestamp, NULL);
    len = encode_change_channels_to(msgbuf, num, vals);
    if( d_connection && 
	d_connection->pack_message( len, o_timestamp,
                                 request_channels_m_id, d_sender_id, msgbuf, 
                                 class_of_service ) ) 
    {
      fprintf(stderr, "vrpn_Analog_Output_Remote: cannot write message: tossing\n");
      return false;
    }
    return true;
}

vrpn_int32 vrpn_Analog_Output_Remote::encode_change_to(char *buf, vrpn_int32 chan, vrpn_float64 val)
{
    // Message includes: int32 channel_number, int32 padding, float64 request_value
    // Byte order of each needs to be reversed to match network standard

    vrpn_int32 pad = 0;
    int	buflen = 2 * sizeof(vrpn_int32) + sizeof(vrpn_float64);

    vrpn_buffer(&buf, &buflen, chan);
    vrpn_buffer(&buf, &buflen, pad);
    vrpn_buffer(&buf, &buflen, val);

    return 2 * sizeof(vrpn_int32) + sizeof(vrpn_float64);
}

vrpn_int32 vrpn_Analog_Output_Remote::encode_change_channels_to(char* buf, vrpn_int32 num, vrpn_float64* vals) 
{
    int i;
	vrpn_int32 pad = 0;
    int buflen = 2 * sizeof(vrpn_int32) + num * sizeof(vrpn_float64);

	vrpn_buffer(&buf, &buflen, num);
	vrpn_buffer(&buf, &buflen, pad);
    for (i = 0; i < num; i++) {
        vrpn_buffer(&buf, &buflen, vals[i]);
    }

    return 2 * sizeof(vrpn_int32) + num * sizeof(vrpn_float64);
}
