/* This file needs some header text, doesn't it? */
#include <strings.h>
#include <stdio.h>
#include <ctype.h>

#include "utils.h"
#include "structs.h"
#include "comm.h"
#include "db.h"


#define MAX_MSGS 50	               /* Max number of messages.          */

/* So we don't have all these messy ifs and duplicate functions */
struct board {
	char *filename;
	char *msgs[MAX_MSGS];
	char *head[MAX_MSGS];
	int msg_num;
	int has_loaded;
	int message_written;
	int remove_level;
	int view_level;
	bool trusted_only;
};

#define MORTAL_SAVE_FILE "board.messages" /* Name of file for saving messages */
#define GOD_SAVE_FILE    "god.messages"      /* File for god's board */
#define PLAN_SAVE_FILE   "planning.messages" /* Board in Planning Office */
#define REIMB_SAVE_FILE  "reimb.messages"    /* Board for reimbursement */
#define WAR_SAVE_FILE    "war.messages"      /* Board in the War Room */
#define FIVE_SAVE_FILE   "five.messages"     /* Board in the Room of Five*/
#define SEVEN_SAVE_FILE  "seven.messages"    /* Board in the Room of Seven*/
#define WILD_SAVE_FILE   "wild.messages"     /* Board in the wilderness */
#define MAX_MESSAGE_LENGTH 2048     /* that should be enough            */

extern struct time_info_data time_info;

char *MORTmsgs[MAX_MSGS];
char *MORThead[MAX_MSGS];
struct board MORTboard;

char *GODmsgs[MAX_MSGS];
char *GODhead[MAX_MSGS];
struct board GODboard;

char *PLANmsgs[MAX_MSGS];
char *PLANhead[MAX_MSGS];
struct board PLANboard;

char *REIMBmsgs[MAX_MSGS];
char *REIMBhead[MAX_MSGS];
struct board REIMBboard;

char *WARmsgs[MAX_MSGS];
char *WARhead[MAX_MSGS];
struct board WARboard;

char *FIVEmsgs[MAX_MSGS];
char *FIVEhead[MAX_MSGS];
struct board FIVEboard;

char *SEVENmsgs[MAX_MSGS];
char *SEVENhead[MAX_MSGS];
struct board SEVENboard;

char *WILDmsgs[MAX_MSGS];
char *WILDhead[MAX_MSGS];
struct board WILDboard;

void board_write_msg(struct char_data *ch, char *arg, struct board *board);
int board_display_msg(struct char_data *ch, char *arg, struct board *board);
int board_remove_msg(struct char_data *ch, char *arg, struct board *board);
void board_save_board(char *file, char **head, char **msgs, int *msg_num);
void board_load_board(char *file, char **head, char **msgs, int *msg_num);
void board_reset_board(char **head, char **msgs, int *msg_num);
void error_log();
void board_fix_long_desc(int num, char *headers[MAX_MSGS]);
int board_show_board(struct char_data *ch, char *arg, struct board *board);


/* This function initializes everything necessary to use the board */
void initialize_boards()
{

	PLANboard.filename=PLAN_SAVE_FILE;
	PLANboard.has_loaded=0;
	PLANboard.message_written=0;
	PLANboard.remove_level=23;
	PLANboard.view_level=0;
	PLANboard.trusted_only=TRUE;

	GODboard.filename=GOD_SAVE_FILE;
	GODboard.has_loaded=0;
	GODboard.message_written=0;
	GODboard.remove_level=23;
	GODboard.view_level=21;
	GODboard.trusted_only=TRUE;

	MORTboard.filename=MORTAL_SAVE_FILE;
	MORTboard.has_loaded=0;
	MORTboard.message_written=0;
	MORTboard.remove_level=22;
	MORTboard.view_level=0;
	MORTboard.trusted_only=FALSE;

	REIMBboard.filename=REIMB_SAVE_FILE;
	REIMBboard.has_loaded=0;
	REIMBboard.message_written=0;
	REIMBboard.remove_level=23;
	REIMBboard.view_level=1;
	REIMBboard.trusted_only=TRUE;

	WARboard.filename=WAR_SAVE_FILE;
	WARboard.has_loaded=0;
	WARboard.message_written=0;
	WARboard.remove_level=26;
	WARboard.view_level=25;
	WARboard.trusted_only=TRUE;

	FIVEboard.filename=FIVE_SAVE_FILE;
	FIVEboard.has_loaded=0;
	FIVEboard.message_written=0;
	FIVEboard.remove_level=25;
	FIVEboard.view_level=25;
	FIVEboard.trusted_only=TRUE;

	SEVENboard.filename=SEVEN_SAVE_FILE;
	SEVENboard.has_loaded=0;
	SEVENboard.message_written=0;
	SEVENboard.remove_level=25;
	SEVENboard.view_level=25;
	SEVENboard.trusted_only=TRUE;

	WILDboard.filename=WILD_SAVE_FILE;
	WILDboard.has_loaded=0;
	WILDboard.message_written=0;
	WILDboard.remove_level=21;
	WILDboard.view_level=0;
	WILDboard.trusted_only=FALSE;
}

