/* Header stuff later... 


working comment area:

The hard part here is going to be conditional operations, trapping events
that the scripts might care about. - perhaps we could have a cron_proc
attached to mobs (rooms/items?) to figure those out...

*/

#include <ctype.h>


#include <string.h>

#include "cron.h"
#include "structs.h" /* need this? */
#include "interp.h"
#include "utils.h"
#include "comm.h"
#include "error.h"
#include "proto.h"

/* global variables */
extern char log_buf[];


/* local variables */
extern struct cronbase cronbase;

/* extern procedures */
extern struct char_data *get_char_by_id(int id);

/* local procedures */
void exec_cron(struct cron *c);
void check_wait(struct cron *c);
void execute_cron(struct cron *c);
struct cron_var *get_var(struct cron *c,int type,char *name);
char *expand_arg(struct cron *c,char *arg);
struct cron_quantum *scan_for_match(struct cron_quantum *q, int match);
void push_stack(struct cron *c,struct stack_unit *frame);
struct stack_unit *pop_stack(struct cron *c);


/* The processing routines */
void scheduler()
{
    static int normal_timer=0,slow_timer=0;
    struct cron *c;

    for(c=cronbase.fast_crons.Head;c->Succ;c=c->Succ)
	exec_cron(c);

    if(++normal_timer==CRON_MEDIUM_TICKS) {
	normal_timer=0;
	for(c=cronbase.medium_crons.Head;c->Succ;c=c->Succ)
	    exec_cron(c);
    }

    if(++slow_timer==CRON_SLOW_TICKS) {
	slow_timer=0;
	for(c=cronbase.slow_crons.Head;c->Succ;c=c->Succ)
	    exec_cron(c);
    }
}

void exec_cron(struct cron *c)
{
    switch(c->mode) {
	case CRON_WAIT:
	    check_wait(c);
	    break;
	case CRON_GO:
	    execute_cron(c);
	    break;
	case CRON_WACKY:
	    /* Don't do anything at all */
	    break;
	default:
	    log("Bad cron type in exec_cron_list!");
	    c->mode=CRON_WACKY;
	    break;
    }
}

void check_wait(struct cron *c)
{

}

/* This is the real meat of the whole thing */
void execute_cron(struct cron *c)
{
    struct cron_quantum *q,*next;
    struct cron_var *var;
    struct char_data *ch;
    struct stack_unit *frame;

    q=c->executing;
    next=q->next;
/*
sprintf(log_buf,"EXECUTING %d: %s",q->cmd,q->arg);
log(log_buf);
*/
    switch(q->cmd) {
	case QC_NOP:
	    break; /* What do you expect? */
	case QC_LOG:
	    log(expand_arg(c,q->arg));
	    break;
	case QC_VAR:
	    break;
	case QC_ACTOR_COMMAND:
	    var=get_var(c,VAR_ACTOR, q->name);
	    if(var) {
		ch=get_char_from_id(var->value);
		if(ch) {
		    command_interpreter(ch,
			  expand_arg(c,q->arg));
		} else {
		    /* Check condition of lost actor */
		    log("Lost actor");
		}
	    } else
		log("cron: No actor found");
	    break;
	case QC_SET_DOOR:
	    break;
	case QC_LOAD:
	    if(q->data==LOAD_MOB) {
	    } else if(q->data==LOAD_OBJ) {
	    }
	    break;
	case QC_ECHO:
	    if(q->data==ECHO_ROOM) {
		send_to_room(q->arg,
		      real_room(eval_exp(c,q->exp1)));
	    } else if(q->data==ECHO_ZONE) {
/*				send_to_zone(q->arg,
		      real_zone(eval_exp(c,q->exp1)));
*/			}
	    break;
	case QC_MESSAGE:
	    break;
	case QC_SPEED:
	    /* Remove from the current list */
	    /* and place in the proper list */
	    unqueue_cron(c);
	    enqueue_cron(c,q->data);
	    break;
	case QC_RESTART:
	    /* This one will be hard to define...
	    do we dump uninit'd vars or all vars?
	    We DEFINITELY dump the stack...*/
	    next = c->script->first;
	    break;
	case QC_WAIT:
	    /* First to check to see if the wait is not */
	    /* already satisfied, then stick in the wait list */
	    break;
	case QC_IF:
	    if(eval_bool(c,q)) {
		CREATE(frame,struct stack_unit,1);
		frame->type = STACK_IF_LOOP;
		frame->ref = q;
		push_stack(c,frame);
	    } else {
		next = scan_for_match(q,QC_ENDIF);
		if(!next) {
		    c->mode = CRON_WACKY;
		    sprintf(log_buf,"CRON: no match for IF in '%s'", c->script->name);
		    log(log_buf);
		} else
		    next = next->next;
	    }
	    break;
	case QC_ENDIF:
	    frame = pop_stack(c);
	    if(frame->type != STACK_IF_LOOP) {
		c->mode = CRON_WACKY;
		log("CRON: mismatched if/endif");
		return;
	    }
	    free(frame);
	    break;
	case QC_WHILE:
	    if(eval_bool(c,q)) {
		CREATE(frame,struct stack_unit,1);
		frame->type = STACK_WHILE_LOOP;
		frame->ref = q;
		push_stack(c,frame);
	    } else {
		next = scan_for_match(q,QC_ENDWHILE);
		if(!next) {
		    c->mode = CRON_WACKY;
		    sprintf(log_buf,"CRON: no match for WHILE in '%s'", c->script->name);
		    log(log_buf);
		} else
		    next = next->next;
	    }
	    break;
	case QC_ENDWHILE:
	    frame = pop_stack(c);
	    if(frame->type != STACK_WHILE_LOOP) {
		c->mode = CRON_WACKY;
		log("CRON: mismatched while/endwhile");
		return;
	    }
	    free(frame);
	    break;
	default:
	    sprintf(log_buf,"Unknown command in script %s",c->script->name);
	    log(log_buf);
	    break;
    }
    if(!next)
	free_cron(c);
    else
	c->executing = next;
}

