/*  -*- LPC -*-  */
/*
 * $Locker:  $
 * $Id: state_change.c,v 1.4 2002/09/30 14:07:19 terano Exp $
 * $Log: state_change.c,v $
 * Revision 1.4  2002/09/30 14:07:19  terano
 * Added some extra functionality in the form of a third arg to transform.
 *
 * Revision 1.3  2002/09/19 19:51:39  tilly
 * Added a check for collective objects along with the continuous, as collective obs were getting fucked up amounts set.
 *
 * Revision 1.2  1998/11/14 05:00:52  jeremy
 * Added autodoc stuff.
 *
 * Revision 1.1  1998/01/06 05:03:33  ceres
 * Initial revision
 * 
 */
/**
 * This handler is meant to simulate the changing of an object's
 * "state", such as melting, grinding, etc.  In reality, rather than
 * changing the source object, the transform() method clones a
 * completely new object, based on the internal lookup table; it is
 * the caller's responsibility to dest the source object and put the
 * new one in its place.
 * <p>
 * The handler's lookup table is stored in a series of files:
 * any files in /obj/state_change ending in .dat will be added
 * to the table when the handler is loaded.  See /include/state_change.h
 * for field definitions.  The data files are loaded by the data
 * handler, and can contain fancy things like expressions and function
 * pointers.
 * @author Jeremy
 * @see /include/state_change.h
 * @see /obj/handlers/data.c
 */
#include <state_change.h>
#define PROP "state name"
// I can't think of anyplace good to put this
#define DATA_DIR "/obj/state_change/"
mapping table = ([ ]);
void set_amount_of_ob( object ob, int amount );
/**
 * This method returns the entire lookup table.  It is mainly for
 * debugging.
 * @return the state_change lookup table
 */
mixed query_table() { return table; }
/** @ignore yes */
void create() {
    string *files;
    int i;
    seteuid("/secure/master"->creator_file(file_name(this_object())));
    files = ({ "base.hdr" });
    files += get_dir(DATA_DIR + "*.dat");
    for (i = 0; i < sizeof(files); i++) {
        files[i] = DATA_DIR + files[i];
    }
    table += "/obj/handlers/data"->compile_data(files);
} /* create() */
// TODO: this should be able to clone and return an array of objects.
// TODO: being able to pass in an argument for the func field would be nice.
/**
 * This method is generally called by the object doing the state
 * change (e.g., a food grinder).  It looks up the specified source
 * object in the table and clones the "transformed" object as specified.
 * <p>
 * The table is indexed by the source object as follows: if the source
 * object has a property "state name", the value of this is used.  If
 * not, the object's medium alias (if any, regardless of whether the
 * object is continuous) is used.  Failing these, the object's short
 * description is used.  The first one resulting in a valid string is
 * concatenated with the transformation string (with a ":" between)
 * and the result is used as the index.  If the lookup fails, 0 is
 * returned.
 * <p>
 * Note that the resulting object is only cloned.  It is the responsibility
 * of the caller to dest the source and put the result in its place.
 * @param ob the source object
 * @param transformation a string specifying the transformation (e.g., "grind")
 * @param The player to whom this state change will be attributed to! [optional]
 * @return the result of the transformation */
varargs object transform( object ob, string transformation, object player ) {
    string index, name;
    object ret;
    class state_change c;
    function f;
    int *weight_unit;
    // First try a property
    if (!stringp(name = ob->query_property(PROP)))
      // Next see if it has a medium alias (this would be the norm)
      if (!stringp(name = ob->query_medium_alias()))
        // Finally, use the short
        name = ob->short();
    if ( !player )
      player = this_player();
    
    index = transformation + ":" + name;
    if (undefinedp(table[index]))
      return 0;
    c = table[index];
    ret = clone_object(c->result);
    foreach (f in c->func)
      evaluate(f, ob, ret, player);
    if (ret->query_continuous()) {
        if ( ob->query_continuous() || ob->query_collective() ) {
            ret->set_amount(to_int(ob->query_amount() * c->factor));
        } else {
            weight_unit = ret->query_weight_unit();
            set_amount_of_ob( ret, to_int( ob->query_weight() * c->factor *
              weight_unit[1] / weight_unit[0] ) );
        }
    } else {
        if ( ob->query_continuous() || ob->query_collective() ) {
            weight_unit = ob->query_weight_unit();
            ret->set_weight(to_int(ob->query_amount() * c->factor *
                                   weight_unit[0] / weight_unit[1]));
        } else {
            ret->set_weight(to_int(ob->query_weight() * c->factor));
        }
    }
    log_file( "STATE_CHANGE", "Changed %d amount of %s to %d amount of %s via %s.\n", 
      ob->query_amount(), ob->query_medium_alias(), ret->query_amount(), 
      ret->query_medium_alias(), transformation );
    return ret;
} /* transform() */
/**
 * @ignore
 * Helper function to ensure 0 amount cont obs are destroyed.
 */
void set_amount_of_ob( object ob, int amount ) {
  if ( !ob ) 
      return;
  if ( !amount )
      ob->dest_me();
  ob->set_amount( amount );
  return;
}