/* I have used cmd number 180-182 as the cmd numbers here. */
/* The commands would be, in order : NOTE <header>         */
/* READ <message number>, REMOVE <message number>          */
/* LOOK AT BOARD should give the long desc of the board    */
/* and that should equal a list of message numbers and     */
/* headers. This is done by calling a function that sets   */
/* the long desc in the board object. This function is     */
/* called when someone does a REMOVE or NOTE command.      */
/* I have named the function board_fix_long_desc(). In the */
/* board_write_msg() function there is a part that should  */
/* be replaced with a call to some dreadful routine used   */
/* by the STRING command to receive player input. It is    */
/* reputed to lurk somewhere within the limits of an evil  */
/* file named act.comm.c.....*/

/* saving the board after the addition of a new messg      */
/* poses a slight problem, since the text isn't actually   */
/* entered in board_write. What I'll do is to let board()  */
/* save the first time a 'look' is issued in the room..    */
/* ugh! that's ugly - gotta think of something better.     */
/* -quinn                                                  */

/* And here is the board...correct me if I'm wrong. */

extern struct index_data *obj_index;

int board(struct obj_data *obj,struct char_data *ch, int cmd, char *arg)
{
 	int irc;

	struct board *this_board;

	int message_num;

	switch(obj_index[obj->item_number].virtual) {
		case 9: /* Planning board */
			this_board=&PLANboard;
			break;
		case 55: /* Five board */
			this_board=&FIVEboard;
			break;
		case 59: /* War board */
			this_board=&WARboard;
			break;
		case 66: /* Reimb board */
			this_board=&REIMBboard;
			break;
		case 77: /* Seven board */
			this_board=&SEVENboard;
			break;
		case 3098: /* God/wiz/immortal board */
			this_board=&GODboard;
			break;
		case 3099: /* Mortal board */
			this_board=&MORTboard;
			break;
		case 12005: /* Wilderness board */
			this_board=&WILDboard;
			break;
		default: 
			log("Bad object number in bulletin board handler!");
			return(FALSE);
			break;
	}

	if (!ch->desc)
		return(FALSE);
/* By MS or all NPC's will be trapped at the board */
/* note: I'll let display and remove return 0 if the arg was non-board- */
/* related. Thus, it'll be possible to read other things than the board */
/* while you're in the room. Conceiveably, you could do this for write, */
/* too, but I'm not in the mood for such hacking.                       */

	if (!this_board->has_loaded)
	{
		board_load_board(this_board->filename, this_board->head,
				 this_board->msgs, &this_board->msg_num);
		this_board->has_loaded = 1;
	}

	switch (cmd) {
		case 15:  /* look */
			irc = board_show_board(ch, arg, this_board);
			break;
		case 149: /* write */
			board_write_msg(ch, arg, this_board);
			this_board->message_written = TRUE;
			return (1);    /* Dont save in this case */
			break;
		case 63: /* read */
			irc = board_display_msg(ch, arg, this_board);
			break;
		case 66: /* remove */
			irc = board_remove_msg(ch, arg, this_board);
			board_save_board(this_board->filename, this_board->head,
					this_board->msgs, &this_board->msg_num);
			break;
		default:
			irc = 0;
			break;
	} /* switch */

 	if (this_board->message_written) {
		this_board->message_written = FALSE;
		board_save_board(this_board->filename, this_board->head,
				 this_board->msgs, &this_board->msg_num);
	} /* if */

   	return irc;
}



