/* 
   CircleMUD Server-Side Macro/Alias Timer support v.1.0
   by Keats (plamzi) @ bedlam.gotdns.com:9000
   bedlam@eyecandid.com
*/




/* ADD to structs.h */

struct txt_q {
	struct txt_block *head;
	struct txt_block *tail;
	
	/* begin addition */
		struct txt_q *next;
		int delay;
	/* end addition */
};

struct descriptor_data {
...
	/* begin addition */
		struct txt_q *timer_input;
	/* end addition */
}




/* ADD to comm.c */

	if (!(pulse % PULSE_VIOLENCE)) perform_violence();
...

	/* begin addition */
		if (!(pulse % PASSES_PER_SEC)) process_timer_queues();
	/* end addition */



/* ADD to handler.c */

void extract_char(struct char_data * ch)
{
...
		/* begin addition */
		// flush any delay timer queues when char is extracted
		if (ch->desc && ch->desc->timer_input) {
			struct txt_q *q, *temp;
			while (ch->desc->timer_input) {
				q = ch->desc->timer_input;
				REMOVE_FROM_LIST(q, ch->desc->timer_input, next);
				free(q);
			}
		}
		/* end addition */

	if (ch->desc) {
		STATE(ch->desc) = CON_MENU;
		SEND_TO_Q(MENU, ch->desc);
	}
} /* end of extract_char */


/* ADD to interpreter.c */

	/* begin addition */
	#define Q_ADD	  0
	#define Q_RESUME   1
	ACMD(do_wait);
	/* end addition */

...
	/* begin addition */
  { "wait"     , POS_DEAD,    do_wait      , 0, 0, 0, 0},
	/* end addition */
...

/* push our temp_queue on to the _front_ of the input queue */
void perform_complex_alias(struct descriptor_data *d, struct txt_q *input_q, char *orig, struct alias *a)
{
...
/* comment out the block at the end of func */
	
	/* push our temp_queue on to the _front_ of the input queue */
	
 /*
 if (input_q->head == NULL)
 *input_q = temp_queue;
 else {
 temp_queue.tail->next = input_q->head;
 input_q->head = temp_queue.head;
 }
 */
	/* begin addition */
	run_timer_queue(d, &d->input, &temp_queue, Q_ADD);
	/* end addition */
}

/* Bedlam server-side timers for aliases (can be used for server-side triggers as well) */

/* called once per sec from comm.c - currently processes timers even on linkless chars but you can change that behavior */
void process_timer_queues() {
	struct descriptor_data *d;
	for (d = descriptor_list; d; d = d->next) {
		if (d->connected != CON_PLAYING || !d->character || IS_NPC(d->character) || !d->timer_input) continue;
		process_timer_queue(d);
	}
}

void process_timer_queue(struct descriptor_data *d) {
	struct txt_q *q, *next;
	int count;
	//this odd loop allows for queues to be removed during iteration
	for (q = d->timer_input; q; q = next) {
		if (!q) continue;
		next = q->next;
		q->delay--;
		if (q->delay == 0) run_timer_queue(d, &d->input, q, Q_RESUME);
	}
}

void run_timer_queue(struct descriptor_data *d, struct txt_q *input_q, struct txt_q *q, int mode) {
	/* mode 0 is Q_ADD (called by perform_complex_alias), 1 is Q_RESUME (called by process_timer_queue)*/
	struct txt_block *this, *next;
	struct txt_q temp_queue;
	struct txt_q *temp;
	int dummy;
	
	temp_queue.head = temp_queue.tail = NULL;
	
	//if (mode)
	//log("DEBUG: entering run timer queue in mode: RESUME");
	//else
	//log("DEBUG: entering run timer queue in mode: ADD");
	
	//log("DEBUG: queue has timer");
	
	for (this = next = q->head;this;this = next) {
		
		if (!next) continue;
		next = this->next;
		
		get_from_q(q, arg, &dummy);
		half_chop(arg, buf, buf2);
		
		if (str_cmp(buf, "wait")) {
			//log("DEBUG: Non-wait cmd written to queue");
			write_to_q(arg, &temp_queue, 1);
			
			if (!next) {
				if (mode == Q_RESUME) {
					REMOVE_FROM_LIST(q, d->timer_input, next);
					free(q);
					//log("DEBUG: remove from timer queues (last cmd reached)");
				}
				add_to_q(temp_queue, d);
				return;
			} else 
				continue;
		}
		
		if (!*buf2) {
			//no wait number, ignore
			//log("DEBUG: no wait seconds");
			continue;
		}
		
		if (!is_number(buf2)) {
			//wait arg is not number, ignore
			//log("DEBUG: wait argument is not a number");
			continue;			
		}
		
		int delay = atoi(buf2);
		if (delay > 0 && delay < 3601) {
			
			//log("DEBUG: valid delay timer detected");
			
			if (!next) {
				if (mode == Q_RESUME) {
					REMOVE_FROM_LIST(q, d->timer_input, next);
					free(q);
					//log("DEBUG: remove from timer queues (last cmd is wait)");
				}
			}
			else {
				//more commands
				q->delay = delay;
				if (mode == Q_ADD) {
					
					//log("DEBUG: adding new timer queue");
					
					if (!d->timer_input) {
						//log("DEBUG: first timer queue");
						CREATE(d->timer_input, struct txt_q, 1);
						d->timer_input->delay = q->delay; 
						d->timer_input->head = q->head;
						d->timer_input->tail = q->tail;
						d->timer_input->next = NULL;
					}
					else {
						//log("DEBUG: append timer queue");
						CREATE(temp, struct txt_q, 1);
						temp->delay = q->delay; 
						temp->head = q->head;
						temp->tail = q->tail;
						temp->next = d->timer_input;
						d->timer_input = temp;
					}
					
				} 
				//else log("DEBUG: reset timer for a delay queue");
			} //end command loop
			
			add_to_q(temp_queue, d);
			
			//print_q(&d->input);
			//print_q(&d->delayed_input);
			
			return;
			
		} // timer out of range error
		
	} // end queue loop
	
}

void add_to_q (struct txt_q temp_queue, struct descriptor_data *d) {
	
	if (temp_queue.head) {
		//log("DEBUG: adding pre-timer commands to input queue");
		if ((&d->input)->head == NULL)
			*(&d->input) = temp_queue;
		else {
			temp_queue.tail->next = (&d->input)->head;
			(&d->input)->head = temp_queue.head;
		}
	}
	else {
		// the dummy command 'wait' is set up to print nothing back
		// we 'need' it for macros that begin with 'wait #' 
		// just so we won't have to modify the way comm.c handles input
		// this is a quick workaround - there's probably a better way
		sprintf(arg, "wait");
		write_to_q(arg, &d->input, 1);	
	}	
	
}

ACMD(do_wait) { return; }

/* debug helper func - print queue */
void print_q (struct txt_q *q) {
	//log("DEBUG: print queue");
	struct txt_block *this;
	int qnum = 1;
	for (this = q->head; this; this = this->next) {
		sprintf(buf, "(Q) %d. %s", qnum, this->text);
		log(buf);
		qnum++;
	}
}