/* $Id: wintalk.c,v 5.0 2001/04/07 20:00:59 dik Exp $
 *
 * XPilot, a multiplayer gravity war game.  Copyright (C) 1991-2001 by
 *
 *      Bjørn Stabell        <bjoern@xpilot.org>
 *      Ken Ronny Schouten   <ken@xpilot.org>
 *      Bert Gijsbers        <bert@xpilot.org>
 *      Dick Balaska         <dick@xpilot.org>
 *
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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 <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#include <errno.h>

#include "version.h"
#include "config.h"
#include "const.h"
#include "../paint.h"
#include "../xinit.h"
#include "error.h"
#include "../netclient.h"
#include "../protoclient.h"
#include "keys.h"
#include "bit.h"

char talk_version[] = VERSION;

#ifndef	lint
static char sourceid[] =
    "@(#)$Id: wintalk.c,v 5.0 2001/04/07 20:00:59 dik Exp $";
#endif


/* Information window dimensions */
#define TALK_TEXT_HEIGHT	(textFont->ascent + textFont->descent)
#define TALK_OUTSIDE_BORDER	2
#define TALK_INSIDE_BORDER	3
#define TALK_WINDOW_HEIGHT	(TALK_TEXT_HEIGHT + 2 * TALK_INSIDE_BORDER)
#define TALK_WINDOW_X		(50 - TALK_OUTSIDE_BORDER)
#define TALK_WINDOW_Y		(draw_height*3/4 - TALK_WINDOW_HEIGHT/2)
#define TALK_WINDOW_WIDTH	(draw_width \
				    - 2*(TALK_WINDOW_X + TALK_OUTSIDE_BORDER))

#define CTRL(c)			((c) & 0x1F)

/*
 * Globals.
 */
bool			talk_mapped;

static bool		talk_created;
static char		talk_str[MAX_CHARS];
static struct {
    bool		visible;
    short		offset;
    short		point;
} talk_cursor;

/* position in history when browsing in the talk window */
static int		history_pos = 0;

/* faster cursor movement in talk window after pressing a key for some time */
#define CRS_START_HOPPING	7
#define CRS_HOP			4

/* selections in draw and talk window */
selection_t		selection;
bool			save_talk_str	 = false; /* see Get_msg_from_history */

/* if a cut doesn't suceed, leave the selection in appropriate state */
#define SET_SELECTION_STATE			\
if (selection.len > 0)				\
    selection.draw.state = SEL_SELECTED;	\
else						\
    selection.draw.state = SEL_NONE;

extern keys_t Lookup_key(XEvent *event, KeySym ks, bool reset);
extern void Add_pending_messages(void);

extern message_t	*TalkMsg[MAX_MSGS], *GameMsg[MAX_MSGS];
extern char		*HistoryMsg[MAX_HIST_MSGS];

static void Talk_create_window(void)
{
}


void Talk_cursor(bool visible)
{
}


void Talk_map_window(bool map)
{
    
}

/*
 * redraw a possible selection [un]emphasized.
 * to unemphasize a selection, `selection.txt' is needed.
 * thus selection.talk.state == SEL_SELECTED indicates that it
 * should not be drawn emphasized
 */
void Talk_refresh() {
}

/*
 * add a line to the history.
 */
void Add_msg_to_history(char *message) {
    char *tmp;
    char **msg_set;
    int i;

    /*always*/
    save_talk_str = false;

    if (!selectionAndHistory || strlen(message) == 0) {
	return; /* unexpected. nothing to add */
    }

    msg_set = HistoryMsg;
    /* pipe the msgs through the buffer, the latest getting into [0] */
    tmp = msg_set[maxLinesInHistory - 1];
    for (i = maxLinesInHistory - 1; i > 0; i--) {
	msg_set[i] = msg_set[i - 1];
    }
    msg_set[0] = tmp; /* memory recycling */

    strcpy(msg_set[0], message);
    history_pos = 0;

    return;
}

/*
 * Fetch a message from the `history' by returning a pointer to it.
 * Choice depends on current poition (*pos, call by ref for modification here)
 * and `direction' of browsing
 *
 * if we are here _and the gobal `save_talk_str' is set, then the submitted
 * message is unfinished - call Add_msg_to_history(), but don't return
 * the next line.
 * Purpose: gives ability to abort writing a message and resume later.
 * The global `save_talk' can be set anywhere else in the code whenever
 * a line from the history gets modified
 * (thus save_talk not as parameter here)
 *
 */