void board_write_msg(struct char_data *ch, char *arg, struct board *board)
{
	char buf[MAX_STRING_LENGTH];

	if(board->trusted_only && !IS_TRUSTED(ch)) {
		send_to_char("You are not holy enough.\n\r", ch);
		return;
	}

	if(GET_LEVEL(ch) < board->view_level) {
		send_to_char("Nobody left any space for you!\n\r",ch);
		return;
	}

	if (board->msg_num > MAX_MSGS - 1) {
		send_to_char("The board is full already.\n\r", ch);
		return;
	}
	/* skip blanks */
	for(; isspace(*arg); arg++);
	if (!*arg) {
		send_to_char("We must have a headline!\n\r", ch);
		return;
	}
	if(strlen(arg)>50) { /* Rather arbitrary, to leave room for name,date */
		send_to_char("Your headline is too long!\n\r",ch);
		return;
	}
	sprintf(buf,"[%d/%d/%d]",time_info.day+1,time_info.month+1,time_info.year);
	board->head[board->msg_num] = (char *)malloc(strlen(arg) + strlen(GET_NAME(ch)) + 5 + strlen(buf));
	/* +5 is for a space, a null, and '()' around the character name. */
	if (!board->head[board->msg_num]) {
		error_log("Malloc for board header failed.\n\r");
		send_to_char("The board is malfunctioning - sorry.\n\r", ch);
		return;
	}
        sprintf(board->head[board->msg_num],"%s %s (%s)",buf,arg,GET_NAME(ch));
	board->msgs[board->msg_num] = NULL;

	send_to_char("Write your message. Terminate with a @.\n\r\n\r", ch);
	act("$n starts to write a message.", TRUE, ch, 0, 0, TO_ROOM);

	ch->desc->str = &board->msgs[board->msg_num];
	ch->desc->max_str = MAX_MESSAGE_LENGTH;

	board->msg_num++;
}


int board_remove_msg(struct char_data *ch, char *arg, struct board *board)
{
	int ind, msg, pos, length;
	char buf[256], number[MAX_INPUT_LENGTH];

	one_argument(arg, number);

	if (!*number || !isdigit(*number))
		return(0);

	if (!(msg = atoi(number))) return(0);

	if (GET_LEVEL(ch) < board->view_level) {
		send_to_char("What messages?\n\r",ch);
		return(1);
	}

	if (!board->msg_num) {
		send_to_char("The board is empty!\n\r", ch);
		return(1);
	}
	if (msg < 1 || msg > board->msg_num) {
		send_to_char("That message exists only in your imagination..\n\r", ch);
		return(1);
	}       

	if (!IS_TRUSTED(ch) || GET_LEVEL(ch) < board->remove_level) {
		pos=length=strlen(board->head[msg-1]);
		while(board->head[msg-1][pos]!='(' && pos>=0)
			pos--;
		strncpy(buf,&board->head[msg-1][pos+1],length-pos-2);
		buf[length-pos-2]='\0';
		if(strcmp(GET_NAME(ch),buf)) {
			send_to_char("That message seems pretty solidly tacked into the board.\n\r", ch);
			return(1);
		}
	}

	ind = msg;
	free(board->head[--ind]);
	if (board->msgs[ind])
		free(board->msgs[ind]);
	for (; ind < (board->msg_num-1); ind++) {
		board->head[ind] = board->head[ind + 1];
		board->msgs[ind] = board->msgs[ind + 1];
	}
	board->msg_num--;
	send_to_char("Message removed.\n\r", ch);  /* Anybody have any idea*/
	sprintf(buf, "$n just removed message %d.", msg/* + 1*/); /* <--- */
	act(buf, FALSE, ch, 0, 0, TO_ROOM);        /* was there? --Sman */

	return(1);
}

void board_save_board(char *file, char **head, char **msgs, int *msg_num) {
	FILE *the_file;		
	int ind, len;


	if (!*msg_num) {
		error_log("No messages to save.\n\r");
		return;
	}
	the_file = fopen(file, "wb");
	if (!the_file) {
		error_log("Unable to open/create savefile..\n\r");
		return;
	}
	fwrite(msg_num, sizeof(int), 1, the_file);
	for (ind = 0; ind < *msg_num; ind++) {
		len = strlen(head[ind]) + 1;
		fwrite(&len, sizeof(int), 1, the_file);
		fwrite(head[ind], sizeof(char), len, the_file);
		len = msgs[ind] ? strlen(msgs[ind]) + 1 : 0;
		fwrite(&len, sizeof(int), 1, the_file);
		if(msgs[ind])
			fwrite(msgs[ind], sizeof(char), len, the_file);
	}
	fclose(the_file);
/*	board_fix_long_desc(msg_num, head);   */
	return;
}

void board_load_board(char *file, char **head, char **msgs, int *msg_num) {
	FILE *the_file;
	int ind, len = 0;

	board_reset_board(head, msgs, msg_num);
	the_file = fopen(file, "rb");
	if (!the_file) {
		error_log("Can't open message file. Board will be empty.\n\r",0);
		return;
	}
	fread(msg_num, sizeof(int), 1, the_file);
	/*changed 1 to a 0 below this line---randall*/
	if (*msg_num < 0 || *msg_num > MAX_MSGS || feof(the_file)) {
		error_log("Board-message file corrupt or nonexistent.\n\r");
                *msg_num = 0;
		fclose(the_file);
		return;
	}
	for (ind = 0; ind < *msg_num; ind++) {
		fread(&len, sizeof(int), 1, the_file);
		head[ind] = (char *)malloc(len + 1);
		if (!head[ind]) {
			error_log("Malloc for board header failed.\n\r");
			board_reset_board(head, msgs, msg_num);
			fclose(the_file);
			return;
		}
		fread(head[ind], sizeof(char), len, the_file);
		fread(&len, sizeof(int), 1, the_file);
		msgs[ind] = (char *)malloc(len + 1);
		if (!msgs[ind]) {
			error_log("Malloc for board msg failed..\n\r");
			board_reset_board(head, msgs, msg_num);
			fclose(the_file);
			return;
		}
		fread(msgs[ind], sizeof(char), len, the_file);
	}
	fclose(the_file);
/*	board_fix_long_desc(msg_num, head);  */
	return;
}