struct cron_quantum *scan_for_match(struct cron_quantum *q, int match)
{
    while(q) {
	if(q->cmd==QC_IF)
	    q = scan_for_match(q,QC_ENDIF);
	else if(q->cmd==QC_WHILE)
	    q = scan_for_match(q,QC_ENDWHILE);
	else if(q->cmd==match)
	    return(q);
	if(q)
	    q = q->next;
    }
    return(NULL);
}

void push_stack(struct cron *c,struct stack_unit *frame)
{
    frame->next = c->stack;
    c->stack = frame;
}

struct stack_unit *pop_stack(struct cron *c)
{
    struct stack_unit *frame;

    frame = c->stack;
    if(!frame) {
	c->mode = CRON_WACKY;
	sprintf(log_buf,"CRON: popping an empty stack in '%s'",
	    c->script->name);
	log(log_buf);
	return(NULL);
    }
    c->stack = frame->next;
    return(frame);
}

struct cron_var *get_var(struct cron *c,int type,char *name)
{
    struct cron_var *var;

    for(var=c->vars;var;var=var->next) {
fprintf(stderr,"Args: %d %s  Var: %d %s\n",type,name,var->type,var->name);
	if(var->type==type)
            if((var->name && name && !strcmp(var->name,name)))
	        return(var);
    }
    sprintf(log_buf,"cron_var %s not found!",name);
    log(log_buf);
    return(NULL);
}

char *expand_arg(struct cron *c,char *arg)
{
    static char buf[MAX_INPUT_LENGTH];
    int marker=0,num,sub_type;
    struct cron_var *var;

    while(*arg) {
	if(*arg == '@') {
	    sub_type = 0 /*<some default type>*/;

	/*	if(arg[1]=='...') {
	    }

	    if(isdigit(arg[1])) {
		arg++;
		num = atoi(arg);
		var = get_var(c,VAR_ACTOR,num);
		switch(sub_type) {

		}
		marker++;
		while(isdigit(arg[1]))
		    arg++;
	    } */
	} else
	    buf[marker++] = *arg;
	arg++;
    }

    /* Take care of that terminating null */
    buf[marker]='\0'; /* I hope that's right */

    return(buf);
}