/*
 * object.c
 *
 * Object manipulation routines.
 *
 */

#include "ztypes.h"

#define PARENT 0
#define NEXT 1
#define CHILD 2

#ifdef __STDC__
static zword_t read_object (zword_t objp, int field);
static void write_object (zword_t objp, int field, zword_t value);
#else
static zword_t read_object ();
static void write_object ();
#endif

/*
 * get_object_address
 *
 * Calculate the address of an object in the data area.
 *
 */

#ifdef __STDC__
zword_t get_object_address (zword_t obj)
#else
zword_t get_object_address (obj)
zword_t obj;
#endif
{
    int offset;

    /* Address calculation is object table base + size of default properties area +
       object number-1 * object size */

    if (h_type < V4)
        offset = h_objects_offset + ((P3_MAX_PROPERTIES - 1) * 2) + ((obj - 1) * O3_SIZE);
    else
        offset = h_objects_offset + ((P4_MAX_PROPERTIES - 1) * 2) + ((obj - 1) * O4_SIZE);

    return ((zword_t) offset);

}/* get_object_address */

/*
 * insert_object
 *
 * Insert object 1 as the child of object 2 after first removing it from its
 * previous parent. The object is inserted at the front of the child object
 * chain.
 *
 */

#ifdef __STDC__
void insert_object (zword_t obj1, zword_t obj2)
#else
void insert_object (obj1, obj2)
zword_t obj1;
zword_t obj2;
#endif
{
    zword_t obj1p, obj2p, child2;

#ifdef STRICTZ
    if (obj1 == 0) {
        report_strictz_error(STRZERR_MOVE_OBJECT, 
            "@move_object called moving object 0");
        return;
    }
    if (obj2 == 0) {
        report_strictz_error(STRZERR_MOVE_OBJECT_2, 
            "@move_object called moving into object 0");
        return;
    }
#endif

    /* Get addresses of both objects */

    obj1p = get_object_address (obj1);
    obj2p = get_object_address (obj2);

    /* Remove object 1 from current parent */

    remove_object (obj1);

    /* Make object 2 object 1's parent */

    write_object (obj1p, PARENT, obj2);

    /* Get current first child of object 2 */

    child2 = read_object (obj2p, CHILD);

    /* Make object 1 first child of object 2 */

    write_object (obj2p, CHILD, obj1);

    /* If object 2 had children then link them into the next child field of object 1 */

    if (child2)
        write_object (obj1p, NEXT, child2);

}/* insert_object */

/*
 * remove_object
 *
 * Remove an object by unlinking from the its parent object and from its
 * siblings.
 *
 */

#ifdef __STDC__
void remove_object (zword_t obj)
#else
void remove_object (obj)
zword_t obj;
#endif
{
    zword_t objp, parentp, childp, parent, child;

#ifdef STRICTZ
    if (obj == 0) {
        report_strictz_error(STRZERR_REMOVE_OBJECT, 
            "@remove_object called with object 0");
        return;
    }
#endif

    /* Get address of object to be removed */

    objp = get_object_address (obj);

    /* Get parent of object, and return if no parent */

    if ((parent = read_object (objp, PARENT)) == 0)
        return;

    /* Get address of parent object */

    parentp = get_object_address (parent);

    /* Find first child of parent */

    child = read_object (parentp, CHILD);

    /* If object is first child then just make the parent child pointer
       equal to the next child */

    if (child == obj)
        write_object (parentp, CHILD, read_object (objp, NEXT));
    else {

        /* Walk down the child chain looking for this object */

        do {
            childp = get_object_address (child);
            child = read_object (childp, NEXT);
        } while (child != obj);

        /* Set the next pointer thre previous child to the next pointer
           of the current object child pointer */

        write_object (childp, NEXT, read_object (objp, NEXT));
    }

    /* Set the parent and next child pointers to NULL */

    write_object (objp, PARENT, 0);
    write_object (objp, NEXT, 0);

}/* remove_object */

/*
 * load_parent_object
 *
 * Load the parent object pointer of an object
 *
 */

#ifdef __STDC__
void load_parent_object (zword_t obj)
#else
void load_parent_object (obj)
zword_t obj;
#endif
{

#ifdef STRICTZ
    if (obj == 0) {
        report_strictz_error(STRZERR_GET_PARENT, 
            "@get_parent called with object 0");
        store_operand(0);
        return;
    }
#endif
                                       
    store_operand (read_object (get_object_address (obj), PARENT));

}/* load_parent_object */

/*
 * load_child_object
 *
 * Load the child object pointer of an object and jump if the child pointer is
 * not NULL.
 *
 */

#ifdef __STDC__
void load_child_object (zword_t obj)
#else
void load_child_object (obj)
zword_t obj;
#endif
{
    zword_t child;

#ifdef STRICTZ
    if (obj == 0) {
        report_strictz_error(STRZERR_GET_CHILD, 
            "@get_child called with object 0");
        store_operand(0);
        conditional_jump(FALSE);
        return;
    }
#endif

    child = read_object (get_object_address (obj), CHILD);

    store_operand (child);

    conditional_jump (child != 0);

}/* load_child_object */

/*
 * load_next_object
 *
 * Load the next child object pointer of an object and jump if the next child
 * pointer is not NULL.
 *
 */

