/* Win32-specific code for Xconq kernel.
   Copyright (C) 1999 Stanley T. Shebs.

Xconq is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.  See the file COPYING.  */

/* Win32 interface stuff.  Do NOT attempt to use this file in a
   non-Windows system! */

/* Also note that this file does not include all Xconq .h files, since
   it may be used with auxiliary programs. */

#include "config.h"
#include "misc.h"
#include "dir.h"
#include "lisp.h"
#include "module.h"
#include "system.h"

extern void close_displays(void);

#include <signal.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>

/* (should flush the following includes?) */
#include <sys/file.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/ioctl.h>

/* Default names for places. */

/* (should change default passed in by compiler instead) */
#undef XCONQDATA

#ifndef XCONQDATA
#define XCONQDATA ".."
#endif

#ifndef XCONQLIB
#define XCONQLIB "lib"
#endif

#ifndef XCONQIMAGES
#define XCONQIMAGES "images"
#endif

#ifndef XCONQSCORES
#define XCONQSCORES "scores"
#endif

static char *game_homedir(void);
static char *game_filename(char *namevar, char *defaultname);
static char *score_file_pathname(char *name);

uid_t games_uid;

char *
default_library_pathname(void)
{
    char *name;

    name = xmalloc(strlen(XCONQDATA) + 1 + strlen(XCONQLIB) + 1);
    strcpy(name, XCONQDATA);
    strcat(name, "\\");
    strcat(name, XCONQLIB);
    return name;
}

char *
default_images_pathname(void)
{
    char *name;

    name = xmalloc(strlen(XCONQDATA) + 1 + strlen(XCONQIMAGES) + 1);
    strcpy(name, XCONQDATA);
    strcat(name, "\\");
    strcat(name, XCONQIMAGES);
    return name;
}

char *
news_filename(void)
{
    /* (should search in library list) */
    make_pathname(xconq_libs->path, NEWSFILE, "", spbuf);
    return spbuf;
}

static char *savegamename;

char *
saved_game_filename(void)
{
    if (savegamename == NULL)
      savegamename = game_filename("XCONQSAVEFILE", SAVEFILE);
    return savegamename;
}

char *
checkpoint_filename(int n)
{
    char *str, *home;

    home = game_homedir();
    str = xmalloc(strlen(home) + 40);
    sprintf(str, "%s\\Check%d.Xconq", home, n);
    return str;
}

static char *preferences_name;

char *
preferences_filename(void)
{
    if (preferences_name == NULL)
      preferences_name = game_filename("XCONQPREFERENCES", PREFERENCESFILE);
    return preferences_name;
}

char *
error_save_filename(void)
{
    /* No need to cache name, will only get requested once. */
    return game_filename("XCONQERRORFILE", ERRORFILE);
}

char *
statistics_filename(void)
{
    /* No need to cache name, will only get requested once. */
    return game_filename("XCONQSTATSFILE", STATSFILE);
}

static char *homedir;

static char *
game_homedir(void)
{
    char *str;
    struct passwd *pwd;

    if (homedir != NULL)
      return homedir;
    if ((str = getenv("XCONQ_HOME")) != NULL) {
	homedir = copy_string(str);
    } else if ((str = getenv("HOME")) != NULL) {
	homedir = xmalloc(strlen(str) + 20);
	strcpy(homedir, str);
	strcat(homedir, "/_xconq");
    } else if ((pwd = getpwuid(getuid())) != NULL) {
	homedir = xmalloc(strlen(pwd->pw_dir) + 20);
	strcpy(homedir, pwd->pw_dir);
	strcat(homedir, "/_xconq");
    } else {
	homedir = ".";
    }
    /* Try to ensure that the directory exists. */
    if (access(homedir, F_OK) != 0) {
	mkdir(homedir, 0755);
	/* (should warn of problems) */
    }
    return homedir;
}

static char *
game_filename(char *namevar, char *defaultname)
{
    char *str, *home;

    if ((str = getenv(namevar)) != NULL && *str)
      return copy_string(str);
    home = game_homedir();
    str = xmalloc(strlen(home) + 1 + strlen(defaultname) + 1);
    strcpy(str, home);
    strcat(str, "\\");
    strcat(str, defaultname);
    return str;
}

/* This wrapper replaces fopen everywhere in the kernel. On the mac
   side it does unix-to-mac linefeed conversion if necessary. Here it
   does absolutely nothing. */

FILE *
open_file(char *filename, char *mode)
{
	return fopen(filename, mode);
}

/* Attempt to open a library file. */

FILE *
open_module_library_file(Module *module)
{
    LibraryPath *p;
    FILE *fp;

    /* Don't try to do on anon modules? */
    if (module->name == NULL)
      return NULL;
    for_all_library_paths(p) {
	/* Generate library pathname. */
	make_pathname(p->path, module->name, "g", spbuf);
	/* Now try to open the file. */
	fp = open_file(spbuf, "r");
	if (fp != NULL) {
	    /* Remember the filename where we found it. */
	    module->filename = copy_string(spbuf);
	    return fp;
	}
    }
    return NULL;
}

FILE *
open_module_explicit_file(Module *module)
{
    if (module->filename == NULL)
      return NULL;
    return (open_file(module->filename, "r"));
}

