// zeal - A portable Glk-based Z-code interpreter
// Copyright (C) 2000 Jeremy Condit <jcondit@eecs.harvard.edu>
// 
// This program 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
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

// =======================================================================
//  unix.cc:
//
//  this module contains support for unix-specific glk features (for
//  glkterm and xglk).  it creates an osdep structure and places it in the
//  global os variable for glk_main to find.
// =======================================================================

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>

#include "zeal.h"
#include "osdep.h"

extern "C" {
#include "glkstart.h"
}

extern osdep* os;

// =======================================================================
//  unix
// =======================================================================

class unix : public osdep {
    public:
        unix(char* story_name, char* debug_name, int strictz_in);
        virtual ~unix();

        virtual unsigned long get_seed();
        virtual strid_t get_story();
        virtual void close_story();
        virtual uint get_strictz();
        virtual bool debug();
        virtual void debug_msg(const char* format, ...);

    private:
        strid_t story_stream;
        FILE* debug_file;
        uint strictz;
};

// constructor
//
// open the appropriate streams.

unix::unix(char* story_name, char* debug_name, int strictz_in)
  : story_stream(NULL), debug_file(NULL), strictz(strictz_in)
{
    if (story_name != NULL) {
        if (strchr(story_name, '/') != NULL) {
            story_stream = glkunix_stream_open_pathname(story_name, 0, 0);
        } else {
            const char* path_env = getenv("INFOCOM_PATH");
            if (path_env == NULL || strlen(path_env) == 0) {
                path_env = ".";
            }

            char* path = new char [strlen(path_env) + 1];
            strcpy(path, path_env);

            char* dir = strtok(path, ":");
            do {
                char* file = new char [strlen(dir) + strlen(story_name) + 2];
                sprintf(file, "%s/%s", dir, story_name);
                story_stream = glkunix_stream_open_pathname(file, 0, 0);
                delete [] file;
            } while (story_stream == NULL && (dir = strtok(NULL, ":")) != NULL);

            delete [] path;
        }
    }
    if (debug_name != NULL) {
        debug_file = fopen(debug_name, "w");
    }
}

// destructor
//
// close those stream.s

unix::~unix()
{
    if (story_stream != NULL) {
        glk_stream_close(story_stream, NULL);
    }
    if (debug_file != NULL) {
        fclose(debug_file);
    }
}

// get_seed
//
// seed the random number generator using the current time.

unsigned long
unix::get_seed()
{
    return time(NULL);
}

// get_story
//
// get the current story file.

strid_t
unix::get_story()
{
    return story_stream;
}

// close_story
//
// no reason to keep this file open the whole time.  this method lets us
// close it as soon as glk_main has created the machine.

void
unix::close_story()
{
    glk_stream_close(story_stream, NULL);
    story_stream = NULL;
}

// get_strictz
//
// get the strictz setting specified by the user.

uint
unix::get_strictz()
{
    return strictz;
}

// debug
//
// are we sending output to the debug file?

bool
unix::debug()
{
    return (debug_file != NULL);
}

// debug_msg
//
// send a message to the debug log.

void
unix::debug_msg(const char* format, ...)
{
    if (debug_file != NULL) {
        va_list args;
        va_start(args, format);
        vfprintf(debug_file, format, args);
        va_end(args);
    }
}

// =======================================================================
//  glkunix stuff
// =======================================================================

// static data
//
// arguments to be expected by glk.

glkunix_argumentlist_t glkunix_arguments[] = {                
    { "", glkunix_arg_ValueFollows, "filename: The game file to load." },
    { "-strictz", glkunix_arg_NumberValue, "-strictz: Set warning level." },
    { "-d", glkunix_arg_NoValue, "-d: Turn debugging info on." },
    { NULL, glkunix_arg_End, NULL }
};

// glkunix_startup_code
//
// called by glkterm/xglk before calling glk_main.  examines args
// structure and creates corresponding osdep structure.

int
glkunix_startup_code(glkunix_startup_t* data)
{
    char* story_name = NULL;
    char* debug_name = NULL;
    int strictz = 1;

    for (int i = 1; i < data->argc; i++) {
        if (strcmp(data->argv[i], "-d") == 0) {
            debug_name = "debug.log";
        } else if (strcmp(data->argv[i], "-strictz") == 0) {
            strictz = atoi(data->argv[++i]);
        } else {
            story_name = data->argv[i];
        }
    }

    os = new unix(story_name, debug_name, strictz);

    return TRUE;
}