char *Get_msg_from_history(int* pos, char *message, keys_t direction) {
    int i;
    char **msg_set;

    if (!selectionAndHistory
	|| (direction != KEY_TALK_CURSOR_UP && direction != KEY_TALK_CURSOR_DOWN
	    && direction != KEY_DUMMY)) {
	return NULL;
    }

    if (direction == KEY_DUMMY && (*pos < 0 || *pos > maxLinesInHistory-1)) {
	*pos = 0;
    }

    msg_set = HistoryMsg;

    if (save_talk_str) {
	if (strlen(message) > 0) {
	    Add_msg_to_history(message);
	}
	save_talk_str = false;
	return NULL;
    }
 
    /* search for the next message, return it */
    for (i=0; i < maxLinesInHistory; i++) {
	if (direction == KEY_TALK_CURSOR_UP) {
            (*pos)++;
            if (*pos >= maxLinesInHistory) {
                *pos = 0; /* wrap */
            }
	} else if (direction == KEY_TALK_CURSOR_DOWN) {
	    (*pos)--;
	    if (*pos < 0) {
		*pos = maxLinesInHistory - 1; /*wrap*/
	    }
	}
	if (strlen(msg_set[*pos]) > 0) {
	    return (msg_set[*pos]);
	}
    }
    return NULL; /* no history */
}

/*
 * Print all available messages to stdout.
 */
void Print_messages_to_stdout(void) {
}

/*
 * Pressing a key while there is an emphasized text in the talk window
 * substitutes this text, means: delete the text before inserting the
 * new character and place the character at this `gap'.
 */
void Talk_delete_emphasized_text() {

    int		oldlen, newlen;
    int		onewidth = XTextWidth(talkFont, talk_str, 1);
    char	new_str[MAX_CHARS];

    if (! (selectionAndHistory && selection.talk.state == SEL_EMPHASIZED)) {
	return;
    }

    Talk_cursor(false);

    strcpy(new_str, talk_str);
    oldlen = strlen(talk_str);
    newlen = oldlen;

    if (oldlen > 0) {
	strncpy(&new_str[selection.talk.x1], &new_str[selection.talk.x2],
		oldlen - selection.talk.x2);
	new_str[selection.talk.x1 + oldlen - selection.talk.x2] = '\0';
	talk_cursor.point = selection.talk.x1;
	newlen -= (selection.talk.x2 - selection.talk.x1);
	selection.talk.state = SEL_NONE;
	new_str[newlen] = '\0';
	if (talk_cursor.point > newlen) {
	    talk_cursor.point = newlen;
	}
    }
    new_str[newlen] = '\0';
    if (talk_cursor.point > newlen) {
	talk_cursor.point = newlen;
    }

    /*
     * Now reflect the text changes onto the screen.
     */
    if (newlen < oldlen) {
	XSetForeground(dpy, talkGC, colors[BLACK].pixel);
	XDrawString(dpy, talk_w, talkGC,
		    talk_cursor.point * onewidth + TALK_INSIDE_BORDER,
		    talkFont->ascent + TALK_INSIDE_BORDER,
		    &talk_str[talk_cursor.point],
		    oldlen - talk_cursor.point);
	XSetForeground(dpy, talkGC, colors[WHITE].pixel);
    }
    if (talk_cursor.point < newlen) {
	XDrawString(dpy, talk_w, talkGC,
		    talk_cursor.point * onewidth + TALK_INSIDE_BORDER,
		    talkFont->ascent + TALK_INSIDE_BORDER,
		    &new_str[talk_cursor.point],
		    newlen - talk_cursor.point);
    }
    Talk_cursor(true);

    strcpy(talk_str, new_str);
}



/*
 * Try to paste `data_len' amount of `data' at the cursor position into
 * the talk window.  Cut off overflow (`accept_len').
 *
 * The global `talk_str' will contain the new content.
 * (safe if *data references *talk_str).
 *
 * if `overwrite' then don't insert/append but substitute the text
 *
 * Return the number of pasted characters.
 */