FILE *
open_library_file(char *filename)
{
    char fullnamebuf[1024];
    LibraryPath *p;
    FILE *fp = NULL;

    fp = open_file(filename, "r");
    if (fp != NULL) {
	return fp;
    }
    for_all_library_paths(p) {
	/* Generate library pathname. */
	make_pathname(p->path, filename, NULL, fullnamebuf);
	fp = open_file(fullnamebuf, "r");
	if (fp != NULL) {
	    return fp;
	}
    }
    return NULL;
}

FILE *
open_scorefile_for_reading(char *name)
{
    char *fname;
    FILE *fp;

    fname = score_file_pathname(name);
    fp = open_file(fname, "r");
    if (fp == NULL) {
	run_warning("Unable to open \"%s\" for reading;
will retry once if you continue", fname);
	fp = open_file(fname, "r");
    }
    return fp;
}

FILE *
open_scorefile_for_writing(char *name)
{
    char *fname;
    FILE *fp;

    fname = score_file_pathname(name);
    fp = open_file(fname, "a");
    if (fp == NULL) {
	run_warning("Unable to open \"%s\" for writing;
will retry once if you continue", fname);
	fp = open_file(fname, "a");
    }
    return fp;
}

void
close_scorefile_for_writing(FILE *fp)
{
    fclose(fp);
}

static char *scorenamebuf;

static char *
score_file_pathname(char *name)
{
    char *scorepath, extrabuf[BUFSIZE];

    /* (Note that this wires in the name on the first call, so cannot
       be called with different names.  We could make this smarter, but
       no point to it right now.) */
    if (scorenamebuf == NULL) {
	scorepath = getenv("XCONQ_SCORES");
	if (empty_string(scorepath)) {
	    strcpy(extrabuf, XCONQDATA);
	    strcat(extrabuf, "\\");
	    strcat(extrabuf, XCONQSCORES);
	    scorepath = extrabuf;
	}
	scorenamebuf = xmalloc(strlen(scorepath) + 1 + strlen(name) + 10);
	make_pathname(scorepath, name, NULL, scorenamebuf);
    }
    return scorenamebuf;
}

void
make_pathname(char *path, char *name, char *extn, char *pathbuf)
{
    strcpy(pathbuf, "");
    if (!empty_string(path)) {
	strcat(pathbuf, path);
	strcat(pathbuf, "/");
    }
    strcat(pathbuf, name);
    /* Don't add a second identical extension, but do add if extension
       is different (in case we want "foo.12" -> "foo.12.g" for instance) */
    if (strrchr(name, '.')
	&& extn
	&& strcmp((char *) strrchr(name, '.') + 1, extn) == 0)
      return;
    if (!empty_string(extn)) {
	strcat(pathbuf, ".");
	strcat(pathbuf, extn);
    }
}

/* Remove a given file. */

int
remove_file(char *fname)
{
    unlink(fname);
    /* (should return real outcome) */
    return TRUE;
}

/* Default behavior on explicit kill. */

void stop_handler(int sig);
void crash_handler(int sig);
void hup_handler(int sig);

void
stop_handler(int sig)
{
    close_displays();
    exit(1);
}

/* This routine attempts to save the state before dying. */

void
crash_handler(int sig)
{
    static int already_been_here = FALSE;

    if (!already_been_here) {
	already_been_here = TRUE;
	close_displays();  
	printf("Fatal error encountered. Signal %d\n", sig);
	write_entire_game_state(error_save_filename());
    }
    abort();
}

/* Accidental disconnection saves state. */

void
hup_handler(int sig)
{
    static int already_been_here = FALSE;

    if (!already_been_here) {
	already_been_here = TRUE;
	close_displays();
	printf("Somebody was disconnected, saving the game.\n");
	write_entire_game_state(error_save_filename());
    }
    abort();
}

void
init_signal_handlers(void)
{
    signal(SIGINT, stop_handler);
    if (0 /* don't accidently quit */ && !Debug) {
	signal(SIGINT, SIG_IGN);
    } else {
	signal(SIGINT, SIG_DFL);
/*	signal(SIGINT, crash_handler);  */
    }
    signal(SIGHUP, hup_handler);
    signal(SIGSEGV, crash_handler);
    signal(SIGFPE, crash_handler);
    signal(SIGILL, crash_handler);
    signal(SIGINT, crash_handler);
    signal(SIGQUIT, crash_handler);
    signal(SIGTERM, crash_handler);
    /* The following signals may not be available everywhere. */
#ifdef SIGBUS
    signal(SIGBUS, crash_handler);
#endif /* SIGBUS */
#ifdef SIGSYS
    signal(SIGSYS, crash_handler);
#endif /* SIGSYS */
}

struct timeval reallasttime = { 0, 0 };

struct timeval realcurtime;

int
n_seconds_elapsed(int n)
{
    gettimeofday(&realcurtime, NULL);
    if (realcurtime.tv_sec > (reallasttime.tv_sec + (n - 1))) {
	reallasttime = realcurtime;
	return TRUE;
    } else {
	return FALSE;
    }
}

/* Returns true if n milliseconds have passed since the time was recorded
   via record_ms(). */

struct timeval reallastmstime = { 0, 0 };

int
n_ms_elapsed(int n)
{
    int interval;
    struct timeval tmprealtime;

    gettimeofday(&tmprealtime, NULL);
    interval =
      (tmprealtime.tv_sec - reallastmstime.tv_sec) * 1000
	+ (tmprealtime.tv_usec - reallastmstime.tv_usec) / 1000;
    return (interval > n);
}

/* Record the current time of day. */

void
record_ms(void)
{
    gettimeofday(&reallastmstime, NULL);
}
