/*
  Head
*/

#include "head.h"

#include "infocom.h"
#include "mem.h"
#include "os.h"
#include "page.h"
#include "shared.h"
#include "stop.h"
#include "support.h"
#include "wio.h"

static filep game_file;

/* Interpreter numbers */

#define XZIP             0
#define DEC_20           1
#define APPLE_2E         2
#define MACINTOSH        3
#define AMIGA            4
#define ATARI_ST         5
#define IBM_MSDOS        6
#define COMMODORE_128    7
#define C64              8
#define APPLE_2C         9
#define APPLE_2GS       10
#define TANDY_COLOR     11

#ifndef INTERPRETER
#define INTERPRETER     APPLE_2E
#endif

/* Script flags */

#define SCRIPT_MODE_ON      0x0001
#define USE_NON_PROP_FONT   0x0002
#define SCRIPT_ERROR        0x0400

/* Mode flags (change with version) */

#define mode3_status        0x02 /* What sort of status line */
#define mode3_tandy         0x08 /* Set for tandy interpreters */
#define mode3_non_status    0x10 /* Set if can't do status line */
#define mode3_can_split     0x20 /* Set if can split screen */
#define mode3_non_fixed     0x40 /* Set if using non-fixed-space fonts */

#define set_mode3           (mode3_status | mode3_can_split | mode3_non_fixed)
#define clr_mode3           (mode3_non_status)

#define mode4_can_colour    0x01 /* Colour available */
#define mode4_can_picture   0x02 /* Always on V4, If pictures on V6 */
#define mode4_can_bold      0x04 /* Bold available */
#define mode4_can_emph      0x08 /* Can do underline */
#define mode4_can_fixed     0x10 /* Can do fixed width */
#define mode4_can_sound     0x20 /* Sound effects */
#define mode4_can_timed     0x80 /* Can do timed inputs */

#define set_mode4           (mode4_can_colour | mode4_can_bold | mode4_can_emph | mode4_can_fixed)
#define clr_mode4           (mode4_can_sound | mode4_can_timed)

/* Offsets into the header, all are 1 byte in extent unless specified */

#define z_code_version        0 /* Game's Z-CODE Version Number      */
#define mode_bits             1 /* Status Bar display indicator      */
#define release               2 /* 2 bytes for game release number   */
#define resident_bytes        4 /* 2 bytes giving resident bytes     */
#define start                 6 /* 2 bytes, offset to game start     */
#define vocab                 8 /* 2 bytes, offset to vocabulary     */
#define object_list          10 /* 2 bytes, offset to objects        */
#define globals              12 /* 2 bytes, offset to globals        */
#define save_bytes           14 /* 2 bytes, length to save to disk   */
#define script_status        16 /* 2 bytes, printing modes           */
#define serial_no            18 /* 6 bytes, serial number            */
#define common_word          24 /* 2 bytes, offset to common words   */
#define verify_length        26 /* 2 bytes, total game file size     */
#define verify_checksum      28 /* 2 bytes, checksum for verify      */
#define interpreter_number   30 /* 1 byte, set by interpreter        */
#define interpreter_version  31 /* 1 byte, letter set by interpreter */
#define screen_height        32 /* 1 byte, set by interpreter        */
#define screen_width         33 /* 1 byte, set by interpreter        */
#define left                 34 /* 1 byte, set by interpreter        */
#define right                35 /* 1 byte, set by interpreter        */
#define top                  36 /* 1 byte, set by interpreter        */
#define bottom               37 /* 1 byte, set by interpreter        */
#define unknown1             38 /* 1 unknown, set by interpreter     */
#define unknown2             39 /* 1 unknown, set by interpreter     */
#define code_offset          40 /* 1 word, code offset in longs (>V5)*/
#define text_offset          42 /* 1 word, code offset in longs (>V5)*/
#define default_back         44 /* 1 byte, default paper set by interpreter */
#define default_fore         45 /* 1 byte, default ink set by interpreter */
#define unknown5             46 /* 1 unknown, set in data file       */
#define padding2             48 /* 6 blanks                          */
#define unknown6             54 /* 2 unknowns                        */
#define padding3             56 /* 8 blanks                          */

/* For a total of 64 bytes of header */

int hd_open(char *filename)
{
  game_file = os_wild_read(filename);
  return game_file != 0;
}

void hd_close(void)
{
  os_close(game_file);
}

void hd_load(word block, word num_blocks, byte *ptr)
{
  int wide    = hd_width();
  int high    = hd_height();
  long_word offset = ((long_word) block) << BLOCK_SHIFT;
  if(os_seek_fore(game_file, offset) < 0)
  {
    display((byte *) "Failed to Seek required Blocks\n");
    quit();
  }
  /* Need the -1 since we may want a final (incomplete) block */
  else if(os_read(ptr, BLOCK_SIZE, num_blocks, game_file) < num_blocks - 1)
  {
    display((byte *) "Failed to Read required Blocks\n");
    quit();
  }
  hd_set_size(high, wide);
}