int Talk_paste(char *data, int data_len, bool overwrite) {

    int str_len;			/* current length */
    int max_len    = MAX_CHARS - 2;	/* absolute max */
    int new_len;			/* after pasting */
    int char_width = XTextWidth(talkFont, talk_str, 1);
    int max_width  = (TALK_WINDOW_WIDTH - 2*TALK_INSIDE_BORDER - 5);
 
    int accept_len;			/* for still matching the window */
    char paste_buf[MAX_CHARS -2];	/* gets the XBuffer */
    char tmp_str[MAX_CHARS - 2];
    char talk_backup[MAX_CHARS - 2];	/* no `collision' with data */
    bool cursor_visible = false;
    int i;
 
    if (!selectionAndHistory || !data || data_len == 0 || strlen(data) == 0) {
	return 0;
    }

    if (overwrite) {
	/* implicitly, old text will be deleted now: */
	str_len = 0;
    } else {
	str_len = strlen(talk_str);
	strcpy(talk_backup, talk_str);
    }
    accept_len = (max_width / char_width) - str_len + 1;
    if (accept_len + str_len > max_len)
	accept_len = max_len - str_len;

    if (!accept_len) {
	/* no room to broom */
	XBell(dpy, 100);
	return 0;
    }
    if (data_len > accept_len) {
	/* not all accepted */
	XBell(dpy, 100);
    } else if (data_len < accept_len) {
	/* not the whole string required to paste */
	accept_len = data_len;
    }
    strncpy(paste_buf, data , accept_len);
    paste_buf[accept_len] = '\0';

    /*
     * substitute unprintables according to iso-latin-1.
     *  (according to `data_len' one could ask for all but the
     *   final '\0' to be converted, but we have only text selections anyway)
     */
    /* don't convert a final newline to space */
    if (paste_buf[accept_len-1] == '\n' || paste_buf[accept_len-1] == '\r') {
	paste_buf[accept_len-1] = '\0';
	accept_len--;
    }
    for(i = 0; i<accept_len; i++) {
        if (	  ((unsigned char)paste_buf[i] <   33
		/* && (unsigned char)paste_buf[i] != '\0' */)
	    ||    ((unsigned char)paste_buf[i] >  126
		&& (unsigned char)paste_buf[i] <  161) ) {
            paste_buf[i] = ' ';
        }
    }
 
    if (overwrite) {
	strncpy(tmp_str, paste_buf, accept_len);
	tmp_str[accept_len] = '\0';
	new_len = accept_len;
    } else {
	/* paste: insert/append */
	strcpy(tmp_str, talk_backup);
	strcpy(&tmp_str[talk_cursor.point], paste_buf);
	strcpy(&tmp_str[talk_cursor.point + accept_len],
	       &talk_backup[talk_cursor.point]);
	new_len = str_len + accept_len;
    }
 

    /*
     * graphics
     */
    if (overwrite) {
	XClearWindow(dpy, talk_w);
	XSetForeground(dpy, talkGC, colors[WHITE].pixel);
	XDrawString(dpy, talk_w, talkGC,
		    TALK_INSIDE_BORDER,
		    talkFont->ascent + TALK_INSIDE_BORDER,
		    tmp_str, accept_len );
    } else {
	if (selection.talk.state == SEL_EMPHASIZED) {
	    /* redraw unemphasized */
	    selection.talk.state = SEL_SELECTED;
	    Talk_refresh();
	}
	if (talk_cursor.point < str_len) {
	    /*
	     * erase from the point of insertion on
	     */
	    XSetForeground(dpy, talkGC, colors[BLACK].pixel);
	    XDrawString(dpy, talk_w, talkGC,
			talk_cursor.point * char_width + TALK_INSIDE_BORDER,
			talkFont->ascent + TALK_INSIDE_BORDER,
			&talk_backup[talk_cursor.point],
			str_len - talk_cursor.point);
	    XSetForeground(dpy, talkGC, colors[WHITE].pixel);
	}

	/* the new part of the line */
	XDrawString(dpy, talk_w, talkGC,
		    talk_cursor.point * char_width + TALK_INSIDE_BORDER,
		    talkFont->ascent + TALK_INSIDE_BORDER,
		    &tmp_str[talk_cursor.point],
		    new_len - talk_cursor.point);
    }
    strcpy(talk_str, tmp_str);

    cursor_visible = talk_cursor.visible;
    Talk_cursor(false);
    if (overwrite) {
	talk_cursor.point = new_len;
    } else {
	talk_cursor.point += accept_len;
    }
    Talk_cursor(cursor_visible);

    return accept_len; 
}


/*
 * place the cursor in the talk window with help of the pointer button.
 * return the cursor position as index in talk_str.
 *
 * place cursor conveniently, if `pending' is set and cutting
 * in the talk window finished outside of it (border is also outside).
 */

void Clear_talk_selection()
{
    selection.talk.x1 = selection.talk.x2 = 0;
    selection.talk.state = SEL_NONE;
    selection.talk.incl_nl = false;
}
     
void Clear_draw_selection()
{
    selection.draw.x1 = selection.draw.x2
	= selection.draw.y1 = selection.draw.y2 = 0;
    selection.draw.state = SEL_NONE;
}
    
/*
 * show that someone else owns the selection now
 */
void Clear_selection()
{
    if (!selectionAndHistory)
	return;

    if (talk_mapped && selection.talk.state == SEL_EMPHASIZED) {
	/* trick to unemphasize */
	selection.talk.state = SEL_SELECTED;
	Talk_refresh();
    }
    Clear_draw_selection();
    Clear_talk_selection();
    if (selection.txt)
	free(selection.txt);
    selection.txt = NULL;
    selection.len = 0;

}

/*
 * cutting from talk window happens here. wanted: selection.txt and the
 * string indices of start and end (selection.draw.x1/2)
 * call Talk_place_cursor() to place the cursor.
 */
void Talk_window_cut(XButtonEvent* xbutton) {
}

/*
 * wanted:
 * indices of the characters and the rows of start and end of
 * the selected text.
 *
 * for proper cutting: Notice if cutting heppens left from the most left
 * or right from the most right character (c1/2.x_off).
 *
 * while the cut is pending, the state of Talk+GameMsg[] is `freezed'
 * in Paint_message(). thus call Add_pending_messages() here after this.
 */
void Talk_cut_from_messages(XButtonEvent* xbutton) {

}

void Talk_reverse_cut() {
    /*
     * think twice: it can't work (yet) without hacking all the
     * c&p stuff even more. thus only unemphasize:
     */
    if (selectionAndHistory && selection.draw.state == SEL_EMPHASIZED) {
	selection.draw.state = SEL_SELECTED;
    }
}
