/*
 * Author: Nick Karanatsios <nickkaranatsios@gmail.com>
 *
 * Copyright (C) 2008-2012 NEC Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation.
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */


#include "trema.h"
#include "ruby.h"


extern VALUE mTrema;
VALUE cFlowRemoved;


/*
 * When a flow is deleted or expired a +OFPT_FLOW_REMOVED+ message is sent as long
 * as the +OFPFF_SEND_FLOW_REM+ bit is toggled in the +flags+ bitmap during
 * flow setup. A user would not explicitly instantiate a {FlowRemoved} object but
 * would be created while parsing the +OPPT_FLOW_REMOVED+ message.
 * Returns an object that encapsulates the +OPPT_FLOW_REMOVED+ OpenFlow message.
 *
 * @overload initialize(options={})
 *   @example 
 *     FlowRemoved.new( 
 *       :datapath_id => 0xabc,
 *       :transaction_id => 0,
 *       :match => Match,
 *       :cookie => 123456789,
 *       :priority => 65535,
 *       :reason => 0,
 *       :duration_sec => 1,
 *       :duration_nsec => 783000000,
 *       :idle_timeout => 1,
 *       :packet_count => 1
 *       :byte_count=> 64
 *     )
 *
 *   @param [Hash] options
 *     the options to create a message with.
 *
 *   @option options [Symbol] :datapath_id
 *     message originator identifier.
 *
 *   @option options [Symbol] :transaction_id
 *     unsolicited message transaction_id is zero.
 *
 *   @option options [Symbol] :match
 *     a {Match} object describing the flow fields copied from the corresponding 
 *     flow setup message.
 *
 *   @option options [Symbol] :cookie
 *     an opaque handle copied from the corresponding 
 *     flow setup message.
 *
 *   @option options [Symbol] :priority
 *     the priority level of the flow copied from the corresponding 
 *     flow setup message.
 *
 *   @option options [Symbol] :reason
 *     the reason why the flow is removed.
 *
 *   @option options [Symbol] :duration_sec
 *     the number of seconds the flow was active.
 *
 *   @option options [Symbol] :duration_nsec
 *     the number of nanoseconds the flow was active.
 *
 *   @option options [Symbol] :idle_timeout
 *     time elapsed in seconds before the flow is removed, copied from the 
 *     corresponding flow setup message.
 *
 *   @option options [Symbol] :packet_count
 *     a counter of the total number of packets.
 *
 *   @option options [Symbol] :byte_count
 *     a counter of the total number of bytes.
 *
 *   @return [FlowRemoved] self
 */
static VALUE
flow_removed_init( VALUE self, VALUE options ) {
  rb_iv_set( self, "@attribute", options );
  return self;
}


/*
 * Message originator identifier.
 *
 * @return [Number] the value of datapath_id.
 */
static VALUE
flow_removed_datapath_id( VALUE self ) {
  return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "datapath_id" ) ) );
}


/*
 * For this asynchronous message the transaction_id is set to zero.
 *
 * @return [Number] the value of transaction_id.
 */
static VALUE
flow_removed_transaction_id( VALUE self ) {
  return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "transaction_id" ) ) );
}


/*
 * Flow fields matched.
 *
 * @return [Match] an object that encapsulates flow fields details.
 */
static VALUE
flow_removed_match( VALUE self ) {
  return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "match" ) ) );
}


/*
 * An opaque handle copied from the corresponding flow setup message.
 *
 * @return [Number] the value of cookie.
 */
static VALUE
flow_removed_cookie( VALUE self ) {
  return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "cookie" ) ) );
}


/*
 * The priority level of the flow copied from the corresponding flow setup
 * message.
 *
 * @return [Number] the value of priority.
 */
static VALUE
flow_removed_priority( VALUE self ) {
  return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "priority" ) ) );
}


/*
 * The reason why the flow is removed.
 *
 * @return [Number] the value of reason.
 */
static VALUE
flow_removed_reason( VALUE self ) {
  return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "reason" ) ) );
}


/*
 * The number of seconds the flow was active.
 *
 * @return [Number] the value of duration_sec.
 */
static VALUE
flow_removed_duration_sec( VALUE self ) {
  return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "duration_sec" ) ) );
}


/*
 * The number of nanoseconds the flow was active.
 *
 * @return [Number] the value of duration_nsec.
 */
static VALUE
flow_removed_duration_nsec( VALUE self ) {
  return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "duration_nsec" ) ) );
}