#ifdef __STDC__
void load_next_object (zword_t obj)
#else
void load_next_object (obj)
zword_t obj;
#endif
{
    zword_t next;

#ifdef STRICTZ
    if (obj == 0) {
        report_strictz_error(STRZERR_GET_SIBLING, 
            "@get_sibling called with object 0");
        store_operand(0);
        conditional_jump(FALSE);
        return;
    }
#endif

    next = read_object (get_object_address (obj), NEXT);

    store_operand (next);

    conditional_jump (next != 0);

}/* load_next_object */

/*
 * compare_parent_object
 *
 * Jump if object 2 is the parent of object 1
 *
 */

#ifdef __STDC__
void compare_parent_object (zword_t obj1, zword_t obj2)
#else
void compare_parent_object (obj1, obj2)
zword_t obj1;
zword_t obj2;
#endif
{

#ifdef STRICTZ
    if (obj1 == 0) {
        report_strictz_error(STRZERR_JIN, 
            "@jin called with object 0");
        conditional_jump(0 == obj2);
        return;
    }
#endif

    conditional_jump (read_object (get_object_address (obj1), PARENT) == obj2);

}/* compare_parent_object */

/*
 * test_attr
 *
 * Test if an attribute bit is set.
 *
 */

#ifdef __STDC__
void test_attr (zword_t obj, zword_t bit)
#else
void test_attr (obj, bit)
zword_t obj;
zword_t bit;
#endif
{
    zword_t objp;
    zbyte_t value;

    assert (O3_ATTRIBUTES == O4_ATTRIBUTES);

#ifdef STRICTZ
    if (obj == 0) {
        report_strictz_error(STRZERR_TEST_ATTR, 
            "@test_attr called with object 0");
        conditional_jump(FALSE);
        return;
    }
#endif

    /* Get attribute address */

    objp = get_object_address (obj) + (bit >> 3);

    /* Load attribute byte */

    value = get_byte (objp);

    /* Test attribute */

    conditional_jump ((value >> (7 - (bit & 7))) & 1);

}/* test_attr */

/*
 * set_attr
 *
 * Set an attribute bit.
 *
 */

#ifdef __STDC__
void set_attr (zword_t obj, zword_t bit)
#else
void set_attr (obj, bit)
zword_t obj;
zword_t bit;
#endif
{
    zword_t objp;
    zbyte_t value;

    assert (O3_ATTRIBUTES == O4_ATTRIBUTES);

#ifdef STRICTZ
    if (obj == 0) {
        report_strictz_error(STRZERR_SET_ATTR, 
            "@set_attr called with object 0");
        return;
    }
#endif

    /* Get attribute address */

    objp = get_object_address (obj) + (bit >> 3);

    /* Load attribute byte */

    value = get_byte (objp);

    /* Set attribute bit */

    value |= (zbyte_t) (1 << (7 - (bit & 7)));

    /* Store attribute byte */

    set_byte (objp, value);

}/* set_attr */

/*
 * clear_attr
 *
 * Clear an attribute bit
 *
 */

#ifdef __STDC__
void clear_attr (zword_t obj, zword_t bit)
#else
void clear_attr (obj, bit)
zword_t obj;
zword_t bit;
#endif
{
    zword_t objp;
    zbyte_t value;

    assert (O3_ATTRIBUTES == O4_ATTRIBUTES);

#ifdef STRICTZ
    if (obj == 0) {
        report_strictz_error(STRZERR_CLEAR_ATTR, 
            "@clear_attr called with object 0");
        return;
    }
#endif

    /* Get attribute address */

    objp = get_object_address (obj) + (bit >> 3);

    /* Load attribute byte */

    value = get_byte (objp);

    /* Clear attribute bit */

    value &= (zbyte_t) ~(1 << (7 - (bit & 7)));

    /* Store attribute byte */

    set_byte (objp, value);

}/* clear_attr */

#ifdef __STDC__
static zword_t read_object (zword_t objp, int field)
#else
static zword_t read_object (objp, field)
zword_t objp;
int field;
#endif
{
    zword_t value;

    if (h_type < V4) {
        if (field == PARENT)
            value = (zword_t) get_byte (PARENT3 (objp));
        else if (field == NEXT)
            value = (zword_t) get_byte (NEXT3 (objp));
        else
            value = (zword_t) get_byte (CHILD3 (objp));
    } else {
        if (field == PARENT)
            value = get_word (PARENT4 (objp));
        else if (field == NEXT)
            value = get_word (NEXT4 (objp));
        else
            value = get_word (CHILD4 (objp));
    }

    return (value);

}/* read_object */

#ifdef __STDC__
static void write_object (zword_t objp, int field, zword_t value)
#else
static void write_object (objp, field, value)
zword_t objp;
int field;
zword_t value;
#endif
{

    if (h_type < V4) {
        if (field == PARENT)
            set_byte (PARENT3 (objp), value);
        else if (field == NEXT)
            set_byte (NEXT3 (objp), value);
        else
            set_byte (CHILD3 (objp), value);
    } else {
        if (field == PARENT)
            set_word (PARENT4 (objp), value);
        else if (field == NEXT)
            set_word (NEXT4 (objp), value);
        else
            set_word (CHILD4 (objp), value);
    }

}/* write_object */
