#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#ifdef __STDC__
#include <memory.h>
#endif

#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "config.h"
#include "exec.h"

/*
 * Swap out programs from objects.
 */

int num_swapped;
int total_bytes_swapped;
char file_name[100];

FILE *swap_file;		/* The swap file is opened once */

int total_num_prog_blocks, total_prog_block_size;

extern int d_flag;

/*
 * marion - adjust pointers for swap out and later relocate on swap in
 *   program
 *   line_numbers
 *   functions
 *   strings
 *   variable_names
 *   inherit
 *   argument_types
 *   type_start
 */
int
locate_out (prog) struct program *prog; {
    char *p = 0; /* keep cc happy */

    if (!prog) return 0;
    if (d_flag > 1) {
	debug_message ("locate_out: %lX %lX %lX %lX %lX %lX %lX %lX\n",
	    prog->program, prog->line_numbers, prog->functions,
	    prog->strings, prog->variable_names, prog->inherit,
	    prog->argument_types, prog->type_start);
    }
    prog->program	= &p[prog->program - (char *)prog];
    prog->line_numbers	= (unsigned short *)
	&p[(char *)prog->line_numbers - (char *)prog];
    prog->functions	= (struct function *)
	&p[(char *)prog->functions - (char *)prog];
    prog->strings	= (char **)
	&p[(char *)prog->strings - (char *)prog];
    prog->variable_names= (struct variable *)
	&p[(char *)prog->variable_names - (char *)prog];
    prog->inherit	= (struct inherit *)
	&p[(char *)prog->inherit - (char *)prog];
    if (prog->type_start) {
	prog->argument_types = (unsigned short *)
	    &p[(char *)prog->argument_types - (char *)prog];
	prog->type_start = (unsigned short *)
	    &p[(char *)prog->type_start - (char *)prog];
    }
    return 1;
}


/*
 * marion - relocate pointers after swap in
 *   program
 *   line_numbers
 *   functions
 *   strings
 *   variable_names
 *   inherit
 *   argument_types
 *   type_start
 */
int
locate_in (prog) struct program *prog; {
    char *p = (char *)prog;

    if (!prog) return 0;
    prog->program	= &p[prog->program - (char *)0];
    prog->line_numbers	= (unsigned short *)
	&p[(char *)prog->line_numbers - (char *)0];
    prog->functions	= (struct function *)
	&p[(char *)prog->functions - (char *)0];
    prog->strings	= (char **)
	&p[(char *)prog->strings - (char *)0];
    prog->variable_names= (struct variable *)
	&p[(char *)prog->variable_names - (char *)0];
    prog->inherit	= (struct inherit *)
	&p[(char *)prog->inherit - (char *)0];
    if (prog->type_start) {
	prog->argument_types = (unsigned short *)
	    &p[(char *)prog->argument_types - (char *)0];
	prog->type_start     = (unsigned short *)
	    &p[(char *)prog->type_start - (char *)0];
    }
    if (d_flag > 1) {
	debug_message ("locate_in: %lX %lX %lX %lX %lX %lX %lX\n",
	    prog->program, prog->line_numbers, prog->functions,
	    prog->strings, prog->variable_names, prog->inherit,
	    prog->argument_types, prog->type_start);
    }
    return 1;
}

/*
 * Swap out an object. Only the program is swapped, not the 'struct object'.
 *
 * marion - the swap seems to corrupt the function table
 */
