/*
  Prop
*/

#include "prop.h"

#include "head.h"
#include "mem.h"
#include "object.h"
#include "shared.h"
#include "support.h"
#include "var.h"

#define STD_PROP_NUM_BITS       5
#define PLUS_PROP_NUM_BITS      6
#define STD_PROP_NUM_MASK       0x1F
#define PLUS_PROP_NUM_MASK      0x3F
#define NEXT_BYTE_IS_LENGTH     0x80U
#define STD_WORD_MASK           0x20U
#define PLUS_WORD_MASK          0x40U

#define STD_PROPERTY_LENGTH(p) \
  (word) ((rd_byte_addr(p) >> STD_PROP_NUM_BITS) + (word) 1)
#define PLUS_PROPERTY_LENGTH(p) \
  PLUS_PROPERTY_NUMBER(p)
#define STD_PROPERTY_NUMBER(p) \
  (rd_byte_addr(p) & STD_PROP_NUM_MASK)
#define PLUS_PROPERTY_NUMBER(p) \
  (byte) (rd_byte_addr(p) & PLUS_PROP_NUM_MASK)
#define WORD_MASK \
  (hd_plus() ? PLUS_WORD_MASK : STD_WORD_MASK)
#define PROPERTY_NUMBER(p) \
  (hd_plus() ? PLUS_PROPERTY_NUMBER(p) : STD_PROPERTY_NUMBER(p))

typedef long_word property;

static property prop_addr(word obj)
{
  property p = obj_prop(obj);
  return p + 2 * (long_word) rd_byte_addr(p) + 1;
}

static property std_next_addr(property p)
{
  return p + (long_word) STD_PROPERTY_LENGTH(p)  + 1;
}

static property plus_next_addr(property p)
{
  byte mode = rd_byte_addr(p++);
  return (mode & NEXT_BYTE_IS_LENGTH) ? p + PLUS_PROPERTY_LENGTH(p) + 1 :
         (mode & PLUS_WORD_MASK)      ? p + 2 : p + 1;
}

static property next_addr(property p)
{
  return hd_plus() ? plus_next_addr(p) : std_next_addr(p);
}

static property find_prop(word obj, word prop)
{
  property p = prop_addr(obj);
  if(hd_plus())
    while(PLUS_PROPERTY_NUMBER(p) > prop)
      p = plus_next_addr(p);
  else
    while(STD_PROPERTY_NUMBER(p) > prop)
      p = std_next_addr(p);
  return p;
}

void prop_getprop(word obj_num, word prop_num)
{
  word prop;
  property p = find_prop(obj_num, prop_num);
  word p_num = PROPERTY_NUMBER(p);
  if(p_num < prop_num)
    prop = rd_word_addr(hd_object() + (((long_word) prop_num - 1) << 1));
  else if(rd_byte_addr(p++) & WORD_MASK)
    prop = rd_word_addr(p);
  else
    prop = rd_byte_addr(p);
  store(prop);
}

void prop_put_prop(word obj_num, word prop_num, word value)
{
  property p = find_prop(obj_num, prop_num);
  word p_num = PROPERTY_NUMBER(p);
  if(p_num < prop_num)
    display((byte *) "Bad property number");
  else if(rd_byte_addr(p++) & WORD_MASK)
    wr_word_addr(p, value);
  else
    wr_byte_addr(p, value);
}

void prop_get_next_prop(word obj_num, word prop_num)
{
  property p;
  if(prop_num != 0)
  {
    p = find_prop(obj_num, prop_num);
    if(PROPERTY_NUMBER(p) < prop_num)
      display((byte *) "Bad property number");
    else
      p = next_addr(p);
  }
  else
  {
    p = prop_addr(obj_num);
  }
  store(PROPERTY_NUMBER(p));
}

void prop_get_prop_addr(word obj_num, word prop_num)
{
  property p = find_prop(obj_num, prop_num);
  word p_num = PROPERTY_NUMBER(p);
  if(p_num < prop_num)
    store(0);
  else if(hd_plus() && (rd_byte_addr(p) & NEXT_BYTE_IS_LENGTH))
    store((word) (p + 2));
  else
    store((word) (p + 1));
}

void prop_get_p_len(word prop)
{
  property p = (long_word) prop - 1;
#if 1 /* BODGE FOR CURSES */
  if(prop == 0)
    store(0); else
#endif
  if(!hd_plus())
    store(STD_PROPERTY_LENGTH((long_word) prop - 1));
  else if(rd_byte_addr(p) & NEXT_BYTE_IS_LENGTH)
    store(PLUS_PROPERTY_LENGTH(p));
  else if(rd_byte_addr(p) & PLUS_WORD_MASK)
    store(2);
  else
    store(1);
}

void load_word_array(word base, word offset)
{
  store(rd_word_addr(base + 2 * (long_word) offset));
}

void load_byte_array(word base, word offset)
{
  store(rd_byte_addr(base + (long_word) offset));
}

void save_word_array(word base, word offset, word value)
{
  wr_word_addr(base + 2 * (long_word) offset, value);
}

void save_byte_array(word base, word offset, word value)
{
  wr_byte_addr(base + (long_word) offset, value);
}
