/*
 * Example LWO using operator overloading
 *
 * August 2015 by Aidil @ Way of the Force
 * aidil@wotf.org
 *
 * This example implements a variation on mappings.
 * Mappings on DGD don't implement the &, | or -
 * operators, this varation does.
 * 
 * This code is in the public domain, no rights reserved.
 *
 * v1.01: Aidil: fixed range operator when x and/or y not specified,
 *               also made the or operator aware of the difference 
 *               between 0 and nil
 */

#include <type.h>

static mapping data;
static object bp;

static void create() {
   string n;

   if (!data) {
      data = ([ ]);
   }
   /* find the blueprint object for this LWO */
   sscanf( object_name( this_object() ), "%s#-1", n );
   bp = find_object( n );
}

/* operators */
static mixed operator [] (mixed idx) {
   return data[idx];
}

static void operator []= (mixed idx, mixed value) {
   data[idx] = value;
}

static object operator [..] (mixed x, mixed y) {
   object target;

   target = new_object( bp );
   if (x == nil && y == nil) {
      target->i_set_data( data[..] );
   } else if (x == nil) {
      target->i_set_data( data[..y] );
   } else if (y == nil) {
      target->i_set_data( data[x..] );
   } else {
      target->i_set_data( data[x..y] );
   }
   return target;
}

/*
 * key/value pairs are copied to the new mapping when
 * their key appears in both the left and rightside
 * argument to &. Values are always taken from the leftside
 * argument.
 */

static object operator & (mixed rval) {
   mixed *idx;
   object target;
   int i, sz;

   switch( typeof( rval ) ) {
      case T_OBJECT :
         rval = rval->i_get_data();
         break;
      case T_MAPPING :
         break;
      default :
         error( "Bad argument 2 to operator &" );
         break;
   }

   idx = map_indices( data ) & map_indices( rval );
   target = new_object( bp );
   for (i = 0, sz = sizeof( idx ); i < sz; i++) {
      target[idx[i]] = data[idx[i]];
   }
   return target;
}

/*
 * Very similar to the + operator, but with a subtle difference:
 * + will use the righthand side value for keys appearing in both
 * mappings, whereas | will use the lefthand side value (in other
 * words, | will only copy key/value pairs from the righthand side
 * when their key does not appear on the lefthand side)
 */

static object operator | (mixed rval) {
   mixed *idx;
   object target;
   int i, sz;

   switch( typeof( rval ) ) {
      case T_OBJECT :
         rval = rval->i_get_data();
         break;
      case T_MAPPING :
         break;
      default :
         error( "Bad argument 2 to operator &" );
         break;
   }

   idx = map_indices( data ) | map_indices( rval );
   target = new_object( bp );
   for (i = 0, sz = sizeof( idx ); i < sz; i++) {
      if (data[idx[i]] != nil) {
         target[idx[i]] = data[idx[i]];
      } else {
         target[idx[i]] = rval[idx[i]];
      }
   }
   return target;
}

static object operator + (mixed rval) {
   object target;

   switch( typeof( rval ) ) {
      case T_OBJECT :
         rval = rval->i_get_data();
         break;
      case T_MAPPING :
         break;
      default :
         error( "Bad argument 2 to operator &" );
         break;
   }

   target = new_object( bp );

   target->i_set_data( data + rval );
   return target;
}

/*
 * remove key/value pairs for keys present in the
 * righthand side argument
 */
static object operator - (mixed rval) {
   object target;
   int i, sz;
   mixed *idx;

   switch( typeof( rval ) ) {
      case T_OBJECT :
         rval = rval->i_get_data();
         break;
      case T_MAPPING :
         break;
      default :
         error( "Bad argument 2 to operator &" );
         break;
   }

   idx = map_indices( data ) - map_indices( rval );
   target = new_object( bp );
   for (i = 0, sz = sizeof( idx ); i < sz; i++) {
      target[idx[i]] = data[idx[i]];
   }
   return target;
}

mapping i_get_data() {
   return data;
}

void i_set_data( mapping d ) {
   data = d;
}

mixed *i_get_indices() {
   return map_indices( data );
}

mixed *i_get_values() {
   return map_values( data );
}