int swap(ob)
    struct object *ob;
{
    if (ob->flags & O_DESTRUCTED)
	return 0;
    if (d_flag > 1) { /* marion */
	debug_message("Swap object %s (ref %d)\n", ob->name, ob->ref);
    }
    if (swap_file == 0) {
#ifndef MSDOS
	char host[50];
	gethostname(host, sizeof host);
	sprintf(file_name, "%s.%s", SWAP_FILE, host);
	swap_file = fopen(file_name, "w+");
#else
	swap_file = fopen(strcpy(file_name,"LPMUD.SWAP"),"w+b");
#endif
	/* Leave this file pointer open ! */
	if (swap_file == 0)
	    return 0;
    }
    if (!ob->prog)
    {
	fprintf(stderr, "warning:no program in object %s, don't swap it\n",
		ob->name);
	/* It's no good freeing a NULL pointer */
	return 0;
    }
    if ((ob->flags & O_HEART_BEAT) || (ob->flags & O_CLONE)) {
	if (d_flag > 1) {
	    debug_message ("  object not swapped - heart beat or cloned.\n");
	}
	return 0;
    }
    if (ob->prog->ref > 1 || ob->interactive) {
	if (d_flag > 1) {
	    debug_message ("  object not swapped - inherited or interactive.\n");
	}
	return 0;
    }
    /*
     * Has this object already been swapped, and read in again ?
     * Then it is very easy to swap it out again.
     */
    if (ob->swap_num >= 0) {
	total_bytes_swapped += ob->prog->total_size;
	free_prog(ob->prog, 0);		/* Do not free the strings */
	ob->prog = 0;
	ob->flags |= O_SWAPPED;
	num_swapped++;
	return 1;
    }
    /*
     * marion - it is more efficient to write one item the size of the
     *   program to the file than many items of size one. besides, it's
     *   much more reasonable, as the fwrite only fails for the whole
     *   block and not for a part of it.
     */
    ob->swap_num = ftell(swap_file);
    locate_out (ob->prog); /* relocate the internal pointers */
    if (fwrite((char *)ob->prog, ob->prog->total_size, 1, swap_file) != 1) {
	debug_message("I/O error in swap.\n");
	ob->swap_num = -1;
	return 0;
    }
    total_bytes_swapped += ob->prog->total_size;
    num_swapped++;
    free_prog(ob->prog, 0);	/* Don't free the shared strings */
    ob->prog = 0;
    ob->flags |= O_SWAPPED;
    return 1;
}

void load_ob_from_swap(ob)
    struct object *ob;
{
    extern int errno;
    struct program tmp_prog;

    if (ob->swap_num == -1)
	fatal("Loading not swapped object.\n");
    if (fseek(swap_file, ob->swap_num, 0) == -1)
	fatal("Couldn't seek the swap file, errno %d, offset %d.\n",
	      errno, ob->swap_num);
    if (d_flag > 1) { /* marion */
	debug_message("Unswap object %s (ref %d)\n", ob->name, ob->ref);
    }
    /*
     * The size of the program is unkown, so read first part to
     * find out.
     *
     * marion - again, the read in a block is more efficient
     */
    if (fread((char *)&tmp_prog, sizeof tmp_prog, 1, swap_file) != 1) {
	fatal("Couldn't read the swap file.\n");
    }
    ob->prog = (struct program *)xalloc(tmp_prog.total_size);
    memcpy((char *)ob->prog, (char *)&tmp_prog, sizeof tmp_prog);
    fread((char *)ob->prog + sizeof tmp_prog,
	  tmp_prog.total_size - sizeof tmp_prog, 1, swap_file);
    /*
     * to be relocated:
     *   program
     *   line_numbers
     *   functions
     *   strings
     *   variable_names
     *   inherit
     *	 argument_types
     *	 type_start
     */
    locate_in (ob->prog); /* relocate the internal pointers */

    /* The reference count will already be 1 ! */
    ob->flags &= ~O_SWAPPED;
    total_bytes_swapped -= ob->prog->total_size;
    num_swapped--;
    total_prog_block_size += ob->prog->total_size;
    total_num_prog_blocks += 1;
    if (fseek(swap_file, 0L, 2) == -1) { /* marion - seek back for more swap */
	fatal("Couldn't seek end the swap file, errno %d.\n", errno);
    }
}

void remove_swap_file(ob)
    struct object *ob;
{
    /* Haven't implemented this yet :-( */
}

/*
 * This one is called at shutdown. Remove the swap file.
 */
void unlink_swap_file() {
    if (swap_file == 0)
	return;
#ifndef MSDOS
    unlink(file_name);
    fclose(swap_file);
#else
    fclose(swap_file);
    unlink(file_name);
#endif
}