/*
 *	Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
 *
 *	Permission is hereby granted, free of charge, to any person obtaining a copy
 *	of this software and associated documentation files (the "Software"), to deal
 *	in the Software without restriction, including without limitation the rights
 *	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the Software is
 *	furnished to do so, subject to the following conditions:
 *
 *	The above copyright notice and this permission notice shall be included in
 *	all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *	SOFTWARE.
 */


#include "object.h"
#include "story.h"
#include "portme.h"
#include "messages.h"
#include "memory.h"


uint32_t objectPointerGet(uint16_t objectId) {
	uint32_t ptr;

	if (objectId == 0) portDie(MSG_INT_OBJECT0_REF);
	if ((storyVersion() <= 3) && (objectId > 255)) portDie(MSG_INT_OBJECT_BAD_ID);

	ptr = storyObjectTableAddress();
	ptr += 31 * 2;  // Skip properties defaults table.
	ptr += 9 * (objectId - 1);  // Find object in table.

	return ptr;
}


uint32_t objectPointerParentGet(uint32_t objectPointer) {
	uint32_t result = 0;
	uint32_t parent;

	if (storyVersion() <= 3) {
		parent = ZPEEK(objectPointer + 4);
		result = parent ? objectPointerGet(parent) : 0;
	} else {
		portDie(MSG_UNIMPLEMENTED);
	}

	return result;
}


uint16_t objectPropertyDefaultGet(uint32_t propertyId) {
	uint32_t values;

	if (((storyVersion() <= 3) && (propertyId > 31)) || ((storyVersion() >= 4) && (propertyId > 63))) {
		//***TODO*** Should we die here?  This seems to work.
		return 0;
	}

	values = storyObjectTableAddress() + ((propertyId - 1) * 2);

	return ZPEEKW(values);
}


uint32_t objectPropertyGet(uint16_t objectId, uint32_t propertyId, uint8_t *size) {
	uint32_t ptr  = objectPointerGet(objectId);
	uint16_t addr;
	uint8_t  info;
	uint16_t num;
	uint8_t  s;

	if (storyVersion() <= 3) {
		ptr += 7;  // Skip to properties address field.
		addr = ZPEEKW(ptr);
		ptr = addr;
		ptr += ZPEEK(ptr) * 2 + 1;  // Skip object name to start of properties.
		while (1) {
			info = ZPEEK(ptr++);
			num  = (info & 0x1f);  // 5 bits for property ID.
			s    = ((info >> 5) & 0x7) + 1;  // 3 bits for property size.

			// These go in descending numeric order, and should fail the interpreter if missing.
			// We use 0xFFFFFFFF internally to mean "first property".
			if ((num == propertyId) || (propertyId == 0xFFFFFFFF)) {  // found it?
				if (size) *size = s;
				return ptr;
			} else if (num < propertyId)  // we're past it.
				break;

			// Try the next property.
			ptr += s;
		}
	} else {
		portDie(MSG_UNIMPLEMENTED);
	}

	return 0;
}


uint16_t objectRelationGet(uint16_t objectId, uint8_t relationship) {
	uint32_t objPtr = objectPointerGet(objectId);
	uint16_t result = 0;

	if (storyVersion() <= 3) {
		result = ZPEEK(objPtr + relationship);
	} else {
		portDie(MSG_UNIMPLEMENTED);
	}

	return result;
}


void objectUnparent(uint16_t objectId) {
	uint32_t objPtr    = objectPointerGet(objectId);
	uint32_t parentPtr = objectPointerParentGet(objPtr);
	uint32_t ptr;

	if (parentPtr != 0) {
		if (storyVersion() <= 3) {
			ptr = parentPtr + 6;  // 4 to skip attrs, 2 to skip to child.
#ifdef DEBUGGING
			printf("Checking %X (%d) for %d\n", (unsigned int)ptr, ZPEEK(ptr), objectId);
#endif
			while (ZPEEK(ptr) != objectId) {  // If not direct child, look through sibling list..
				ptr = objectPointerGet(ZPEEK(ptr)) + 5;
#ifdef DEBUGGING
				printf("Checking %X (%d) for %d\n", (unsigned int)ptr, ZPEEK(ptr), objectId);
#endif
			}
			ZPOKE(ptr, ZPEEK(objPtr + 5));  // obj sibling takes obj's place.
#ifdef DEBUGGING
			printf("Setting %X to %d from %X\n", (unsigned int)ptr, ZPEEK(objPtr + 5), objPtr + 5);
#endif
		} else {
			portDie(MSG_UNIMPLEMENTED);
		}
	}
}

