// data structure routines (vocab, dstroy, juggle, move, put, carry, drop);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "adv_struc.h"    // function prototypes

// LOOK UP ID IN THE VOCABULARY (ATAB) AND RETURN ITS "DEFINITION" (KTAB), OR
// -1 IF NOT FOUND.  IF INIT IS POSITIVE, THIS IS AN INITIALISATION CALL SETTING
// UP A KEYWORD VARIABLE, AND NOT FINDING IT CONSTITUTES A BUG.  IT ALSO MEANS
// THAT ONLY KTAB VALUES WHICH TAKEN OVER (div by) 1000 EQUAL INIT MAY BE CONSIDERED.
// (THUS "STEPS", WHICH IS A MOTION VERB AS WELL AS AN OBJECT, MAY BE LOCATED
// AS AN OBJECT.)  AND IT ALSO MEANS THE KTAB VALUE IS TAKEN MOD 1000.
// **************************************************************************
// looks like maybe "gm.atab" and "theword" should be char[] or char *???
//   this is some of that "5 chars per word" horse hocgm.key!
// OK, char [6] - 5 chars plus the gm.null terminator
int vocab(char *theword, int icode)
{
      int i;
      char hash[8];

      // figure out the hash function later, settle for the easy way now
      // hash=theword.xor."phrog";
      strcpy(hash,theword);        // this should work temporarily

      for (i=1;i<=gm.tabndx && gm.ktab[i]!=-1;i++)
      {
         if ( ((icode < 0) || (gm.ktab[i]/1000 == icode)) && 
              (0 == strcmp(gm.atab[i],hash)) )
             return (gm.ktab[i]);
      }  // end  for (i=1;i<=gm.tabndx && gm.ktab[i]!=-1;i++)
      // if we drop through, the word is unknown for the given prefix
      //   but let's just tell the caller, not crash - caller might want to try
      //   a different category (like several kinds of gm.verb but NOT gm.object)
      return (-1);
}  // end vocab --------------------------------------------------------------

// PERMANENTLY ELIMINATE "OBJECT" BY MOVING TO A NON-EXISTENT LOCATION.
void dstroy(int object)
{
      move(object,NOWHERE);
}  // end dstroy --------------------------------------------------------------

// JUGGLE AN OBJECT BY PICKING IT UP AND PUTTING IT DOWN AGAIN, THE PURPOSE
// BEING TO GET THE OBJECT TO THE FRONT OF THE CHAIN OF THINGS AT ITS LOC.
void juggle(int object)
{
      int i, j;

      i = gm.place[object];
      j = gm.fixed[object];
      move(object,i);
      move(object+100,j);
}  // end juggle --------------------------------------------------------------

// PLACE ANY OBJECT ANYWHERE BY PICKING IT UP AND DROPPING IT.  MAY ALREADY BE
// TOTING, IN WHICH CASE THE CARRY IS A NO-OP.  MUSTN"T PICK UP OBJECTS WHICH
// ARE NOT AT ANY LOC, SINCE CARRY WANTS TO REMOVE OBJECTS FROM ATLOC CHAINS.
void move(int object, int where)
{
      int from;

      // gm.find out where object really is
      if (object <= 100)
          from = gm.place[object];
      else
          from = gm.fixed[object-100];

      if ((from > 0) && (from <= 300))    // if not already carrying, pick up
          carry(object,from);
      drop(object,where);            // now, drop it at gm.destination
}  // end move --------------------------------------------------------------------

// PUT IS THE SAME AS MOVE, EXCEPT IT RETURNS A VALUE USED TO SET UP THE
// NEGATED PROP VALUES FOR THE REPOSITORY OBJECTS.
int put(int object, int where, int pval)
{
      move(object,where);
      return ((-1)-pval);
}  // end put ---------------------------------------------------------------

// START TOTING AN OBJECT, REMOVING IT FROM THE LIST OF THINGS AT ITS FORMER
// LOCATION.  INCR HOLDNG UNLESS IT WAS ALREADY BEING TOTED.  IF OBJECT>100
// (MOVING "FIXED" SECOND LOC), DON"T CHANGE PLACE OR HOLDNG.
void carry(int object, int where)
{
      int temp;

      // printf("Carry: object = %i, loc = %i, 1st atloc = %i\n",object,where,gm.atloc[where]);

      if (object <= 100)           // normal object
      {
          if(gm.place[object] == -1)
              return;              // already carrying - nothing to do
          gm.place[object] = CARRIED; // special location "carried"
          gm.holdng++;
      }

      if (gm.atloc[where] == object)  // gm.obj is 1st on list for loc
          gm.atloc[where] = gm.link[object];  // now NEXT is 1st on list
      else
      {
          temp = gm.atloc[where];         // gm.obj NOT 1st, prepare to scan list
          while (gm.link[temp] != object) // step thru until NEXT is desired gm.obj
              temp = gm.link[temp];
          gm.link[temp] = gm.link[object];   // gm.link from PREV to FOLLOWIN
      }
}  // end carry -------------------------------------------------------------

// PLACE AN OBJECT AT A GIVEN LOC, PREFIXING IT ONTO THE ATLOC LIST.  DECR
// HOLDNG IF THE OBJECT WAS BEING TOTED.
void drop(int object, int where)
{
      if(object <= 100)            // "normal" object
      {
          if(gm.place[object] == CARRIED)
              gm.holdng--;            // if carrying, decrement count objects carried
          gm.place[object] = where;   // put object in current loc
      }
      else
          gm.fixed[object-100] = where; // special "gm.fixed" object

      if (where > 0)               // if real location (not carried, etc.)
      {
          // gm.place object 1st in list of objects at location
          gm.link[object] = gm.atloc[where];
          gm.atloc[where] = object;
      }
}  // end drop --------------------------------------------------------------------

