/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 2007  Umberto Salsi
 *
 *  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; version 2 dated June, 1991.
 *
 *  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., 675 Mass Ave., Cambridge, MA 02139, USA.
 */

#include <string.h>
#include "../util/memory.h"
#include "../util/prng.h"
#include "dis_if.h"
#include "init.h"
#include "list.h"
#include "pm.h"
#include "patchlevel.h"

#define prompt_IMPORT
#include "prompt.h"

/** Clear current msg after ... (s). */
#define EXPIRE_PERIOD 7.0

/** DIS status feedback period (s) */
#define DIS_FEEDBACK_PERIOD 25.0

/** if no msgs to show, show an hint after ... (s). */
#define HINT_PERIOD  30.0

static list_Type *hints;

#define BUF_SIZE 100

typedef struct prompt_Line {
	struct prompt_Line *next;

	_BOOL have_msg;
	double expire;
	char buf[BUF_SIZE];

	double last_msg_shown_at;
} prompt_Line;

static prompt_Line *free_list = NULL;


void prompt_free(viewer *u)
{
	prompt_Line *pd;

	if( u->prompt == NULL )
		return;
	
	pd = u->prompt;

	pd->next = free_list;
	free_list = pd;
	u->prompt = NULL;
}


static void prompt_cleanup()
{
	prompt_Line *pd;

	while( free_list != NULL ){
		pd = free_list;
		free_list = pd->next;
		memory_dispose(pd);
	}
	
	memory_dispose(hints);
	hints = NULL;
}


static int isSpace(int c)
{
	return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}

/**
 * Reads list of hints messages from the objects/hints.txt file.
 * Empty lines an lines starting with '#' are ignored.
 */
static void prompt_read_hints()
{
	char line[999];
	int s_len;
	char *s;
	if( hints != NULL )
		return;
	memory_registerCleanup(prompt_cleanup);
	FILE *f = init_fopen("hints.txt", "r");
	hints = list_new();
	while( fgets(line, sizeof(line), f) != NULL ){
		s = line;
		// Skip leading spaces:
		while( isSpace(*s) )
			s++;
		// Ignore empty and comment line:
		if( *s == '#' || *s == '\0' )
			continue;
		// Remove trailing spaces:
		s_len = strlen(s);
		while(s_len > 0 && isSpace(s[s_len-1]))
			s_len--;
		s[s_len] = '\0';
		// Ok, add line:
		list_add_elem(hints, s);
	}
	fclose(f);
}


void prompt_viewer_print(viewer *u, char *s)
{
	prompt_Line *pd;

	if( u->prompt == NULL ){
		if( free_list == NULL ){
			pd = memory_allocate( sizeof(prompt_Line), NULL );
			memory_registerCleanup(prompt_cleanup);
		} else {
			pd = free_list;
			free_list = pd->next;
		}
		pd->next = NULL;
		pd->have_msg = FALSE;
		pd->expire = 0.0;
		pd->buf[0] = '\0';
		pd->last_msg_shown_at = -HINT_PERIOD; /* next msg shown immediately */
		u->prompt = pd;
	} else {
		pd = u->prompt;
	}

	pd->expire = curTime + EXPIRE_PERIOD;
	memory_strcpy(pd->buf, sizeof(pd->buf), s);
	pd->have_msg = TRUE;
}


void prompt_craft_print(craft *c, char *s)
{
	viewer *v;

	v = vl_head;

	while( v != NULL ){
		if( v->c == c )
			prompt_viewer_print(v, s);
		v = v->next;
	}
}


void prompt_broadcast_print(char *s)
{
	viewer *v;

	v = vl_head;

	while( v != NULL ){
		prompt_viewer_print(v, s);
		v = v->next;
	}
}


static void draw_string(Viewport *v, prompt_Line *pd)
{
	double fh, fw, w, k;
	int width, height, l, x, y;

	width = RectWidth(v->rect);
	height = RectHeight(v->rect);
	fh = ceil(height / 30.0);
	fw = VFontWidthPixels(v, (int) (fh+0.5));
	l = strlen(pd->buf);
	k = l * fw / (0.95*width);
	if( k >= 1.0 ){
		fh = floor(fh/k);
		fw = VFontWidthPixels(v, (int) (fh+0.5));
	}
	w = fw * l;

	Alib_setClipRect(v->w, &v->rect);
	x = v->rect.a.x + width/2 - w/2.0;
	y = v->rect.b.y - fh;
	VDrawStrokeString(v, x + 1, y, pd->buf, l, fh, blackColor);
	VDrawStrokeString(v, x, y, pd->buf, l, fh, whiteColor);
}


void prompt_draw(viewer *u)
{
	prompt_Line *pd;
	int n, i;

	if( u->prompt == NULL ){
		prompt_viewer_print(u, "**** Welcome to ACM-"
			patchlevel_REVISION_STRING " ****");
		return;
	}
	
	pd = u->prompt;

	/*
	 * Avoid to distract the maneuvering pilot with silly messages:
	 */
	if( fabs(u->c->G.x) + fabs(u->c->G.y) + fabs(u->c->G.z + 1) > 0.2 )
		pd->last_msg_shown_at = curTime + HINT_PERIOD;


	if( pd->have_msg ){
		/*
		 * Draw current msg:
		 */
		if( curTime > pd->expire ){
			pd->have_msg = FALSE;
			pd->last_msg_shown_at = curTime;
			return;
		}

		draw_string(u->v, pd);
	
	} else if( dis_if_readyToReceive()
	&& pd->last_msg_shown_at + DIS_FEEDBACK_PERIOD < curTime ) {
		/*
		 * Show DIS protocol health state:
		 */
		char s[100];
		snprintf(s, sizeof(s), "DIS: %d remote entities, %.1f incoming packets/s",
			dis_if_getNumberOfRemoteEntities(),
			dis_if_getProcessedPacketsPerSecond());
		prompt_viewer_print(u, s);
	
	} else if( pd->last_msg_shown_at + HINT_PERIOD < curTime ){
		/*
		 * Chose another hint:
		 */
		if( hints == NULL )
			prompt_read_hints();
		
		n = hints->n;
		if( n > 0 ){
			i = prng_getIntInRange(0, n - 1);
			prompt_viewer_print(u, hints->arr[i]);
		}
	}
}

/* End of the prompt module. */