/*
 * Time elapsed in seconds before the flow is removed.
 *
 * @return [Number] the value of idle_timeout.
 */
static VALUE
flow_removed_idle_timeout( VALUE self ) {
  return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "idle_timeout" ) ) );
}


/*
 * A counter of the total number of packets.
 *
 * @return [Number] the value of packet_count.
 */
static VALUE
flow_removed_packet_count( VALUE self ) {
  return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "packet_count" ) ) );
}


/*
 * A counter of the total number of bytes.
 *
 * @return [Number] the value of byte_count.
 */
static VALUE
flow_removed_byte_count( VALUE self ) {
  return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "byte_count" ) ) );
}


void
Init_flow_removed() {
  cFlowRemoved = rb_define_class_under( mTrema, "FlowRemoved", rb_cObject );
  rb_define_method( cFlowRemoved, "initialize", flow_removed_init, 1 );
  rb_define_method( cFlowRemoved, "datapath_id", flow_removed_datapath_id, 0 );
  rb_define_method( cFlowRemoved, "transaction_id", flow_removed_transaction_id, 0 );
  rb_define_method( cFlowRemoved, "match", flow_removed_match, 0 );
  rb_define_method( cFlowRemoved, "cookie", flow_removed_cookie, 0 );
  rb_define_method( cFlowRemoved, "priority", flow_removed_priority, 0 );
  rb_define_method( cFlowRemoved, "reason", flow_removed_reason, 0 );
  rb_define_method( cFlowRemoved, "duration_sec", flow_removed_duration_sec, 0 );
  rb_define_method( cFlowRemoved, "duration_nsec", flow_removed_duration_nsec, 0 );
  rb_define_method( cFlowRemoved, "idle_timeout", flow_removed_idle_timeout, 0 );
  rb_define_method( cFlowRemoved, "packet_count", flow_removed_packet_count, 0 );
  rb_define_method( cFlowRemoved, "byte_count", flow_removed_byte_count, 0 );
}


void
handle_flow_removed( uint64_t datapath_id, flow_removed message ) {
  VALUE controller = ( VALUE ) message.user_data;
  if ( rb_respond_to( controller, rb_intern( "flow_removed" ) ) == Qfalse ) {
    return;
  }

  VALUE attributes = rb_hash_new();
  rb_hash_aset( attributes, ID2SYM( rb_intern( "datapath_id" ) ), ULL2NUM( datapath_id ) );
  rb_hash_aset( attributes, ID2SYM( rb_intern( "transaction_id" ) ), UINT2NUM( message.transaction_id ) );

  VALUE match_obj = rb_eval_string( "Match.new" );
  rb_funcall( match_obj, rb_intern( "replace" ), 1, Data_Wrap_Struct( cFlowRemoved, NULL, NULL, &message.match ) );

  rb_hash_aset( attributes, ID2SYM( rb_intern( "match" ) ), match_obj );
  rb_hash_aset( attributes, ID2SYM( rb_intern( "cookie" ) ), ULL2NUM( message.cookie ) );
  rb_hash_aset( attributes, ID2SYM( rb_intern( "priority" ) ), UINT2NUM( message.priority ) );
  rb_hash_aset( attributes, ID2SYM( rb_intern( "reason" ) ), UINT2NUM( message.reason ) );
  rb_hash_aset( attributes, ID2SYM( rb_intern( "duration_sec" ) ), UINT2NUM( message.duration_sec ) );
  rb_hash_aset( attributes, ID2SYM( rb_intern( "duration_nsec" ) ), UINT2NUM( message.duration_nsec ) );
  rb_hash_aset( attributes, ID2SYM( rb_intern( "idle_timeout" ) ), UINT2NUM( message.idle_timeout ) );
  rb_hash_aset( attributes, ID2SYM( rb_intern( "packet_count" ) ), ULL2NUM( message.packet_count ) );
  rb_hash_aset( attributes, ID2SYM( rb_intern( "byte_count" ) ), ULL2NUM( message.byte_count ) );

  VALUE r_message = rb_funcall( cFlowRemoved, rb_intern( "new" ), 1, attributes );
  rb_funcall( controller, rb_intern( "flow_removed" ), 2, ULL2NUM( datapath_id ), r_message );
}


/*
 * Local variables:
 * c-basic-offset: 2
 * indent-tabs-mode: nil
 * End:
 */