/*
// ColdMUD was created and is copyright 1993, 1994 by Greg Hudson
//
// ColdX is a derivitive work, and is copyright 1995 by the ColdX Project.
// Full copyright information can be found in the file doc/CREDITS
//
// File:        miscop.c
// Version:     0.1-5
// Last Edited: 5 Aug 1995
//
// ---
//
// Miscellaneous operations.
*/

/* #define _POSIX_SOURCE */

#include "config.h"

#include <stdlib.h>
#include <time.h>
#include <sys/time.h>    /* for mtime() */
#include "defs.h"
#include "x.tab.h"
#include "operator.h"
#include "execute.h"
#include "data.h"
#include "util.h"
#include "ident.h"
#include "lookup.h"

static void find_extreme(int which);

void op_version(void)
{
    List *version;
    Data *d;

    /* Take no arguments. */
    if (!func_init_0())
	return;

    /* Construct a list of the version numbers and push it. */
    version = list_new(3);
    d = list_empty_spaces(version, 3);
    d[0].type = d[1].type = d[2].type = INTEGER;
    d[0].u.val = VERSION_MAJOR;
    d[1].u.val = VERSION_MINOR;
    d[2].u.val = VERSION_PATCH;
    push_list(version);
    list_discard(version);
}

void op_random(void)
{
    Data *args;

    /* Take one integer argument. */
    if (!func_init_1(&args, INTEGER))
	return;

    /* Replace argument on stack with a random number. */
    args[0].u.val = random_number(args[0].u.val) + 1;
}

void op_time(void)
{
    /* Take no arguments. */
    if (!func_init_0())
	return;

    push_int(time(NULL));
}

void op_localtime(void)
{
    struct tm * tms;
    Data * d;
    List * l;
    time_t t;
    register int x;

    if (!func_init_0())
	return;

    time(&t);
    tms = localtime(&t);

#if defined(Solaris) || defined(IRIX)
#define __LSIZE0__ 10
#define __LSIZE1__ 10
#else
#define __LSIZE0__ 11
#define __LSIZE1__ 12
#endif

    l = list_new(__LSIZE0__);
    d = list_empty_spaces(l, __LSIZE0__);
    for (x=0; x < __LSIZE1__; x++)
        d[x].type = INTEGER;

    d[0].u.val = (int) t;
    d[1].u.val = tms->tm_sec;
    d[2].u.val = tms->tm_min;
    d[3].u.val = tms->tm_hour;
    d[4].u.val = tms->tm_mday;
    d[5].u.val = tms->tm_mon;
    d[6].u.val = tms->tm_year;
    d[7].u.val = tms->tm_wday;
    d[8].u.val = tms->tm_yday;
    d[9].u.val = tms->tm_isdst;
#if !defined(Solaris) && !defined(IRIX)
    d[10].u.val = tms->tm_gmtoff;
    d[11].type = STRING;
    d[11].u.str = string_from_chars(tms->tm_zone, strlen(tms->tm_zone));
#endif

    push_list(l);
    list_discard(l);
}

/* May as well give it to them, the code is in the driver */
void op_timestamp(void) {
    String * str;
    char   * s;

    /* Take no arguments. */
    if (!func_init_0())
        return;

    s = timestamp(NULL);
    str = string_from_chars(s, strlen(s));

    push_string(str);
    string_discard(str);
}

void op_strftime(void) {
    char        s[LINE];
    char      * fmt;
    Data      * args;
    String    * str;
    int         nargs;
    time_t      tt;
    struct tm * t;

    if (!func_init_1_or_2(&args, &nargs, STRING, INTEGER))
	return;

    tt = ((nargs == 2) ? (time_t) args[1].u.val : time(NULL));
    t  = localtime(&tt);
 
    fmt = string_chars(args[0].u.str);

    /* some OS's are weird and do odd things when you end in %
       (accidentally or no) */
    if (fmt[strlen(fmt)] == '%')
        fmt[strlen(fmt)] = NULL;

    if (strftime(s, LINE, fmt, t) == (size_t) 0) {
        cthrow(range_id,
               "Format results in a string longer than 80 characters.");
        return;
    }

    str = string_from_chars(s, strlen(s));

    pop(nargs);
    push_string(str);
    string_discard(str);
}

void op_mtime(void) {
    struct timeval tp;

    /* Take no arguments. */
    if (!func_init_0())
        return;

    gettimeofday(&tp, NULL);

    /* usec is microseconds */
    push_int((int) tp.tv_usec);
}

void op_ctime(void)
{
    Data *args;
    int num_args;
    time_t tval;
    char *timestr;
    String *str;

    /* Take an optional integer argument. */
    if (!func_init_0_or_1(&args, &num_args, INTEGER))
	return;

    tval = (num_args) ? args[0].u.val : time(NULL);
    timestr = ctime(&tval);
    str = string_from_chars(timestr, 24);

    pop(num_args);
    push_string(str);
    string_discard(str);
}

/* which is 1 for max, -1 for min. */
static void find_extreme(int which)
{
    int arg_start, num_args, i, type;
    Data *args, *extreme, d;

    arg_start = arg_starts[--arg_pos];
    args = &stack[arg_start];
    num_args = stack_pos - arg_start;

    if (!num_args) {
	cthrow(numargs_id, "Called with no arguments, requires at least one.");
	return;
    }

    type = args[0].type;
    if (type != INTEGER && type != STRING) {
	cthrow(type_id, "First argument (%D) not an integer or string.",
	      &args[0]);
	return;
    }

    extreme = &args[0];
    for (i = 1; i < num_args; i++) {
	if (args[i].type != type) {
	    cthrow(type_id, "Arguments are not all of same type.");
	    return;
	}
	if (data_cmp(&args[i], extreme) * which > 0)
	    extreme = &args[i];
    }

    /* Replace args[0] with extreme, and pop other arguments. */
    data_dup(&d, extreme);
    data_discard(&args[0]);
    args[0] = d;
    pop(num_args - 1);
}

void op_max(void)
{
    find_extreme(1);
}

void op_min(void)
{
    find_extreme(-1);
}

void op_abs(void)
{
    Data *args;

    if (!func_init_1(&args, INTEGER))
	return;

    if (args[0].u.val < 0)
	args[0].u.val = -args[0].u.val;
}

void op_get_dbref(void)
{
    Data *args;
    long dbref;

    if (!func_init_1(&args, SYMBOL))
	return;

    if (!lookup_retrieve_name(args[0].u.symbol, &dbref)) {
	cthrow(namenf_id, "Cannot find object %I.", args[0].u.symbol);
	return;
    }

    pop(1);
    push_dbref(dbref);
}

void op_ticks_left(void)
{
  if (!func_init_0())
    return;

  push_int(cur_frame->ticks);
}