/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "glk/jacl/jacl.h"
#include "glk/jacl/language.h"
#include "glk/jacl/types.h"
#include "glk/jacl/prototypes.h"

namespace Glk {
namespace JACL {

#define FIRST_LIST      noun_number-2
#define SECOND_LIST     noun_number

int                             object_list[4][MAX_OBJECTS];
int                             multiple_resolved[MAX_OBJECTS];

// THIS IS THE NUMBER OF OBJECTS LEFT IN THE LIST
int                             list_size[4];

// THIS IS THE INDEX OF THE FINAL OBJECT
int                             max_size[4];

/* object_list[] HAS THE FOLLOWING INDEXES:
 * noun1            : 0
 * noun2            : 1
 * noun1 EXCEPTIONS : 2
 * noun2 EXCEPTIONS : 3
 */

int                             selection;

int                             matches = 0;
int                             highest_confidence = 0;
int                             prime_suspect = 0;
int                             done = 0;
int                             backup_pointer = 0;
int                             everything = 0;

int                             confidence[MAX_OBJECTS];
int                             possible_objects[MAX_OBJECTS];

int                             it;
int                             them[MAX_OBJECTS];
int                             her;
int                             him;
int                             parent;

int                             custom_error;

int                             oops_word;
int                             last_exact;

char                            *expected_scope[3];

// THIS ARRAY DEFINES THE OBJECTS THAT THE CURRENT OBJECTS ARE
// SUPPOSED TO BE A CHILD OF
int                             from_objects[MAX_OBJECTS];

int                             after_from;
const char                      *from_word;

int                             object_expected = FALSE;

char                            default_function[84];
char                            object_name[84];

char                            base_function[84];
char                            before_function[84];
char                            after_function[84];
char                            local_after_function[84];

extern char                     text_buffer[];
extern char                     function_name[];
extern char                     temp_buffer[];
extern char                     error_buffer[];
extern char                     override_[];
extern const char               *word[];

extern int                      quoted[];

extern struct object_type       *object[];
extern int                      objects;

extern int                      noun[];
extern int                      wp;
extern int                      player;

extern struct word_type         *grammar_table;
extern struct function_type     *executing_function;
extern struct object_type       *object[];
extern struct variable_type     *variable[];

void parser() {
	// THIS FUNCTION COMPARES THE WORDS IN THE PLAYER'S COMMAND TO THE
	// GRAMMAR TREE OF POSSIBLE COMMANDS

	struct word_type *pointer;
	struct word_type *matched_word = nullptr;

	int             index;
	int             current_noun = 0;

	// RESET TO START OF PROCESSING

	////printf("--- in parser\n");

	// THIS IS USED TO STORE THE LAST EXACT WORD MATCH IN THE PLAYERS COMMAND
	last_exact = -1;
	after_from = -1;
	from_objects[0] = 0;

	noun[0] = FALSE;
	noun[1] = FALSE;

	// RESET BOTH THE LISTS TO BE EMPTY
	//printf("--- reset lists\n");
	for (index = 0; index < 4; index++) {
		list_size[index] = 0;
		max_size[index] = 0;
	}

	//printf("--- clear $integer\n");
	clear_cinteger("$integer");
	//printf("--- clear $string\n");
	clear_cstring("$string");
	//printf("--- clear action\n");
	clear_cstring("action");

	if (grammar_table == nullptr) {
		// THERE ARE NO USER DEFINED COMMANDS AVAILABLE, SO THE USER'S
		// COMMAND IS INEVITABLY INVALID
		//printf("--- no grammar table\n");
		INTERRUPTED->value = TRUE;
		diagnose();
		return;
	}

	//printf("--- set pointer to grammar table\n");
	// START AT THE TOP OF THE GRAMMAR TREE
	pointer = grammar_table;

	while (word[wp] != nullptr && pointer != nullptr) {
		//printf("--- wp = %d\n", wp);
		//printf("--- word[%d] = %s\n", wp, word[wp]);
		object_expected = FALSE;

		if (!strcmp(cstring_resolve("THEN_WORD")->value, word[wp])) {
			// CONSIDER THIS THE END OF THIS COMMAND AS 'THEN' IS
			// TREATED LIKE A FULL STOP
			break;
		} else if ((matched_word = exact_match(pointer)) != nullptr) {
			// THIS WORD WAS AN EXACT MATCH FOR ONE OF THE POSSIBLE WORDS
			// AT THE CURRENT GRAMMAR TREE LEVEL - MOVE ON!
			pointer = matched_word;
			pointer = pointer->first_child;
		} else if ((matched_word = object_match(pointer, current_noun)) != nullptr) {
			// THIS WAS AN OBJECT PLACE HOLDER AT THIS GRAMMAR LEVEL AND
			// THIS POINT IN THE PLAYER'S COMMAND COULD BE RESOLVED TO
			// AT LEAST ONE OBJECT

			if (list_size[current_noun] > 1) {
				// MULTIPLE OBJECTS WERE RETURNED
				if (matched_word->word[1] != '*') {
					if (last_exact == -1) {
						write_text(cstring_resolve("NO_MULTI_START")->value);
					} else {
						sprintf(error_buffer, cstring_resolve("NO_MULTI_VERB")->value, word[last_exact]);
						write_text(error_buffer);
					}

					INTERRUPTED->value = TRUE;
					return;
				}
			}

			// STORE THE EXPECTED SCOPE FOR LATER CHECKING
			expected_scope[current_noun] = (char *)&matched_word->word;
			//printf("--- expected scope for noun%d is %s\n", current_noun, expected_scope[current_noun]);

			// THE NEXT MATCH OR GROUP OF MATCHES SHOULD BE IN THE SECOND
			// LIST OF OBJECTS
			current_noun++;

			// PUSH ON FROM THE POINT A MATCH WAS FOUND....
			pointer = matched_word;
			pointer = pointer->first_child;
		} else {
			// THIS IS AN UNKNOWN WORD
			if (oops_word == -1 && word[wp] != nullptr) {
				oops_word = wp;
			}

			if (custom_error == TRUE) {
				// THERE HAS BEEN SOME CUSTOM ERROR DISPLAYED ALREADY
				// SO JUST RETURN
				TIME->value = FALSE;
				INTERRUPTED->value = TRUE;
				return;
			} else {
				// THERE ARE NO MORE POSIBILITIES, THE WORD CAN'T BE
				// USED IN THIS CONTEXT
				INTERRUPTED->value = TRUE;
				diagnose();
				return;
			}
		}
	};

	if (pointer == nullptr) {
		// THIS CAN ONLY HAPPEN IF MOVING THE POINTER TO ITS
		// FIRST CHILD RESULTS IN A NULL - AN INCOMPLETE
		// GRAMMAR STATEMENT.
		log_error(INCOMPLETE_GRAMMAR, PLUS_STDOUT);
		INTERRUPTED->value = TRUE;
		return;
	}

	//printf("--- first list has %d objects\n", list_size[0]);
	//printf("--- first list has %d max\n", max_size[0]);
	//printf("--- second list has %d objects\n", list_size[1]);
	//printf("--- second list has %d max\n", max_size[1]);

	/* THE PLAYER'S MOVE HAS NO MORE WORDS, AND A BRANCH OF
	 * THE GRAMMAR TREE HAS BEEN FOUND THAT SO FAR MATCHES
	 * THIS MOVE. SCAN THROUGH ALL THE CHILDREN OF THE FINAL
	 * WORD OF THE PLAYER'S COMMAND AND FIND A FUNCTION BASE.
	 * (FUNCTION BASES BEGIN WITH '>'). IF THIS IS FOUND,
	 * CALL THE APPROPRIATE JACL FUNCTIONS IN THE GAME CODE
	 * IF NO FUNCTION BASE IS FOUND, MORE WORDS WERE EXPECTED
	 * IN ORDER TO CONSTRUCT A COMPLETE COMMAND. SHOW ERROR. */
	do {
		if (pointer->word[0] == '>') {
			//printf("--- found action %s\n", pointer->word);
			/* CALL ALL THE APPROPRIATE FUNCTIONS FOR EACH OF */
			/* THE OBJECTS IN THE SET */
			add_cstring("action", &pointer->word[1]);

			if (list_size[0] > 1) {
				/* FIRST IS MULTI, PRESUME SECOND IS SINGLE OR ZERO AS YOU
				 * CAN HAVE COMMANDS WITH TWO MULTIPLE OBJECT REFERENCES */
				noun[1] = first_available(1);

				for (index = 0; index < max_size[0]; index++) {
					/* CALL ALL THE FUNCTIONS ONCE FOR EACH OJECT */
					if (object_list[0][index] != 0) {
						noun[0] = object_list[0][index];

						if (MULTI_PREFIX->value) {
							// DISPLAY THE OBJECT BEING ACTED ON
							plain_output(noun[0], FALSE);
							write_text(temp_buffer);
							write_text(": ");
						}

						//printf("--- 1 calling functions for %s\n", object[noun[0]]->label);
						call_functions(pointer->word);

						/* IF INTERRUPTED BY SOME SPECIAL CONDITION, DON'T
						 * PERFORM THIS COMMAND FOR THE REMAINING OBJECTS */
						if (INTERRUPTED->value) {
							break;
						}
					}
				}
			} else {
				// FIRST LIST ISN'T MULTI
				//printf("--- first list isn't multi\n");
				if (list_size[1] > 1) {
					//printf("--- second list is multi\n");
					// ONLY SECOND IS MULTI
					noun[0] = first_available(0);

					for (index = 0; index < max_size[1]; index++) {
						/* CALL ALL THE FUNCTIONS ONCE FOR EACH OJECT */
						if (object_list[1][index] != 0) {
							noun[1] = object_list[1][index];

							if (MULTI_PREFIX->value) {
								// DISPLAY THE OBJECT BEING ACTED ON
								plain_output(noun[0], FALSE);
								write_text(temp_buffer);
								write_text(": ");
							}

							//printf("--- 2 calling functions for %s\n", object[noun[1]]->label);
							call_functions(pointer->word);

							/* IF INTERRUPTED BY SOME SPECIAL CONDITION, DON'T
							 * PERFORM THIS COMMAND FOR THE REMAINING OBJECTS */
							if (INTERRUPTED->value) {
								break;
							}
						}
					}
				} else {
					//printf("--- second list isn't multi\n");
					// NEITHER OBJECT REFERENCE IS MULTI
					if (list_size[0] == 0) {
						noun[0] = 0;
						noun[1] = 0;

						/* THIS IS AN OBJECT-LESS COMMAND */
						call_functions(pointer->word);
					} else {
						noun[0] = first_available(0);
						noun[1] = first_available(1);

						//printf("--- 3 calling functions for %s and %s\n", object[noun[0]]->label, object[noun[1]]->label);
						call_functions(pointer->word);
					}
				}
			}
			if (TIME->value == FALSE) {
				/* IS THIS ENOUGH TO INDICATE THAT THE OTHER COMMANDS
				 * SHOULDN'T BE PERFORMED??? */
				//INTERRUPTED->value = TRUE;
			}
			return;
		} else {
			//printf("--- move to next grammar sibling\n");
			// MOVE THROUGH THE OPTIONS AT THIS LEVEL OF THE GRAMMAR TREE
			// TO FIND THE ACTION THAT MATCHES THIS COMMAND
			if (pointer->next_sibling == nullptr) {
				break;
			} else {
				pointer = pointer->next_sibling;
			}
		}
	} while (TRUE);

	/* THERE IS NO FUNCTION AS A CHILD OF THE LAST WORD OF THE PLAYER'S
	 * COMMAND SO DISPLAY AN IN-GAME ERROR TO THE PLAYER. THIS IS LIKELY
	 * TO BE AN INCOMPLETE COMMAND */
	INTERRUPTED->value = TRUE;
	diagnose();
	return;
}

/* THIS FUNCTION RETURNS THE FIRST OBJECT IN THE SPECIFIED LIST
 * OR 0 IF IT IS EMPTY. THE FIRST OBJECT IN THE LIST IS THE FIRST
 * ELEMENT THAT IS NON-ZERO BEFORE THE max-size IS REACHED. */
int first_available(int list_number) {
	int index;

	if (list_size[list_number] == 0) return (0);

	//printf("--- looking for next object in list\n");
	for (index = 0; index < max_size[list_number]; index++) {
		if (object_list[list_number][index] != 0) {
			//printf("--- returning object %s\n", object[object_list[list_number][index]]->label);
			return (object_list[list_number][index]);
		}
	}

	//printf("--- no objects left in list\n");
	/* NO OBJECTS LEFT IN THE LIST */
	return (0);
}

void call_functions(const char *base_name) {
	/* THIS FUNCTION CALLS ALL THE APPROPRIATE JACL FUNCTIONS TO RESPOND
	 * TO A PLAYER'S COMMAND GIVEN A BASE FUNCTION NAME AND THE CURRENT
	 * VALUE OF noun1 AND noun2 */

	/* THE DEFAULT IS THAT THE COMMAND IS SUCCESSFUL AND THAT TIME SHOULD
	 * PASS. IF THE COMMAND FAILS, 'TIME' WILL BE SET TO FALSE */
	TIME->value = TRUE;

	strncpy(base_function, base_name + 1, 80);
	strcat(base_function, "_");

	strncpy(override_, base_function, 80);

	strcpy(before_function, "+before_");
	strcat(before_function, base_name + 1);

	strcpy(after_function, "+after_");
	strcat(after_function, base_name + 1);

	strcpy(local_after_function, "after_");
	strcat(local_after_function, base_name + 1);
	if (noun[1] != FALSE) {
		strcat(local_after_function, "_");
		strcat(local_after_function, object[noun[1]]->label);
	}
	if (noun[0] != FALSE) {
		strcat(local_after_function, "_");
		strcat(local_after_function, object[noun[0]]->label);
	}

	/* THIS IS CALLED IF AN 'override' COMMAND IS EXECUTED
	 * IN A LIBRARY FUNCTION BUT THE OBJECT-OR-LOCATION-SPECIFIC
	 * OVERRIDE DOES NOT EXIST. IT IS SET TO '+default_func' */
	strcpy(default_function, "+default_");
	strcat(default_function, base_name + 1);

	/* EXECUTE THE GLOBAL *DEFAULT* BEFORE FUNCTION
	 * AND RETURN IF IT RETURNS TRUE */
	if (execute("+before") != FALSE)
		return;

	/* EXECUTE THE VERB-SPECIFIC BEFORE
	   FUNCTION AND RETURN IF IT RETURNS TRUE */
	if (execute(before_function) != FALSE)
		return;

	if (noun[0] == FALSE) { /* USER'S COMMAND HAS NO NOUNS */
		strcat(base_function, object[HERE]->label);
		/* EXECUTE THE FUNCTION 'func_here' */
		if (execute(base_function) == FALSE) {
			/* THIS LOCATION-SPECIFIC FUNCTION DOES NOT
			 * EXIST OR HAS ISSUED A 'break false' COMMAND.
			 * EXECUTE THE FUNCTION '+func'
			 * WITH THE POSSIBLILITY OF
			 * EXECUTING THE FUNCTION 'func_override_here'
			 * IF AN 'override' COMMAND IS FOUND IN '+func'
			 * IF THIS OVERRIDE FUNCTION ISN'T FOUND
			 * THE DEFAULT FUNCTION WILL BE EXECUTED */

			/* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
			 * IS NEEDED */
			strcat(override_, "override_");
			strcat(override_, object[HERE]->label);

			/* CREATE THE FUNCTION NAME '+func' */
			strcpy(base_function, "+");
			strcat(base_function, base_name + 1);

			/* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
			if (execute(base_function) == FALSE)
				unkfunrun(base_function);
		}
	} else if (noun[1] == FALSE) { /* USER'S COMMAND HAS ONE NOUN */
		strcat(base_function, object[noun[0]]->label);
		/* EXECUTE THE FUNCTION 'func_noun1' */
		if (execute(base_function) == FALSE) {
			/* THIS OBJECT-SPECIFIC FUNCTION DOES NOT
			 * EXIST OR HAS ISSUED A 'break false' COMMAND.
			 * EXECUTE THE FUNCTION '+func'
			 * WITH THE POSSIBLILITY OF
			 * EXECUTING THE FUNCTION 'func_override_noun1'
			 * IF AN 'override' COMMAND IS FOUND IN '+func'
			 * IF THIS OVERRIDE FUNCTION ISN'T FOUND
			 * THE DEFAULT FUNCTION WILL BE EXECUTED */

			/* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
			 * IS NEEDED */
			strcat(override_, "override_");
			strcat(override_, object[noun[0]]->label);

			/* CREATE THE FUNCTION NAME '+func' */
			strcpy(base_function, "+");
			strcat(base_function, base_name + 1);

			/* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
			if (execute(base_function) == FALSE)
				unkfunrun(base_function);
		}
	} else { /* USER'S COMMAND HAS TWO NOUNS */
		strcat(base_function, object[noun[1]]->label);
		strcat(base_function, "_");
		strcat(base_function, object[noun[0]]->label);
		/* EXECUTE THE FUNCTION 'func_noun2_noun1'
		 * IE give_to_fred THAT IS ASSOCIATED WITH
		 * THE OBJECT flint_stone */
		if (execute(base_function) == FALSE) {
			/* THIS OBJECTS-SPECIFIC FUNCTION DOES NOT
			 * EXIST OR HAS ISSUED A 'break false' COMMAND.
			 * EXECUTE THE FUNCTION '+func'
			 * WITH THE POSSIBLILITY OF
			 * EXECUTING THE FUNCTION 'func_override_noun2_noun1'
			 * IF AN 'override' COMMAND IS FOUND IN '+func'
			 * IF THIS OVERRIDE FUNCTION ISN'T FOUND
			 * THE DEFAULT FUNCTION WILL BE EXECUTED */

			/* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
			 * IS NEEDED */
			strcat(override_, object[noun[1]]->label);
			strcat(override_, "_override_");
			strcat(override_, object[noun[0]]->label);

			/* CREATE THE FUNCTION NAME '+func' */
			strcpy(base_function, "+");
			strcat(base_function, base_name + 1);

			/* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
			if (execute(base_function) == FALSE)
				unkfunrun(base_function);
		}
	}

	/* EXECUTE THE LOCAL AFTER FUNCTION
	 * AND RETURN IF IT RETURNS TRUE */
	if (execute(local_after_function) != FALSE)
		return;

	/* EXECUTE THE VERB-SPECIFIC AFTER
	 * FUNCTION AND RETURN IF IT RETURNS TRUE */
	if (execute(after_function) != FALSE)
		return;

	/* EXECUTE THE GLOBAL *DEFAULT* AFTER FUNCTION
	 * AND RETURN IF IT RETURNS TRUE */
	if (execute("+after") != FALSE)
		return;


	//sprintf(temp_buffer, "TIME = %d", TIME->value);
	//log_error(temp_buffer, PLUS_STDERR);

	if (TIME->value) {
		//printf("--- %s\n", base_function);
		eachturn();
	}

	return;
}

struct word_type *object_match(struct word_type *iterator, int noun_number) {
	/* THIS FUNCTION LOOPS THROUGH ALL THE POSIBILITIES IN THE CURRENT LEVEL
	 * OF THE GRAMMAR TREE TO SEE IF THERE ARE ANY OBJECT PLACE HOLDERS */

	/* STORE WHETHER BY THE END AN OBJECT PLACEHOLDER WAS ENCOUNTERED AND
	 * DISPLAY A MESSAGE IF NO OBJECTS MATCHED */
	int object_was_option = FALSE;

	do {
		/* THIS LOOP MEANS THAT CERTAIN ERRORS SUCH AS TAKING FROM A
		 * CLOSED CONTAINER CAN OCCUR MORE THAN ONCE */
		if (iterator->word[0] == '*') {
			object_was_option = TRUE;
			if (build_object_list(iterator, noun_number)) {
				/* RETURN THE POINT IN THE GRAMMAR TREE THAT MATCHED TO
				* CONTINUE ON FROM */
				//printf("--- returned TRUE from build_object_list\n");
				return (iterator);
			}
		}

		if (custom_error == TRUE) {
			/* AN ERROR OCCURRED IN THE FIRST OBJECT PLACEHOLDER, DON'T
			 * TRY ANY OTHERS */
			return (nullptr);
		}
	} while ((iterator = iterator->next_sibling) != nullptr);

	/* THERE WERE NO OBJECT PLACE HOLDERS OR, IF THERE WERE, NO
	 * MATCHING OBJECTS COULD BE RESOLVED */
	//printf("--- returning null from object_match\n");

	if (object_was_option) {
		/* NO OBJECT MATCHED, BUT AN OBJECT WAS VALID AT THIS POINT IN THE
		 * PLAYER'S COMMAND */
		diagnose();
		custom_error = TRUE;
	}

	return (nullptr);
}

struct word_type *exact_match(struct word_type *pointer) {
	/* THIS FUNCTION LOOPS THROUGH ALL THE POSIBILITIES IN THE CURRENT LEVEL
	 * OF THE GRAMMAR TREE TO SEE IF THERE ARE ANY EXACT MATCHES WITH THE
	 * CURRENT WORD IN THE PLAYER'S COMMAND.
	 * AN EXACT MATCH IS ANYTHING THAT ISN'T AN OBJECT PLACE HOLDER. */
	struct word_type *iterator = pointer;

	do {
		if (iterator->word[0] == '*') {
			/* THIS IS AN OBJECT PLACE HOLDER, THEREFORE IGNORE AND
			 * KEEP LOOKING FOR EXACT MATCHES */
		} else if (!strcmp(iterator->word, "$string")) {
			add_cstring("$string", word[wp]);
			last_exact = wp;
			wp++;
			return (iterator);
		} else if (!strcmp(iterator->word, "$integer") &&
		           validate(word[wp])) {
			add_cinteger("$integer", atoi(word[wp]));
			last_exact = wp;
			wp++;
			return (iterator);
		} else if (!strcmp(word[wp], iterator->word)) {
			last_exact = wp;
			wp++;
			return (iterator);
		}
	} while ((iterator = iterator->next_sibling) != nullptr);

	/* THERE WERE NO EXACT MATCHES, SO RETURN FALSE */
	return (nullptr);
}

int is_terminator(struct word_type *scope_word) {
	struct word_type *terminator = scope_word->first_child;

	if (terminator != nullptr) {
		/* THERE MAY NO BE ANY MORE POSSIBLE WORDS IN THIS COMMAND
		 * BUT THERE SHOULD ALWAYS AT LEAST BE A BASE FUNCTION NAME */
		do {
			/* LOOP THROUGH ALL WORDS IN THE NEXT LEVEL OF THE
			 * GRAMMAR TABLE. THESE ARE THE WORDS THAT MARK THE END
			 * OF THE OBJECT REFERENCE PART OF THE COMMAND */
			if (!strcmp(word[wp], terminator->word)
			        || (!strcmp(terminator->word, "$integer")
			            && validate(word[wp]))) {
				return (TRUE);
			}
		} while ((terminator = terminator->next_sibling) != nullptr);
	}

	return (FALSE);
}

int build_object_list(struct word_type *scope_word, int noun_number) {
	/* THIS FUNCTION BUILDS A LIST OF OBJECTS FROM THE PLAYER'S COMMAND
	 * AND RETURNS THE NUMBER OF OBJECTS IN THAT LIST */

	int             index, counter;
	int             resolved_object;
	const char      *except_word;

	//printf("--- entering build object list starting at %s with a scope_word of %s\n", word[wp], scope_word->word);
	/* LOOK AHEAD FOR A FROM CLAUSE AND STORE from_object IF SO */
	if (get_from_object(scope_word, noun_number) == FALSE) {
		/* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
		 * DISPLAYED */
		//printf("--- from had an error\n");
		return (FALSE);
	}

	while (word[wp] != nullptr) {
		/* LOOP THROUGH WORDS IN THE PLAYER'S INPUT ENDING WHEN EITHER
		 * THERE ARE NO MORE WORDS OR ONE OF THE CHILD NODES OF THE
		 * CURRENT scope_word NODE IS REACHED INDICATING THERE ARE NO
		 * MORE OBJECTS TO RESOLVE */

		if (!strcmp(word[wp], cstring_resolve("BUT_WORD")->value) ||
		        !strcmp(word[wp], cstring_resolve("EXCEPT_WORD")->value)) {
			/* START ADDING ALL FUTURE RESOLVED OBJECTS TO A SECOND LIST
			 * TO REMOVE FROM THE FIRST */
			except_word = word[wp];

			wp++;

			if (word[wp] != nullptr && !strcmp(word[wp], cstring_resolve("FOR_WORD")->value)) {
				/* SKIP PAST THE WORD 'FOR' */
				wp++;
			}

			/* LOOK FORWARD FOR A FROM CLAUSE */
			if (get_from_object(scope_word, noun_number) == FALSE) {
				/* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
				 * DISPLAYED */
				return (FALSE);
			}

			/* MOVE TO THE SECOND LIST THAT WILL ULTIMATELY BE SUBTRACTED
			 * FROM THE FIRST LIST */
			if (noun_number < 2) {
				/* CREATE A 'them' SET THAT CAN BE REFERRED TO IN THE
				 * 'except' CLAUSE */
				set_them(noun_number);
				/* JUMP TO THE 'EXCEPT' LIST THAT CORRESPONDS TO THIS
				 * RESOLVED LIST */
				noun_number = noun_number + 2;
			} else {
				sprintf(error_buffer, cstring_resolve("DOUBLE_EXCEPT")->value, except_word);
				write_text(error_buffer);
				custom_error = TRUE;
				return (FALSE);
			}
		} else if (after_from != -1 && !strcmp(word[wp], cstring_resolve("FROM_WORD")->value)) {
			/* SET THE WORD POINTER TO AFTER THE ALREADY-PROCESSED FROM
			 * CLAUSE (IF ONE EXISTED) AND CONTINUE */
			wp = after_from;
			//printf("--- hit from in processing moving on to word '%s'\n", word[wp]);

			/* IF NO OBJECTS WERE MATCHED BY THE TIME WE HIT FROM THEN EITHER
			 * PRESUME THE PLAYER MEANT 'ALL FROM' OR PRINT AN ERROR */
			if (list_size[noun_number] == 0) {
				//printf("--- adding all due to empty list.\n");
				add_all(scope_word, noun_number);
			}

			/* LOOK FOR THE NEXT FROM CLAUSE */
			if (get_from_object(scope_word, noun_number) == FALSE) {
				/* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
				 * DISPLAYED */
				return (FALSE);
			}
		} else if (!strcmp("then", word[wp])) {
			break;
		} else if (is_terminator(scope_word)) {
			/* THERE ARE NO MORE OBJECTS TO RESOLVE */

			//printf("--- %s is a build list terminator\n", word[wp]);
			break;
		} else if (!strcmp(word[wp], "comma") ||
		           !strcmp(word[wp], cstring_resolve("AND_WORD")->value)) {
			/* JUST MOVE ONTO THE NEXT WORD AND SEE WHAT COME NEXT */
			wp++;
		} else {
			/* CALL noun_resolve TO FETCH THE FIRST MATCHING OBJECT */
			/* FALSE INDICATES THAT WE ARE NOT LOOKING FOR A FROM OBJECT */
			resolved_object = noun_resolve(scope_word, FALSE, noun_number);

			if (resolved_object == -1) {
				/* THERE WERE MULTIPLE MATCHES DUE TO PLURAL NAME
				 * BEING USED */
				index = 0;

				while (multiple_resolved[index] != 0) {
					/* ADD ALL THE RESOLVED OBJECTS TO THE LIST */
					add_to_list(noun_number, multiple_resolved[index]);
					index++;
				}
			} else if (resolved_object) {
				/* ADD IT TO THE LIST */
				add_to_list(noun_number, resolved_object);
			} else {
				/* NO OBJECTS COULD BE RESOLVED, THIS MIGHT NOT BE A BAD
				 * THING YET IF THERE ARE OTHER OBJECT PLACEHOLDERS TO
				 * COME THAT ARE LESS RESTRICTIVE */
				return (FALSE);
			}
		}
	}

	if (noun_number > 1 && list_size[noun_number] != 0) {
		/* A SECOND EXCEPTION LIST EXISTS, SUBTRACT IT FROM THE FIRST */
		//printf("--- there are some exceptions.\n");

		//printf("--- first list: %d, second list: %d\n", max_size[FIRST_LIST], max_size[SECOND_LIST]);
		/* LOOP THROUGH ALL THE ITEMS IN THE SECOND LIST */
		for (index = 0; index < max_size[SECOND_LIST]; index++) {
			if (object_list[SECOND_LIST][index] != 0) {
				/* THIS OBJECT IS A REAL OBJECT SO LOOP THROUGH ALL THE ITEMS
				 * IN THE FIRST LIST */
				//printf("--- exception object is %s\n", object[object_list[SECOND_LIST][index]]->label);
				for (counter = 0; counter < max_size[FIRST_LIST]; counter++) {
					/* LOOP THROUGH ALL THE OBJECTS IN THE FIRST LIST
					 * IF AN OBJECT FROM THE SECOND LIST EXISTS IN THE FIRST
					 * LIST, REMOVE IT */
					//printf("--- comparing %s = %s\n", object[object_list[FIRST_LIST][counter]]->label, object[object_list[SECOND_LIST][index]]->label);
					if (object_list[FIRST_LIST][counter] ==
					        object_list[SECOND_LIST][index]) {

						//printf("--- removing object %s\n", object[object_list[FIRST_LIST][counter]]->label);
						object_list[FIRST_LIST][counter] = 0;
						list_size[FIRST_LIST]--;
					}
				}
			}
		}
	}

	if (noun_number > 1) {
		/* IF THERE WERE EXCEPTIONS, MOVE BACK TO THE FIRST LIST */
		noun_number = FIRST_LIST;
	}

	/* THE RETURN TRUE IF AN OBJECT COULD BE RESOLVED */
	if (list_size[noun_number] != 0) {
		/* SET THEM ARRAY */
		set_them(noun_number);

		return (TRUE);
	} else {
		/* THE LIST IS NOW EMPTY, DISPLAY ' I DON'T SEE WHAT YOU
		 * ARE REFERRING TO.' ERROR */
		if (!strcmp(scope_word->word, "*held") ||
		        !strcmp(scope_word->word, "**held")) {
			write_text(cstring_resolve("NONE_HELD")->value);
		} else {
			write_text(cstring_resolve("NO_OBJECTS")->value);
		}

		custom_error = TRUE;
		return (FALSE);
	}
}

void set_them(int noun_number) {
	int index, counter;

	if (list_size[noun_number] == 1) {
		/* THERE IS ONLY ONE OBJECT LEFT IN THE LIST, FIND IT AND ADD
		 * TO THEM IF REQUIRED */
		for (index = 0; index < max_size[noun_number]; index++) {
			if (object_list[noun_number][index] != 0) {
				if (object[object_list[noun_number][index]]->attributes & PLURAL) {
					them[0] = object_list[noun_number][index];
					them[1] = 0;
					break;
				}
			}
		}
	} else {
		/* THERE IS MORE THAN ONE OBJECT IN THE LIST SO COPY IT TO THE
		 * 'THEM' LIST */
		counter = 0;

		for (index = 0; index < max_size[noun_number]; index++) {
			if (object_list[noun_number][index] != 0) {
				them[counter] = object_list[noun_number][index];
				//printf("--- storing %s in them list\n", object[them[counter]]->label);
				counter++;
			}
		}

		/* NULL TERMINATE THE LIST */
		them[counter] = 0;
	}
}

void add_all(struct word_type *scope_word, int noun_number) {
	int index;

	//printf("--- trying to add all\n");

	for (index = 1; index <= objects; index++) {
		if ((object[index]->MASS < HEAVY) &&
		        !(object[index]->attributes & LOCATION)) {
			if (is_direct_child_of_from(index) &&
			        scope(index, scope_word->word, RESTRICT)) {
				//printf("--- objects parent is %s\n", object[object[index]->PARENT]->label);
				add_to_list(noun_number, index);
			}
		}
	}
}

int is_child_of_from(int child) {
	/* THIS FUNCTION DETERMINES IF THE PASSED OBJECT IS A CHILD OF ANY OF
	 * THE RESOLVED 'FROM' OBJECTS, OR ANY OBJECT IN A FROM OBJECT */
	int index = 0;

	if (from_objects[0] == 0) {
		/* THERE HAS BEEN NO FROM CLAUSE */
		return (TRUE);
	}

	while (from_objects[index] != 0) {
		//printf("--- in is_child from object is %s, child is %s\n", object[from_objects[index]]->label, object[child]->label);
		/* THIS OLD WAY OF DOING THINGS ALLOWS SPECIFIC 'take thing from box'
		 * WHEN thing IS INSIDE SOMETHING ELSE INSIDE box. THAT IS KINDA COOL
		 * BUT NOT PARTICULARLY NECESSARY. BY ONLY CHECKING IMMEDIATE CHILDREN
		 * THE NON-RESTRICTIVE VERSION OF parent_of CAN BE USED FROM SCOPE
		 * WHEN SAYING take all from box BECAUSE THIS FROM STOPS OBJECT IN
		 * OBJECTS FROM BEING TAKING FROM SOMETHING, SO scope DOESN'T HAVE
		 * TO. THIS BEHAVIOUR WOULD NOT BE NECESSARY WHEN NOT taking all from */

		//if (parent_of(from_objects[index], child, RESTRICT)) {
		if (object[child]->PARENT == from_objects[index]) {
			//printf("--- %s is in %s\n", object[child]->label, object[from_objects[index]]->label);
			return (TRUE);
		}
		index++;
	}

	return (FALSE);
}

int is_direct_child_of_from(int child) {
	/* THIS FUNCTION DETERMINES IF THE PASSED OBJECT IS A CHILD OF ANY OF
	 * THE RESOLVED 'FROM' OBJECTS, OR ANY OBJECT IN A FROM OBJECT */
	int index = 0;

	if (from_objects[0] == 0) {
		/* THERE HAS BEEN NO FROM CLAUSE */
		return (TRUE);
	}

	while (from_objects[index] != 0) {
		//printf("--- in is_direct from object is %s\n", object[from_objects[index]]->label);
		if (object[child]->PARENT == from_objects[index]) {
			//printf("--- object %s is in the from object\n", object[child]->label);
			return (TRUE);
		}
		index++;
	}

	return (FALSE);
}

int get_from_object(struct word_type *scope_word, int noun_number) {
	/* THIS FUNCTION LOOKS AHEAD TO FIND IF THE CURRENT OBJECT REFERENCE
	 * IS QUALIFIED BY A 'FROM' WORD. IT RETURNS FALSE ON AN ERROR
	 * CONDITION AND TRUE OTHERWISE, REGARDLESS OF WHETHER A FROM OBJECT
	 * IS SPECIFIED */

	int index, counter, from_object;

	/* TAKE A COPY OF THE CURRENT VALUE OF wp */
	int backup = wp;

	/* SET TERMINATOR TO THE FIRST OF THE TERMINATING WORDS */
	struct word_type *terminator = scope_word->first_child;

	/* SEE IF 'FROM' IS ONE OF THE TERMINATORS OF THIS CURRENT OBJECT
	 * PLACEHOLDER. IF SO, DON'T LOOK FOR A FROM OBJECT */
	if (terminator != nullptr) {
		//printf("--- checking if terminator word (%s) is from\n", terminator->word);
		if (!strcmp(cstring_resolve("FROM_WORD")->value, terminator->word)) {
			//printf("--- from is a terminator, don't get a from object\n");
			return (TRUE);
		}
		while ((terminator = terminator->next_sibling) != nullptr);
	}

	/* LOOP FROM THE CURRENT WORD TO THE NEXT TERMINATOR AND LOOK FOR THE
	 * WORD 'FROM' AND STORE THE FOLLOWING OBJECT */
	while (word[wp] != nullptr) {
		//printf("--- from loop checking %s\n", word[wp]);
		if (!strcmp(word[wp], cstring_resolve("FROM_WORD")->value)) {
			from_word = word[wp];
			wp++;

			/* scope_word FOR THE CURRENT OBJECT IS PASSED ONLY SO
			 * noun_resolve CAN FIND OUT THE APPROPRIATE TERMINATORS
			 * THE ACCEPTABLE SCOPE FOR THE FROM OBJECT SHOULD BE
			 * AT LEAST *present */
			/* TRUE INDICATES THAT WE ARE LOOKING FOR A FROM OBJECT */
			from_object = noun_resolve(scope_word, TRUE, noun_number);

			/* STORE THE wp FROM AFTER THE RESOLVED FROM OBJECT SO
			 * WE CAN JUMP FORWARD TO HERE AGAIN WHEN WE HIT THIS
			 * FROM CLAUSE IN PROCESSING (THIS FUNCTION IS A LOOK-AHEAD */
			after_from = wp;

			//printf("--- looked forward and found a from object of %s\n", object[from_object]->label);

			if (from_object == -1) {
				/* THERE WERE MULTIPLE MATCHES DUE TO PLURAL NAME
				 * BEING USED */
				index = 0;
				counter = 0;
				/* LOOK THROUGH ALL THE OBJECTS RESOLVED AND CHECK THAT
				 * THEY ARE ALL VALID FROM OBJECTS */
				while (multiple_resolved[index] != 0) {
					if (verify_from_object(multiple_resolved[index]) == FALSE) {
						/* AS SOON AS ONE IS BAD, ABORT THE FROM CLAUSE */
						return (FALSE);
					} else {
						from_objects[counter] = multiple_resolved[index];
						counter++;
					}
					index++;
				}

				/* NULL TERMINATE THE LIST */
				from_objects[counter] = 0;

				/* OBJECTS HAVE BEEN SET AND ARE VALID, RESTORE THE WORD
				 * POINTER */
				wp = backup;
				return (TRUE);
			} else if (from_object) {
				if (verify_from_object(from_object) == FALSE) {
					return (FALSE);
				} else {
					/* ADD THIS OBJECT TO THE NULL TERMINATED LIST */
					from_objects[0] = from_object;
					from_objects[1] = 0;

					/* OBJECT HAS BEEN SET AND IS VALID, RESTORE THE WORD
					* POINTER */
					wp = backup;
					return (TRUE);
				}
			} else {
				/* FROM UNKNOWN OBJECT, DISPLAY ERROR */
				diagnose();
				custom_error = TRUE;
				return (FALSE);
			}
			//printf("--- finished processing from clause, next word is %s\n", word[wp]);
		} else if (!strcmp(cstring_resolve("EXCEPT_WORD")->value, word[wp]) ||
		           !strcmp(cstring_resolve("BUT_WORD")->value, word[wp])) {
			/* THIS IS THE LIMIT OF THE EFFECT ANY FROM OBJECT ANYWAY,
			 * SO TREAT IT LIKE A TERMINATOR */
			//printf("--- %s is a get_from_object except word\n", word[wp]);
			wp = backup;
			return (TRUE);
		} else if (is_terminator(scope_word) || !strcmp("then", word[wp])) {
			/* THERE ARE NO MORE OBJECTS TO RESOLVE */

			//printf("--- %s is a get_from_object terminator\n", word[wp]);
			wp = backup;
			return (TRUE);
		}
		wp++;
	}

	/* HIT THE END OF THE PLAYER'S COMMAND BEFORE A 'FROM' WORD */
	//printf("--- no from clause specified in this block\n");
	wp = backup;
	return (TRUE);
}

int verify_from_object(int from_object) {
	//printf("--- from object is %s\n", object[from_object]->label);
	//if (!(object[from_object]->attributes & CONTAINER) &&
	//  !(object[from_object]->attributes & SURFACE) &&
	//  !(object[from_object]->attributes & ANIMATE)) {
	//  sprintf (error_buffer, FROM_NON_CONTAINER, from_word);
	//  write_text (error_buffer);
	//  custom_error = TRUE;
	//  return (FALSE);
	//} else if (object[from_object]->attributes & CLOSED) {
	if (object[from_object]->attributes & CONTAINER && object[from_object]->attributes & CLOSED) {
		//printf("--- container is concealing\n");
		if (object[from_object]->attributes & FEMALE) {
			sprintf(error_buffer, cstring_resolve("CONTAINER_CLOSED_FEM")->value, sentence_output(from_object, TRUE));
		} else {
			sprintf(error_buffer, cstring_resolve("CONTAINER_CLOSED")->value, sentence_output(from_object, TRUE));
		}
		write_text(error_buffer);
		custom_error = TRUE;
		return (FALSE);
		/* IF THE PERSON IS CONCEALING, THEN THE OBJECT CAN'T BE REFERRED TO
		 * IF THE PERSON IS POSSESSIVE LET THE LIBRARY HANDLE THE RESPONSE
		} else if (object[from_object]->attributes & POSSESSIVE) {
		    //printf("--- container is closed\n");
		    sprintf (error_buffer, PERSON_POSSESSIVE, sentence_output(from_object, TRUE));
		    write_text(error_buffer);
		    custom_error = TRUE;
		    return (FALSE);
		} else if (object[from_object]->attributes & CONCEALING) {
		    //printf("--- container is closed\n");
		    sprintf (error_buffer, PERSON_CONCEALING, sentence_output(from_object, TRUE));
		    write_text(error_buffer);
		    custom_error = TRUE;
		    return (FALSE);*/
	}

	//printf("--- set from object just fine\n");
	return (TRUE);
}

void add_to_list(int noun_number, int resolved_object) {
	/* ADD THIS OBJECT TO THE OBJECT LIST DEPENDING */
	/* AND SET IT, THEM, HER AND HIM */
	if (!(object[resolved_object]->attributes & ANIMATE))
		it = resolved_object;
	if (object[resolved_object]->attributes & ANIMATE
	        && object[resolved_object]->attributes & FEMALE)
		her = resolved_object;
	if (object[resolved_object]->attributes & ANIMATE
	        && !(object[resolved_object]->attributes & FEMALE))
		him = resolved_object;

	//printf("--- adding_object %s to list %d at index %d\n", object[resolved_object]->label, noun_number, max_size[noun_number]);
	object_list[noun_number][max_size[noun_number]] = resolved_object;
	list_size[noun_number]++;
	max_size[noun_number]++;
}

int noun_resolve(struct word_type *scope_word, int finding_from, int noun_number) {
	/* THIS FUNCTION STARTS LOOKING AT THE PLAYER'S COMMAND FROM wp ONWARDS
	 * AND LOOKS FOR OBJECTS IN THE SCOPE SPECIFIED BY THE GRAMMAR ELEMENT
	 * POINTED TO BY THE PASSED pointer */

	/* THIS IS SET TO TRUE WHEN THE CURRENT WORD MATCHES THE CURRENT OBJECT */
	int             object_matched;

	/* THIS IS SET TO > 0 WHEN THE PLURAL FORM OF AN OBJECT IS USED */
	int             return_limit = 0;

	int             index;
	int             counter;
	int             first_word = TRUE;

	struct word_type *terminator = scope_word->first_child;
	struct name_type *current_name;

	matches = 0;
	highest_confidence = 0;
	prime_suspect = 0;
	done = FALSE;
	backup_pointer = wp;
	everything = FALSE;

	if (word[wp] == nullptr) {
		/* NOTHING TO RESOLVE... */
		return (FALSE);
	}

	/* SET THE CONFIDENCE FOR EACH OBJECT TO 1 TO INDICATE THAT THEY ARE
	 * ALL EQUALLY POSSIBLE AT THE MOMENT. SET TO ZERO TO DISCOUNT */
	for (index = 1; index <= objects; index++)
		confidence[index] = 1;

	/* CLEAR THE OBJECT NAME BEFORE BUILDING IT WAS WE GO ALONG */
	object_name[0] = 0;

	/* CHECK FOR A QUANTITY QUALIFIER */
	if (validate(word[wp])) {
		/* FIRST WORD IS AN INTEGER AND SECOND WORD IS 'OF' SO
		 * TREAT THIS AS A LIMIT QUALIFIER BEFORE STARTING TO
		 * PROCESS THE REST OF THE WORDS */
		if (word[wp + 1] != nullptr && !strcmp(word[wp + 1], cstring_resolve("OF_WORD")->value)) {
			return_limit = atoi(word[wp]);

			/* MAKE SURE THE RETURN LIMIT IS SOMETHING SENSIBLE */
			if (return_limit < 1) {
				return_limit = 1;
			}

			object_expected = TRUE;
			strcpy(object_name, word[wp]);
			strcat(object_name, " ");
			strcat(object_name, cstring_resolve("OF_WORD")->value);

			/* MOVE THE WORD POINTER TO AFTER THE 'OF' */
			wp = wp + 2;
		}
		/* IF AN INTEGER IS NOT FOLLOWED BY 'OF', PRESUME IT IS AN OBJECT
		 * NAME */
	}

	// CLEAR THE ERROR BUFFER AND USE IT TO STORE ALL THE WORDS THE PLAYER
	// HAS USED TO REFER TO THE OBJECT
	error_buffer[0] = 0;

	while (word[wp] != nullptr) {
		// ADD THE WORDS USED TO error_buffer FOR POSSIBLE USE
		// IN A DISABMIGUATE EMESSAGE
		if (first_word == FALSE) {
			strcat(error_buffer, " ");
		}
		strcat(error_buffer, word[wp]);
		first_word = FALSE;

		/* LOOP THROUGH WORDS IN THE PLAYER'S INPUT */

		/* RESET TERMINATOR TO THE FIRST OF THE TERMINATING WORDS */
		terminator = scope_word->first_child;

		if (terminator != nullptr) {
			/* THERE MAY NO BE ANY MORE POSSIBLE WORDS IN THIS COMMAND
			 * BUT THERE SHOULD ALWAYS AT LEAST BE A BASE FUNCTION NAME */
			do {
				/* LOOP THROUGH ALL WORDS IN THE NEXT LEVEL OF THE
				 * GRAMMAR TABLE. THESE ARE THE WORDS THAT MARK THE END
				 * OF THE OBJECT REFERENCE PART OF THE COMMAND */
				//printf("--- checking terminator word %s\n", terminator->word);
				if (!strcmp(word[wp], terminator->word)
				        || (!strcmp(word[wp], cstring_resolve("FROM_WORD")->value))
				        || (!strcmp(word[wp], cstring_resolve("AND_WORD")->value))
				        || (!strcmp(word[wp], "comma"))
				        || (!strcmp(word[wp], cstring_resolve("BUT_WORD")->value))
				        || (!strcmp(word[wp], cstring_resolve("THEN_WORD")->value))
				        || (!strcmp(word[wp], cstring_resolve("EXCEPT_WORD")->value))
				        || (!strcmp(terminator->word, "$integer")
				            && validate(word[wp]))) {
					if (!matches) {
						/* A TERMINATOR HAS BEEN FOUND BEFORE A
						 * SINGLE MATCHING OBJECT NAME. */
						return (FALSE);
					} else {
						/* A TERMINATOR HAS BEEN FOUND, BUT NOT
						 * BEFORE MATCHING OBJECT NAMES. JUMP FORWARD TO
						 * RESOLVING INTO OBJECT NUMBER(S) */
						done = TRUE;
						break;
					}
				}
			} while ((terminator = terminator->next_sibling) != nullptr);
		}

		if (done == TRUE) {
			/* A TERMINATING WORD HAS BEEN REACHED, SO DON'T TEST THIS
			 * WORD AGAINST THE OBJECT NAMES, JUST MOVE ON TO CHOSING
			 * AN OBJECT. */
			//printf("--- %s is a terminator\n", word[wp]);
			break;
		}

		//puts("--- passed checking for a terminator");

		/* ADD THE CURRENT WORD TO THE NAME OF THE OBJECT THE PLAYER
		 * IS TRYING TO REFER TO FOR USE IN AN ERROR MESSAGE IF
		 * LATER REQUIRED */
		if (object_name[0] != 0)
			strcat(object_name, " ");

		strcat(object_name, word[wp]);

		if (!strcmp("everything", word[wp])) {
			/* ALL THIS NEEDS TO SIGNIFY IS THAT IT IS OKAY TO RETURN MULTIPLE
			 * RESULTS. EVERYTHING ELSE SHOULD TAKE CARE OF ITSELF GIVEN ALL
			 * OBJECTS START OF WITH A CONFIDENCE OF 1, AND ALL OBJECTS WITH
			 * A CONFIDENCE > 0 ARE RETURNED WHEN return_limit > 1 */
			if (return_limit == FALSE) {
				return_limit = MAX_OBJECTS;
			}

			/* THIS IS USED TO ALTER THE BEHAVIOUR OF SCOPE SELECTION LATER */
			everything = TRUE;

			matches = 0;

			//printf("--- entering for loop\n");
			for (index = 1; index <= objects; index++) {
				if (confidence[index] != FALSE) {
					matches++;
				}
			}
			//printf("--- exiting for loop\n");

			if (word[wp + 1] != nullptr && !strcmp(cstring_resolve("OF_WORD")->value, word[wp + 1])) {
				/* MOVE PAST THE 'OF' IF IT IS NEXT */
				wp++;
			}
			//printf("--- finished setting matches to %d for all\n", matches);
		} else {
			/* THE CURRENT WORD IS NOT ONE OF THE NEXT POSSIBLE WORDS IN THE
			 * PLAYER'S COMMAND OR ANY OF THE SPECIAL MEANING WORDS, THEREFORE
			 * TEST IT AGAINST ALL OF THE NAMES OF ALL OF THE OBJECTS. */
			for (index = 1; index <= objects; index++) {
				if (!confidence[index]) {
					/* SKIP OBJECTS THAT HAVE ALREADY
					 * BEEN EXCLUDED BY A PREVIOUS WORD */
					continue;
				}

				/* NEXT WORD IN PLAYERS INPUT IS YET TO
				 * BE TESTED AGAINST ALL THIS OBJECT'S NAMES */
				object_matched = FALSE;

				if (!strcmp(cstring_resolve("IT_WORD")->value, word[wp]) ||
				        !strcmp(cstring_resolve("ITSELF_WORD")->value, word[wp])) {
					if (it == FALSE) {
						no_it();
						return (FALSE);
					} else {
						if (index == it) {
							object_matched = TRUE;
						}
					}
				} else if (!strcmp(cstring_resolve("HER_WORD")->value, word[wp]) ||
				           !strcmp(cstring_resolve("HERSELF_WORD")->value, word[wp])) {
					if (her == FALSE) {
						no_it();
						return (FALSE);
					} else {
						if (index == her) {
							object_matched = TRUE;
						}
					}
				} else if (!strcmp(cstring_resolve("HIM_WORD")->value, word[wp]) ||
				           !strcmp(cstring_resolve("HIMSELF_WORD")->value, word[wp])) {
					if (him == FALSE) {
						no_it();
						return (FALSE);
					} else {
						if (index == him) {
							object_matched = TRUE;
						}
					}
				} else if (!strcmp(cstring_resolve("THEM_WORD")->value, word[wp]) ||
				           !strcmp(cstring_resolve("THEMSELVES_WORD")->value, word[wp]) ||
				           !strcmp(cstring_resolve("ONES_WORD")->value, word[wp])) {
					/* THIS NEED ONLY BE THE SIZE OF 'THEM', BUT NO HARM
					 * IN MAKING IT THE FULL SIZE */
					if (return_limit == FALSE) {
						return_limit = MAX_OBJECTS;
					}

					/* LOOP THROUGH ALL THE OBJECT IN THE 'THEM' ARRAY AND
					 * SEE IF THIS OBJECT IS PRESENT */
					counter = 0;

					while (them[counter] != 0) {
						if (them[counter] == index) {
							//printf("--- found previous them object %s\n", object[index]->label);
							object_matched = TRUE;
							break;
						}
						counter++;
					}
					/*} else if (!strcmp("1", word[wp])) {
					    return_limit = 1;

					    // LOOP THROUGH ALL THE OBJECT IN THE 'THEM' ARRAY AND
					    // SEE IF THIS OBJECT IS PRESENT
					    counter = 0;

					    while (them[counter] != 0) {
					        if (them[counter] == index) {
					            //printf("--- found previous them object %s\n", object[index]->label);
					            object_matched = TRUE;
					            break;
					        }
					        counter++;
					    }*/
				} else {
					current_name = object[index]->first_name;

					while (current_name != nullptr) {
						/* LOOP THROUGH ALL THE CURRENT OBJECTS NAMES */
						if (!strcmp(word[wp], current_name->name)) {
							/* CURRENT WORD MATCHES THE CURRENT NAME
							 *OF THE CURRENT OBJECT */
							//printf("--- %s is a name match of object %d\n", word[wp], index);
							object_matched = TRUE;

							/* MOVE ON TO NEXT OBJECT, THIS OBJECT SHOULD
							 * NOT HAVE THE SAME NAME TWICE */
							break;
						}
						current_name = current_name->next_name;
					}

					/* NOW LOOP THROUGH ALL THE OJBECTS PLURAL NAMES */
					current_name = object[index]->first_plural;

					while (current_name != nullptr) {
						/* LOOP THROUGH ALL THE CURRENT OBJECTS NAMES */
						if (!strcmp(word[wp], current_name->name)) {
							/* CURRENT WORD MATCHES THE CURRENT NAME
							 *OF THE CURRENT OBJECT */
							//printf("--- %s is a plural name match of object %d\n", word[wp], index);
							object_matched = TRUE;

							/* IT IS NOW OKAY FOR THIS FUNCTION TO RETURN MORE
							 * THAT ONE MATCHING OBJECT AS THE PLURAL FORM
							 * HAS BEEN USED. THIS IS INDICATED BY RETURNING
							 * -1 TO INDICATED THAT THE OBJECT LIST IS STORED
							 * IN A GLOBALLY ACCESSIBLE ARRAY */
							if (return_limit == FALSE) {
								return_limit = MAX_OBJECTS;
							}

							/* MOVE ON TO NEXT OBJECT, THIS OBJECT SHOULD
							 * NOT HAVE THE SAME NAME TWICE */
							break;
						}
						current_name = current_name->next_name;
					}
				}

				if (object_matched) {
					/* THE CURRENT WORD MATCHES ONE OF THE NAMES OF THE
					 * CURRENT OBJECT. */
					if (confidence[index] == 1) {
						/* OBJECT HAD NOT YET BEEN COUNTED AS MATCH SO INCREMENT
						 * THE NUMBER OF OBJECTS THAT MATCH SO FAR. */
						matches++;
					}
					if (confidence[index] != FALSE) {
						/* IF OBJECT HAS NOT BEEN EXCLUDED, INCREMENT THE
						 * NUMBER OF NAMES IT HAS MATCHING. */
						confidence[index]++;
					}
				} else {
					/* THE CURRENT WORD IS NOT ONE OF THE NAMES OF THE
					 * CURRENT OBJECT.
					 * IF THE OBJECT HAD PREVIOUSLY BEEN A CANDIDATE, DECREMENT
					 * THE NUMBER OF MATCHING OBJECTS. */
					if (confidence[index] > 1)
						matches--;

					/* AS THE CURRENT WORD DID NOT MATCH ANY OF THE NAMES OF
					 * THE THIS OBJECT, EXCLUDE IT FROM CONTENTION. */
					confidence[index] = FALSE;
				}
			} /* MOVE ON TO NEXT OBJECT FOR IN LOOP */
		}

		if (!matches) {
			/* IF THERE ARE NO REMAINING MATCHES DON'T MOVE ON TO THE NEXT
			 * WORD IN THE PLAYER'S INPUT. */
			//printf("--- %s isnt a name match for any object\n", word[wp]);

			/* THIS WORD IS A LIKELY BE INCORRECT AS IT DIDN'T MATCH
			 * ANY OBJECTS */
			if (oops_word == -1 && word[wp] != nullptr) {
				oops_word = wp;
			}

			break;
		}

		/* MOVE ON TO THE NEXT WORD IN THE PLAYER'S INPUT. */
		//printf("--- moving on to next word\n");
		wp++;
	}

	//printf("--- finished processing words\n");

	/***************************************************
	 * FINISHED LOOKING THROUGH THE PLAYER'S INPUT NOW *
	 * MOVE THE POINTER BACK FOR PARSER TO PROCESS     *
	 * THIS SHOULDN'T BE DONE FOR A WORD LIKE 'AND'    *
	 ***************************************************/

	if (return_limit == FALSE) {
		/* THE RETURN LIMIT WAS NEVER SET, SO TREAT THIS AS AN IMPLICIT 1 */
		return_limit = 1;
	}

	if (matches == 0) {
		/* THE PLAYER'S INPUT COULD NOT BE RESOLVED TO A SINGLE OBJECT
		 * BUT NO TERMINATING WORD WAS USED BEFORE A NON-TERMINATING
		 * WORD IN A PLACE WHERE AN OBJECT COULD BE SPECIFIED ---
		 * IN OTHER WORDS, PRESUME THE PLAYER WAS TRYING TO REFER TO
		 * AN OBJECT BUT SOMEHOW GOT IT WRONG */

		//printf("--- matches = 0\n");
		/* THIS VARIABLE IS USED TO CHANGE THE BEHAVIOR OF diagnose()
		 * SHOULD THIS COMMAND TURN OUT TO BE INVALID */
		object_expected = TRUE;

		wp = backup_pointer;    /* BACK UP THE CURRENT WORD POINTER.
								 * SO THE PARSER CAN INVESTIAGE THE
								 * POSIBILITY THAT THIS ISN'T A
								 * REFERENCE TO AN OBJECT AT ALL */

		return (FALSE);         /* RETURN TO PARSER WITH NO MATCHING
								 * OBJECT HAVING BEEN FOUND. */
	}

	//printf("--- starting with %d matches\n", matches);

	/* LOOP THROUGH ALL OBJECTS AND REMOVE FROM ANY OBJECT THAT IS NOT IN THE
	 * CURRENT LOCATION, PROVIDED THE VERB DOESN'T ALLOW THE OBJECT TO
	 * BE *ANYWHERE. */
	for (index = 1; index <= objects; index++) {
		if (confidence[index] != FALSE && strcmp(scope_word->word, "*anywhere") && strcmp(scope_word->word, "**anywhere") && strcmp(scope_word->word, "*location")) {
			if (scope(index, "*present", UNRESTRICT) == FALSE) {
				matches--;
				confidence[index] = FALSE;
				//printf("--- removing %s for not being present\n", object[index]->label);
			}
		}

		/* IF THE OBJECT IS IMPLICITLY INCLUDED DUE TO A MULTIPLE RETURN
		 * LIMIT, REMOVE IT IF IT IS PART OF THE SCENERY */
		if (confidence[index] == 1 && return_limit > 1) {
			if ((object[index]->MASS >= HEAVY) ||
			        (object[index]->attributes & LOCATION)) {
				matches--;
				confidence[index] = 0;
				//printf("--- removing %s for being scenery\n", object[index]->label);
			}
		}

		/* FOR ALL CONTENDERS, CALCULATE THE CONFIDENCE AS A PERCENTAGE,
		 * SO LONG AS WE ARE STILL LOOKING FOR A SINGLE OBJECT */
		if (confidence[index] != FALSE && return_limit == 1) {
			current_name = object[index]->first_name;
			counter = 0;
			while (current_name != nullptr) {
				counter++;
				current_name = current_name->next_name;
			}
			confidence[index] = ((confidence[index] - 1) * 100) / counter;
		}
	}

	// CALCULATE THE HIGHEST CONFIDENCE OF ALL THE POSSIBLE OBJECTS
	highest_confidence = 0;

	for (index = 1; index <= objects; index++) {
		if (confidence[index] > highest_confidence) {
			highest_confidence = confidence[index];
		}
	}

	/* REMOVE ANY OBJECTS THAT ARE NOT IN THEIR VERB'S SPECIFIED SCOPE */
	for (index = 1; index <= objects; index++) {
		if (confidence[index] != FALSE) {
			if (scope(index, "*present", UNRESTRICT) != FALSE) {
				// TAKE SPECIAL NOT OF AN OBJECT THAT IS PRESENT
				// AND AT LEAST EQUAL FOR THE HIGHEST CONFIDENCE
				// IN CASE NO OBJECT ARE LEFT AFTER SPECIFIED SCOPE
				// IS USED TO FILTER THE LIST */
				if (confidence[index] == highest_confidence) {
					prime_suspect = index;
				}
				//printf("--- storing %s as prime_suspect\n", object[index]->label);
			}

			if (finding_from) {
				if (strcmp(scope_word->word, "*anywhere") && strcmp(scope_word->word, "**anywhere")) {
					if (scope(index, "*present") == FALSE) {
						matches--;
						confidence[index] = FALSE;
						continue;
					}
				}
			} else if (scope(index, scope_word->word, (everything && !from_objects[0])) == FALSE) {
				/* IF everything IS TRUE, scope IS RESTRICTED */
				//printf("--- removing %s due to regular scope\n", object[index]->label);
				matches--;
				confidence[index] = FALSE;
				continue;
			}

			/* CHECK IF THIS OBJECT IS IN ACCORDANCE WITH ANY FROM CLAUSE
			 * THAT MAY OR MAY NOT HAVE USED */
			if (is_child_of_from(index) == FALSE) {
				matches--;
				confidence[index] = FALSE;
				//printf("--- removing %s due to from clause\n", object[index]->label);
			}
		}
	}

	//printf("--- there are %d matches left\n", matches);

	/* THIS LOOP REMOVES ANY OBJECT THAT ARE NOT EQUAL TO THE HIGHEST
	 * CONFIDENCE UNLESS A PLURAL NAME WAS USED. IN THAT CASE, ONLY
	 * EXCLUDE OBJECTS THAT DO NOT HAVE ONE OF THE NAMES SUPPLIED */
	if (matches > 1 && return_limit == 1) {
		// CALCULATE THE HIGHEST CONFIDENCE NOW THAT OBJECTS
		// NOT IN SCOPE HAVE BEEN REMOVED
		highest_confidence = 0;

		for (index = 1; index <= objects; index++) {
			if (confidence[index] > highest_confidence) {
				highest_confidence = confidence[index];
			}
		}

		//printf("--- removing lower confidence objects\n");
		for (index = 1; index <= objects; index++) {
			/* REMOVE ANY OBJECTS THAT ARE NOT EQUAL
			   TO THE HIGHEST CONFIDENCE. */
			if (confidence[index] != FALSE) {
				if (confidence[index] < highest_confidence) {
					//printf("--- removing %s due to confidence of %d being under %d\n", object[index]->label, confidence[index], highest_confidence);
					confidence[index] = FALSE;
					matches--;
				}
			}
		}
	}

	if (matches == 0) {
		/* IF THIS LEAVES NO OBJECT, RETURN THE OBJECT THAT WAS PRESENT
		 * AND SET THE APPROPRIATE POINTERS. */
		if (prime_suspect != FALSE) {
			return (prime_suspect);
		} else {
			object_expected = TRUE;
			wp = backup_pointer;
			return (FALSE);
		}
	}

	if (matches == 1) {
		/* IF ONLY ONE POSSIBILITY REMAINS, RETURN THIS OBJECT AND SET THE
		 * APPROPRIATE POINTERS. */
		//printf("--- only one object left\n");
		for (index = 1; index <= objects; index++) {
			if (confidence[index] != FALSE) {
				return (index);
			}
		}
	}

	if (return_limit > 1) {
		/* THE PLURAL NAME OF AN OBJECT WAS USED OR A QUANTITY QUALIFIER */

		counter = 0;

		//printf("--- return_limit == TRUE\n");

		for (index = 1; index <= objects; index++) {
			if (confidence[index] != FALSE) {
				/* ADD EACH OBJECT TO multiple_resolved UNTIL
				 * THE return_limit IS REACHED */
				multiple_resolved[counter] = index;
				return_limit--;
				//printf("--- adding %s to multiple_resolved\n", object[index]->label);
				counter++;

				if (return_limit == 0) {
					break;
				}
			}
		}

		/* NULL TERMINATE THE LIST */
		multiple_resolved[counter] = 0;

		//printf("--- returning multiple objects\n");
		/* RETURN -1 TO INDICATED THERE ARE MULTIPLE OBJECTS */
		return (-1);
	}

	/* AN AMBIGUOUS REFERENCE WAS MADE. ATTEMPT TO CALL ALL THE disambiguate
	 * FUNCTIONS TO SEE IF ANY OF THE OBJECT WANTS TO TAKE PREFERENCE IN
	 * THIS CIRCUMSTANCE */

	/*
	int situation = noun_number;

	if (finding_from) {
		situation += 4;
	}

	for (index = 1; index <= objects; index++) {
	    if (confidence[index] != FALSE) {
	        strcpy(function_name, "disambiguate");
	        strcat(function_name, "_");
	        strcat(function_name, object[index]->label);
	        strcat(function_name, "<");
	        sprintf(temp_buffer, "%d", situation);
	        strcat(function_name, temp_buffer);

	        // CALL THE DISAMBIGUATION FUNCTION ATTACHED TO EACH OF THE
	        // POSSIBLE OBJECTS
	        int return_code = execute (function_name);

	        if (return_code == 1) {
	            // THIS OBJECT CAN CLAIMED OWNERSHIP OF THIS COMMAND
	            return (index);
	        } else if (return_code == -1) {
	            // THIS OBJECT HAS REJECTED OWNERSHIP OF THIS COMMAND
	            confidence[index] = FALSE;
	            matches--;
	        }
	    }
	}
	*/

	// CHECK IF ALL THE OBJECTS WERE REJECTED
	if (matches == 0) {
		return (prime_suspect);
	}

	// CHECK IF ONLY ONE OBJECT NOW REMAINS
	if (matches == 1) {
		/* IF ONLY ONE POSSIBILITY REMAINS, RETURN THIS OBJECT AND SET THE
		 * APPROPRIATE POINTERS. */
		//printf("--- only one object left\n");
		for (index = 1; index <= objects; index++) {
			if (confidence[index] != FALSE) {
				return (index);
			}
		}
	}

#if defined GLK || defined __NDS__
	/* NO OBJECT HAS CLAIMED OWNERSHIP, PROMPT THE PLAYER TO SPECIFY
	 * WHICH ONE THEY REQUIRE. */
	counter = 1;
	write_text(cstring_resolve("BY")->value);
	write_text(error_buffer);
	write_text(cstring_resolve("REFERRING_TO")->value);
	for (index = 1; index <= objects; index++) {
		if (confidence[index] != FALSE) {
			possible_objects[counter] = index;
			sprintf(text_buffer, "  [%d] ", counter);
			write_text(text_buffer);
			sentence_output(index, 0);
			write_text(temp_buffer);
			matches--;
			if (counter < 9)
				counter++;
			write_text("^");
		}
	}

	/* GET A NUMBER: don't insist, low = 1, high = counter */
	selection = get_number(FALSE, 1, counter - 1);

	if (selection == -1) {
		write_text(cstring_resolve("INVALID_SELECTION")->value);
		custom_error = TRUE;
		return (FALSE);
	} else {
		write_text("^");
		return (possible_objects[selection]);
	}
#else
	/* IF MORE THAT ONE OBJECT STILL REMAINS, PROMPT THE PLAYER TO SPECIFY
	 * WHICH ONE THEY REQUIRE. */
	write_text(cstring_resolve("WHEN_YOU_SAY")->value);
	write_text(error_buffer);
	write_text(cstring_resolve("MUST_SPECIFY")->value);
	for (index = 1; index <= objects; index++) {
		if (confidence[index] != FALSE) {
			sentence_output(index, 0);
			write_text(temp_buffer);
			matches--;
			if (matches == 0) {
				write_text(".");
				break;
			} else if (matches == 1) {
				write_text(cstring_resolve("OR_WORD")->value);
			} else
				write_text(", ");
		}
	}
	write_text("^");

	custom_error = TRUE;
	return (FALSE);
#endif
}

void diagnose() {
	if (custom_error) {
		TIME->value = FALSE;
		return;
	}
	if (word[wp] == nullptr)
		write_text(cstring_resolve("INCOMPLETE_SENTENCE")->value);
	else if (object_expected && wp != 0) {
		write_text(cstring_resolve("UNKNOWN_OBJECT")->value);
		write_text(object_name);
		write_text(cstring_resolve("UNKNOWN_OBJECT_END")->value);
	} else {
		write_text(cstring_resolve("CANT_USE_WORD")->value);
		write_text(word[wp]);
		write_text(cstring_resolve("IN_CONTEXT")->value);
	}
	TIME->value = FALSE;
}

int scope(int index, const char *expected, int restricted) {
	/* THIS FUNCTION DETERMINES IF THE SPECIFIED OBJECT IS IN THE SPECIFIED
	 * SCOPE - IT RETURNS TRUE IF SO, FALSE IF NOT. */

	int temp = 0;

	/* WHEN THE ARGUMENT restricted IS TRUE IT HAS A MORE LIMITED
	 * SENSE OF WHAT IS ACCEPTABLE */

	if (!strcmp(expected, "*held") || !strcmp(expected, "**held")) {
		if (object[index]->PARENT == HELD) {
			return (TRUE);
		} else if (object[index]->MASS >= HEAVY) {
			/* ALLOW AND OBJECT TO BE CONSIDERED HELD IF IT HAS A
			 * MASS OF HEAVY OR GREATER AND ITS DIRECT PARENT IS
			 * BEING HELD. IE, A LABEL ON A JAR CAN BE SHOWN BECAUSE
			 * THE JAR IS BEING HELD. */
			temp = object[index]->PARENT;
			if (temp > 0 && temp < objects) {
				if (object[temp]->PARENT == HELD) {
					return (TRUE);
				}
			}
			return (FALSE);
		} else {
			return (FALSE);
		}
	} else if (!strcmp(expected, "*location")) {
		if (object[index]->attributes & LOCATION) {
			return (TRUE);
		} else {
			return (FALSE);
		}
	} else if (!strcmp(expected, "*here") || !strcmp(expected, "**here")) {
		if (object[index]->PARENT == HERE || index == HERE) {
			/* THE OBJECT IN QUESTION IS IN THE PLAYER'S CURRENT LOCATION
			 * OR IS THE PLAYER'S CURRENT LOCATION. */
			return (TRUE);
		} else if (object[index]->PARENT == HELD) {
			/* IT IS ONLY A PROBLEM FOR THE OBJECT TO BE AN IMMEDIATE CHILD
			 * OF THE PLAYER. THE PLAYER CAN STILL TAKE AN OBJECT THAT IS IN
			 * SOMETHING THEY ARE CARRYING */
			return (FALSE);
		} else {
			/* IS THE OBJECT A CHILD OF THE CURRENT LOCATION SOMEHOW */
			return (parent_of(HERE, index, restricted));
		}
	} else if (!strcmp(expected, "*anywhere") || !strcmp(expected, "**anywhere")) {
		return (TRUE);
	} else if (!strcmp(expected, "*inside") || !strcmp(expected, "**inside")) {
		if (object_list[0][0] > 0 && object_list[0][0] < objects) {
			return (parent_of(object_list[0][0], index, restricted));
		} else {
			// THERE IS NO PREVIOUS OBJECT SO TREAT THIS LIKE A *here
			return (parent_of(HERE, index, restricted));
		}
	} else if (!strcmp(expected, "*present") || !strcmp(expected, "**present")) {
		if (index == HERE) {
			return (TRUE);
		} else {
			if (find_parent(index)) {
				return (TRUE);
			} else {
				return (FALSE);
			}
		}
	} else {
		unkscorun(expected);
		return (FALSE);
	}
}

int find_parent(int index) {
	/* THIS FUNCTION WILL SET THE GLOBAL VARIABLE parent TO
	 * THE OBJECT THAT IS AT THE TOP OF THE POSSESSION TREE.
	 * IT WILL RETURN TRUE IF THE OBJECT IS VISIBLE TO THE
	 * PLAYER */
	//printf("--- find parent of %s\n", object[index]->label);

	if (!(object[index]->attributes & LOCATION) &&
	        object[index]->PARENT != NOWHERE) {

		parent = object[index]->PARENT;
		//printf("--- parent is %s\n", object[parent]->label);

		if (index == parent) {
			/* THIS OBJECT HAS ITS PARENT SET TO ITSELF */
			sprintf(error_buffer, SELF_REFERENCE, executing_function->name, object[index]->label);
			log_error(error_buffer, PLUS_STDOUT);
			return (FALSE);
		} else  if (!(object[parent]->attributes & LOCATION)
		            && ((object[parent]->attributes & CLOSED && object[parent]->attributes & CONTAINER)
		                || object[parent]->attributes & CONCEALING)) {
			//printf("--- %s is closed, so return FALSE\n", object[parent]->label);
			return (FALSE);
		} else if (parent == HERE || parent == HELD) {
			/* THE OBJECT IS THE PLAYER'S CURRENT LOCATION OR BEING HELD */
			return (TRUE);
		} else {
			if (object[parent]->attributes & LOCATION) {
				//printf("--- %s is a location, so dont recuse\n", object[parent]->label);
				return (FALSE);
			} else {
				//printf("--- %s isnt a location, so recuse\n", object[parent]->label);
				return (find_parent(parent));
			}
		}
	} else {
		if (index == HERE)
			/* THE OBJECT IS THE PLAYER'S CURRENT LOCATION. */
			return (TRUE);
		else
			return (FALSE);
	}
}

int parent_of(int parent_, int child, int restricted) {
	/* THIS FUNCTION WILL CLIMB THE OBJECT TREE STARTING AT 'CHILD' UNTIL
	 * 'PARENT' IS REACHED (RETURN TRUE), OR THE TOP OF THE TREE OR A CLOSED
	 * OR CONCEALING OBJECT IS REACHED (RETURN FALSE). */

	/* restricted ARGUMENT TELLS FUNCTION TO IGNORE OBJECT IF IT IS IN AN
	 * OBJECT WITH A mass OF heavy OR LESS THAT IS NOT THE SUPPLIED
	 * PARENT ie. DON'T ACCEPT OBJECTS IN SUB OBJECTS */

	int             index;

	//printf("--- parent_ is %s, child is %s\n", object[parent_]->label, object[child]->label);
	if (child == parent_) {
		return (TRUE);
	} else if (!(object[child]->attributes & LOCATION) &&
	           object[child]->PARENT != NOWHERE) {
		/* STORE THE CHILDS PARENT OBJECT */
		index = object[child]->PARENT;
		//printf("--- %s is the parent_ of %s\n", object[index]->label, object[child]->label);

		if (index == child) {
			/* THIS CHILD HAS IT'S PARENT SET TO ITSELF */
			sprintf(error_buffer, SELF_REFERENCE, executing_function->name, object[index]->label);
			log_error(error_buffer, PLUS_STDOUT);
			//printf("--- self parent_.\n");
			return (FALSE);
		} else  if (!(object[index]->attributes & LOCATION)
		            && ((object[index]->attributes & CLOSED && object[index]->attributes & CONTAINER)
		                || object[index]->attributes & CONCEALING)) {
			/* THE CHILDS PARENT IS CLOSED OR CONCEALING - CANT BE SEEN */
			//printf("--- parent_ %s is closed\n", object[index]->label);
			return (FALSE);
		} else if (restricted && object[index]->MASS < HEAVY && index != parent_) {
			//printf("--- scenery object.\n");
			return (FALSE);
		} else {
			//printf("--- comparing %s with %s\n", object[index]->label, object[parent_]->label);
			if (index == parent_) {
				/* YES, IS PARENT OF CHILD */
				return (TRUE);
			} else if (object[index]->attributes & LOCATION) {
				return (FALSE);
			} else {
				/* KEEP LOOKING UP THE TREE TILL THE CHILD HAS NO MORE
				 * PARENTS */
				return (parent_of(parent_, index, restricted));
			}
		}
	} else {
		/* THE SPECIFIED OBJECT HAS NO PARENT */
		return (FALSE);
	}
}

} // End of namespace JACL
} // End of namespace Glk
