/*
  PG_Flat
  Keep all bytes resident; much faster but memory hungry
  (Should exploit the knowledge that the memory is flat in mem)
*/

#include "page.h"

#include "alloc.h"
#include "head.h"
#include "interp.h"
#include "mem.h"
#include "os.h"
#include "pc.h"
#include "stack.h"
#include "stop.h"
#include "var.h"

typedef byte block[BLOCK_SIZE];

byte *base_ptr;

static word minpages;
static block *lock;

int pg_head(void)
{
  base_ptr = alloc(1, block);
  hd_load(0, 1, base_ptr);
  return base_ptr != 0;
}

int pg_init(word minimum, word maximum)
{
  minpages = minimum;
  lock = alloc(maximum, block);
  os_mcpy(lock[0], base_ptr, sizeof(block));
  base_ptr  = lock[0];
  hd_load(1, maximum - 1, lock[1]);
  return 1;
}

byte *pg_fetch(word new_page)
{
  return lock[new_page];
}


/* Additions for undo */

static byte *undo = 0;
static int undo_size = 0;

static int needed(void)
{
  /* Paragraphs needed for the undo buffer */
  word need = minpages * sizeof(block)
            + 2 * sizeof(word)
            + sizeof(stack_space)
            + sizeof(var_ptr)
            + sizeof(stack_ptr)
            ;
  return need;
}

static void append(int *off, void *from, word size)
{
  os_mcpy(undo + *off, from, size);
  *off += size;
}

void pg_save_undo(void)
{
  word result = 0; /* Failed */
  int need = needed();
  if(undo == 0)
  {
    undo_size = need;
    undo = alloc(undo_size, byte);
  }
  if(undo_size < need)
  {
    undo_size = need;
    undo = os_realloc(undo, undo_size);
  }
  if(undo)
  {
    int off = 0;
    word p = pc_page();
    word o = pc_offset();
    append(&off, lock,           minpages * sizeof(block));
    append(&off, &p,             sizeof(p));
    append(&off, &o,             sizeof(o));
    append(&off, stack_space,    sizeof(stack_space));
    append(&off, &var_ptr,       sizeof(var_ptr));
    append(&off, &stack_ptr,     sizeof(stack_ptr));
    result = 1;
  }
  store(result);
}

static void restore(int *off, void *to, word size)
{
  os_mcpy(to, undo + *off, size);
  *off += size;
}

void pg_restore_undo(void)
{
  if(undo)
  {
    int off = 0;
    word p, o;
    restore(&off, lock,           minpages * sizeof(block));
    restore(&off, &p,             sizeof(p));
    restore(&off, &o,             sizeof(o));
    restore(&off, stack_space,    sizeof(stack_space));
    restore(&off, &var_ptr,       sizeof(var_ptr));
    restore(&off, &stack_ptr,     sizeof(stack_ptr));
    set_pc(p, o);
    init_interpreter(0);
    store(2);
  }
  else
  {
    display("Undo attempted when not valid.");
    quit();
  }
}