void board_reset_board(char **head, char **msgs, int *msg_num) {
	int ind;
	for (ind = 0; ind < MAX_MSGS; ind++) {
		free(head[ind]);
		free(msgs[ind]);
	}
	*msg_num = 0;
/* 	board_fix_long_desc(0, head);   */
	return;
}

void error_log(char *str) {	/* The original error-handling was MUCH */
	fputs("Board : ", stderr);	/* more competent than the current but  */
	fputs(str, stderr);	/* I got the advice to cut it out..;)   */
	return;
}

int board_display_msg(struct char_data *ch, char *arg, struct board *board)
{
	char buf[512], number[MAX_INPUT_LENGTH], buffer[MAX_STRING_LENGTH];
	int msg;


	one_argument(arg, number);
	if (!*number || !isdigit(*number))
		return(0);
	if (!(msg = atoi(number))) return(0);

	if(board->trusted_only && !IS_TRUSTED(ch)) {
		send_to_char("Your eyes are not holy enough.\n\r", ch);
		return(1);
	} /* if */		

	if (!board->msg_num || GET_LEVEL(ch) < board->view_level) {
		send_to_char("The board is empty!\n\r", ch);
		return(1);
	}
	if (msg < 1 || msg > board->msg_num) {
		send_to_char("That message exists only in your imagination..\n\r",
			ch);
		return(1);
	}

	/* Can PERFORM() handle this...?  no. Sorry*/
	/* sprintf(ch, "Message %d	: %s\n\r%s", msg, head[msg - 1], msgs[msg - 1]); */
	/* Bad news */

	sprintf(buffer, "Message %d : %s\n\r\n\r%s", msg, board->head[msg - 1],
		board->msgs[msg - 1]);
	page_string(ch->desc, buffer, 1);
	return(1);
}


#if defined XYZZY
/* Disabled */
		
void board_fix_long_desc(int num, char *headers[MAX_MSGS]) {

	struct obj_data *ob;




	/**** Assign the right value to this pointer..how? ****/
	/**** It should point to the bulletin board object ****/
	/**** Then make ob.description point to a malloced ****/
	/**** space containing itoa(msg_num) and all the   ****/
	/**** headers. In the format :
	This is a bulletin board. Usage : READ/REMOVE <message #>, NOTE <header>
	There are 12 messages on the board.
	1	: Re : Whatever and something else too.
	2	: I don't agree with Rainbird.
	3	: Me neither.
	4 	: Groo got hungry again - bug or sabotage?

	Well...something like that..;) 			   ****/
	
	/**** It is always to contain the first line and   ****/
	/**** the second line will vary in how many notes  ****/
	/**** the board has. Then the headers and message  ****/
	/**** numbers will be listed. 			   ****/
	return;
}

#endif


int board_show_board(struct char_data *ch, char *arg, struct board *board)
{
	int i;
	char buf[MAX_STRING_LENGTH], tmp[MAX_INPUT_LENGTH];

	one_argument(arg, tmp);

	if (!*tmp || !isname(tmp, "board bulletin"))
		return(0);

	if(board->trusted_only && !IS_TRUSTED(ch)){
		send_to_char("Your eyes are not holy enough.\n\r", ch);
		return(1);
	} /* if */

	if(board->view_level > GET_LEVEL(ch)) {
		send_to_char("Those messages must be written invisible ink!\n\r",ch);
		return(0);
	}

	act("$n studies the board.", TRUE, ch, 0, 0, TO_ROOM);

	strcpy(buf,
"This is a bulletin board. Usage: READ/REMOVE <messg #>, WRITE <header>\n\r");
	if (!board->msg_num)
		strcat(buf, "The board is empty.\n\r");
	else
	{
		sprintf(buf + strlen(buf), "There are %d messages on the board.\n\r",
			board->msg_num);
		for (i = 0; i < board->msg_num; i++)
			sprintf(buf + strlen(buf), "%-2d %s\n\r", i + 1, board->head[i]);
 	}
	page_string(ch->desc, buf, 1);

	return(1);
}