void hd_flip(word page, byte *block)
{
#if 0
  int i;
  byte b[BLOCK_SIZE];
  hd_load(page, 1, b);
  for(i = 0; i < BLOCK_SIZE; ++i)
    block[i] ^= b[i];
#else
  byte b[BLOCK_SIZE];
  word *s = (void *) b;
  word *d = (void *) block;
  int c = BLOCK_SIZE / (2 * sizeof(*s));
  hd_load(page, 1, b);
  do { *d ^= *s; *++d ^= *++s; ++d; ++s; } while(--c);
#endif
}

void hd_no_colour(void)
{
  if(hd_plus())
    base_ptr[mode_bits] &= ~mode4_can_sound;
}

void hd_init(void)
{
  base_ptr[interpreter_number]  = INTERPRETER;
  base_ptr[interpreter_version] = 'A';
  if(hd_plus())
    base_ptr[mode_bits] = (base_ptr[mode_bits] | set_mode4) & ~clr_mode4;
  else
    base_ptr[mode_bits] = (base_ptr[mode_bits] | set_mode3) & ~clr_mode3;
  if(hd_five())
  {
    base_ptr[left]         = 0;
    base_ptr[top]          = 0;
    base_ptr[unknown1]     = 1;
    base_ptr[unknown2]     = 1;
    base_ptr[default_back] = 9;
    base_ptr[default_fore] = 2;
  }
}

void hd_set_size(int high, int wide)
{
  base_ptr[screen_width]  = wide;
  base_ptr[screen_height] = high - 1;
  base_ptr[right]         = wide;
  base_ptr[bottom]        = high - 1;
}

int hd_width(void)
{
  return base_ptr[screen_width];
}

int hd_height(void)
{
  return (int) base_ptr[screen_height] + 1;
}

version hd_version(void)
{
  return base_ptr[z_code_version];
}

/*
  hd_resident_blocks could actually use save_bytes rather than
  resident_bytes -- Graham calls these

  dynamic memory                        (can be changed)
                <-- split defined by $0e, so save_bytes
  static memory                         (assumed resident)
                <-- split defined by $04, so resident_bytes
  high_memory                           (used for code and strings)

  Doing so would mean that pg_delta packed pages less often
*/

word hd_resident_blocks(void)
{
#if 0
  return pg_blocks(rd_word_ptr(base_ptr + resident_bytes));
#else
  return pg_blocks(rd_word_ptr(base_ptr + save_bytes));
#endif
}

word hd_verify(void)
{
  return rd_word_ptr(base_ptr + verify_length);
}

word hd_save_blocks(void)
{
  return pg_blocks(rd_word_ptr(base_ptr + save_bytes));
}

word hd_start(void)
{
  return rd_word_ptr(base_ptr + start);
}

byte hd_mode(void)
{
  return base_ptr[mode_bits];
}

void hd_set_screen(void)
{
  wr_word_ptr(base_ptr + mode_bits, rd_word_ptr(base_ptr + mode_bits) | SCREEN_MODES);
}

word hd_check(void)
{
  return rd_word_ptr(base_ptr + verify_checksum);
}

static word hd_script(void)
{
  return rd_word_ptr(base_ptr + script_status);
}

void hd_set_script(int on)
{
  if(on)
    wr_word_ptr(base_ptr + script_status, rd_word_ptr(base_ptr + script_status) | SCRIPT_MODE_ON);
  else
    wr_word_ptr(base_ptr + script_status, rd_word_ptr(base_ptr + script_status) & ~ SCRIPT_MODE_ON);
}

void hd_set_fixed(int on)
{
  if(on)
    wr_word_ptr(base_ptr + script_status, rd_word_ptr(base_ptr + script_status) | USE_NON_PROP_FONT);
  else
    wr_word_ptr(base_ptr + script_status, rd_word_ptr(base_ptr + script_status) & ~USE_NON_PROP_FONT);
}

bool hd_get_script(void)
{
  return (hd_script() & SCRIPT_MODE_ON) != 0;
}

bool hd_get_fixed(void)
{
  return (hd_script() & USE_NON_PROP_FONT) != 0;
}

void hd_err_script(void)
{
  wr_word_ptr(base_ptr + script_status, SCRIPT_ERROR);
}

word hd_object(void)
{
  return rd_word_ptr(base_ptr + object_list);
}

word hd_global(void)
{
  return rd_word_ptr(base_ptr + globals);
}

word hd_common(void)
{
  return rd_word_ptr(base_ptr + common_word);
}

word hd_vocab(void)
{
  return rd_word_ptr(base_ptr + vocab);
}

int hd_plus(void)
{
  return hd_version() >= VERSION_4;
}

int hd_five(void)
{
  return hd_version() >= VERSION_5;
}

static word hd_offset(word w)
{
  switch(hd_version())
  {
    case VERSION_6:
    case VERSION_7:
      return rd_word_ptr(base_ptr + w);
    default:
      return 0;
  }
}

word hd_code_offset(void)
{ return hd_offset(code_offset); }

word hd_text_offset(void)
{ return hd_offset(text_offset); }

void hd_request_status(void)
{
  /* Set the header bit asking for a redraw? */
}
