/*
  File
*/

#include "file.h"

#include "config.h"
#include "console.h"
#include "head.h"
#include "interp.h"
#include "jump.h"
#include "os.h"
#include "page.h"
#include "pc.h"
#include "mem.h"
#include "shared.h"
#include "stack.h"
#include "stop.h"
#include "support.h"
#include "var.h"
#include "wio.h"

#define COOKIE "IDFZ" /* Cookie to indicate new compressed save file */

bool game_saved = 1;

static char *filename(void)
{
  static char name[257];
  getline((byte *) "Filename: ", name, sizeof(name) - 1);
  return name;
}

static bool put_word(filep save_file, word number)
{
  byte c[2];
  wr_word_ptr(c, number);
  return os_write(c, sizeof(byte), 2, save_file) != 2;
}

static bool ld_word(filep save_file, word *number)
{
  byte c[2];
  int res = os_read(c, sizeof(byte), 2, save_file);
  *number = rd_word_ptr(c);
  return res != 2;
}

static bool save_byte(byte b, filep file)
{
  return os_write(&b, sizeof(byte), 1, file) != 1;
}

static bool save_block(word page, filep file)
{
  int i = 0;
  bool err = 0;
  byte *p  = pg_fetch(page);

  hd_flip(page, p);
  while(i < BLOCK_SIZE && !err)
  {
    err = save_byte(p[i], file);
    if(p[i++] == 0 && !err)
    {
      int c = 1;
      while(i < BLOCK_SIZE && c < 256 && p[i] == 0) { ++c; ++i; }
      err = save_byte(c - 1, file);
    }
  }
  hd_flip(page, p);
  return err;
}

void save_game(void)
{
  filep save_file;
  bool err = 0;
  int i;
  char *name;

  game_saved = 0;
  name = filename();
  os_patch_save(name);
  if((save_file = os_open_write(name)) == (filep) 0)
  {
    if(!hd_plus())
      ret_value(0);
    else
      store((word) 0);
    return;
  }
  err = os_write(COOKIE, 1, sizeof(COOKIE), save_file) != sizeof(COOKIE);
  for(i = 0; i < hd_save_blocks() && !err; ++i)
    err = save_block(i, save_file);

  /* Save the Stack, Stack Pointers & Program Counter */

  err = err || put_word(save_file, stk_encode_stk());
  err = err || put_word(save_file, stk_encode_var());
  for(i = STACK_SIZE - stk_encode_stk(); i < STACK_SIZE && !err; ++i)
    err = put_word(save_file, stk_get_abs(i));
  err = err || put_word(save_file, pc_page());
  err = err || put_word(save_file, pc_offset());

  err = err || os_close(save_file);
  game_saved = !err;
  if(!hd_plus())
  {
    ret_value(!err);
  }
  else
  {
    store(!err);
  }
}

static bool find_cookie(filep file)
{
  char b[sizeof(COOKIE)];
  if(os_read(b, 1, sizeof(COOKIE), file) == sizeof(COOKIE)
  && os_strcmp(b, COOKIE) == 0)
  {
    return 1;
  }
  else
  {
    os_seek_fore(file, 0L);
    return 0;
  }
}

static bool load_byte(filep file, byte *b)
{
  return os_read(b, sizeof(byte), 1, file) != 1;
}

static bool load_block(word page, filep file)
{
  bool err = 0;
  byte *p  = pg_fetch(page);
  int i = 0;
  while(i < BLOCK_SIZE && !err)
  {
    err = load_byte(file, &p[i]);
    if(!err && p[i++] == 0)
    {
      byte c;
      err = load_byte(file, &c) || i + c > 512;
      if(!err)
      {
        os_mset((char *) &p[i], c, 0);
        i += c;
      }
    }
  }
  if(!err)
    hd_flip(page, p);
  return err;
}

void restore_game(void)
{
  filep save_file;
  bool err;
  word i;
  word temp;
  word blocks = hd_save_blocks();
  int high    = hd_height();
  int wide    = hd_width();
  char *name;

  game_saved = 0;
  name = filename();
  os_patch_save(name);
  if((save_file = os_open_read(name)) == (filep) 0)
  {
    if(!hd_plus())
      ret_value(0);
    else
      store((word) 0);
    return;
  }
  err = !find_cookie(save_file);
  for(i = 0; i < blocks && !err; ++i)
    err = load_block(i, save_file);
  hd_set_size(high, wide);

  err = err || ld_word(save_file, &temp);
  stk_decode_stk(temp);
  err = err || ld_word(save_file, &temp);
  stk_decode_var(temp);
  for(i = STACK_SIZE - stk_encode_stk(); i < STACK_SIZE && !err; ++i)
  {
    err = ld_word(save_file, &temp);
    stk_set_abs(i, temp);
  }
  {
    word page, offset;
    err = err || ld_word(save_file, &page);
    err = err || ld_word(save_file, &offset);
    set_pc(page, offset);
  }
  /* Should be at EOF now, or have the GAME: cookie */
  if(!err)
  {
    char test[5];
    int found = os_read(test, sizeof(char), 5, save_file);
    switch(found)
    {
      case 0:
        break; /* EOF */
      case 5:
        if(test[0] == 'G'
        && test[1] == 'A'
        && test[2] == 'M'
        && test[3] == 'E'
        && test[4] == ':')
#if 0
        /* This would stop users renaming the story file ... bad */
#endif
        break; /* Cookie */
      default:
        err = 1;
    }
  }

  err = os_close(save_file) || err;


  game_saved = !err;
  if(!err)
  {
    init_interpreter(0);
    if(!hd_plus())
      ret_value(1);
    else
      store(2);
  }
  else
  {
    display((byte *) "Wrong Game or Version ...\n");
    quit();
  }
}


/*
**      Scripting Routines and Variables.
*/

static filep script_file = (filep) 0;

void init_script(void)
{
  extern bool script_on;

  script_on = 0;
}

bool open_script(void)
{
  script_file = os_open_append(filename());
  return script_file != 0;
}

void close_script(void)
{
  extern filep script_file;

  if(script_file)
  {
    if(os_close(script_file))
      display((byte *) "Cannot Close Script File\n");
    script_file = 0;
  }
}

void script_char(word c)
{
  extern filep script_file;
  extern bool disable_script;
  extern bool script_on;

  if(1 || !hd_plus())
  {
    if(script_on && !hd_get_script())
    {
      script_on = 0;
      hd_set_script(0);
      close_script();
      return;
    }
    if(!script_on && hd_get_script())
    {
      script_on = 1;
      if(open_script())
        hd_set_script(1);
      else
        script_on = 0;
    }
  }

  if(script_on && script_file && !disable_script && kind(c) == 0)
  {
    byte b = c;
    os_write(&b, sizeof(b), 1, script_file);
  }
}


