/*
 *                             M O V E . C
 *
 *  implements the move command:
 *  -  initialize & display popup
 *  -  send value
 *  -  interpret result
 *
 *  Version      : $Revision: 1.6 $
 *
 *  Created      : Wed Jun 15 00:16:48 1994
 *  Author       : Ulrich Drepper <drepper@mydec>
 *
 *  Last modified: Mon Jul 11 17:01:02 1994
 *  Author       : Ulrich Drepper <drepper@mydec>
 *
 *  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 1, 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.
 *
 */
#if !defined(lint)
static const char *vcid = "$Id: move.c,v 1.6 1994/07/11 16:39:06 drepper Exp $";
#endif /* lint */

#include <assert.h>
#include <malloc.h>
#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Toggle.h>

#include "empire.h"

/*
 * local data type
 */
typedef struct {
    int fromX;
    int fromY;
    int toX;
    int toY;
    char commodity;
    int number;
} MoveData;

/*
 * local variables
 */
static Widget movePopup;
static Widget form;
static Widget fromText;
static Widget firstToggle;
static Widget numberText;
static Widget pathText;
static Widget okButton;
static Widget currentEditWidget;
static void (*oldEditAdd)(Bool, XawTextBlock *, Bool);

static char *commStr[] = {
    "civ", "mil", "uw", "food", "guns", "shells", "petrol", "iron",
    "dust", "bars", "oil", "light", "heavy", "rad" };

/*
 * prototypes for local functions
 */
static void commandInitMove(void);
static void moveEditAdd(Bool active, XawTextBlock *textBlock, Bool async);
static void callbackMove(Widget widget, XtPointer closure, XtPointer callData);
static void prepareMoveEdit(void);
static void prevEdit(Widget w, XEvent *event, String *params,
		     Cardinal *numParams);
static void nextEdit(Widget w, XEvent *event, String *params,
		     Cardinal *numParams);
static void buttonOnText(Widget widget, XtPointer closure, XEvent *event,
			 Boolean *cont);
static Bool processMove(Bool first, Bool last, char *str, void *data);

/*
 * exported functions
 */
void
queryCommandMove(void)
{
    static Bool firstCall = True;
    Position fx, fy;
    Position x, y;

    if (firstCall) {
	commandInitMove();
	firstCall = False;
    }
    
    XtVaGetValues(
	commandForm,
	XtNx, &fx,
	XtNy, &fy,
	NULL);

    XtVaGetValues(
	horizPane,
	XtNx, &x,
	XtNy, &y,
	NULL);

    XtTranslateCoords(
	topLevel,
	x+fx, y+fy,
	&x, &y);

    XtVaSetValues(
	movePopup,
	XtNx, x,
	XtNy, y,
	NULL);

    /* prepare edit widget communication */
    oldEditAdd = currentEditAdd;
    oldEditAdd(False, NULL, False);

    currentEditAdd = moveEditAdd;
    currentEditWidget = fromText;

    prepareMoveEdit();
    
    XtPopup(movePopup, XtGrabNonexclusive);
    XtAddGrab(showViewport, False, False);
}

Bool
moveCommand(char comm, int sx, int sy, int number, int ex, int ey)
{
    MoveData *moveData;

    if (charToProduct(comm, empire.map[MAP2ARR(sx,sy)]) < number) {
	message(WARN, "move: sector content not enough");
	return False;
    }
    
    moveData = (MoveData*)malloc(sizeof(MoveData));
    if (!moveData) {
	message(WARN, "move: out of memory");
	return False;
    }

    moveData->fromX = sx;
    moveData->fromY = sy;
    moveData->toX = ex;
    moveData->toY = ey;
    moveData->commodity = comm;
    moveData->number = number;
    
    return sendCmdStr(processMove, moveData, NULL,
		      "move %c %d,%d %d %d,%d\n",
		      comm, sx, sy, number, ex, ey);
}

/*
 * local functions
 */
static void
commandInitMove(void)
{
    static String textTranslations = 
	"#override\n"
	"Shift<Key>Tab: prevMoveEdit(False)\n"
	"<Key>Tab: nextMoveEdit(False)\n"
	"<Key>Return: nextMoveEdit(True)\n"
	"<Key>Linefeed: nextMoveEdit(True)\n"
	"Ctrl<Key>M: nextMoveEdit(True)\n"
	"Ctrl<Key>J: nextMoveEdit(True)\n";
    static XtActionsRec cursorActions[2] = {
	{ "nextMoveEdit", nextEdit },
	{ "prevMoveEdit", prevEdit },
    };
    static String toggleTranslations =
	"<EnterWindow>: highlight(Always)\n"
	"<LeaveWindow>: unhighlight()\n"
	"<Btn1Down>,<Btn1Up>: set() notify()\n";
    static XtTranslations textTrans;
    static XtTranslations toggleTrans;
    Widget tmp;
    Bool first;
    int i;

    XtAppAddActions(appContext, cursorActions, 2);
    textTrans = XtParseTranslationTable(textTranslations);
    toggleTrans = XtParseTranslationTable(toggleTranslations);
    first = False;
    
    movePopup = XtVaCreatePopupShell(
	"movepopupshell",
	topLevelShellWidgetClass,
	topLevel,
	NULL);
    form = XtVaCreateManagedWidget(
	"form",
	formWidgetClass,
	movePopup,
	NULL);
    tmp = XtVaCreateManagedWidget(
	"move",
	labelWidgetClass,
	form,
	NULL);
    tmp = XtVaCreateManagedWidget(
	"fromlabel",
	labelWidgetClass,
	form,
	NULL);
    fromText = XtVaCreateManagedWidget(
	"fromtext",
	asciiTextWidgetClass,
	form,
	XtNeditType, XawtextEdit,
	XtNtranslations, textTrans,
	NULL);
    XtAddEventHandler(
	fromText,
	ButtonPressMask,
	False,
	buttonOnText,
	NULL);
    firstToggle = XtVaCreateManagedWidget(
	commStr[0],
        toggleWidgetClass,
        form,
        XtNradioData, commStr[0][0],
        XtNtranslations, toggleTrans,
	XtNstate, True,
	NULL);
    for (i=1; i<XtNumber(commStr); i++) {
	tmp = XtVaCreateManagedWidget(
	    commStr[i],
	    toggleWidgetClass,
	    form,
	    XtNradioData, commStr[i][0],
	    XtNradioGroup, firstToggle,
	    XtNtranslations, toggleTrans,
	    XtNstate, False,
	    NULL);
    }
    tmp = XtVaCreateManagedWidget(
	"numberlabel",
	labelWidgetClass,
	form,
	NULL);
    numberText = XtVaCreateManagedWidget(
	"numbertext",
	asciiTextWidgetClass,
	form,
	XtNeditType, XawtextEdit,
	XtNtranslations, textTrans,
	NULL);
    XtAddEventHandler(
	numberText,
	ButtonPressMask,
	False,
	buttonOnText,
	NULL);
    tmp = XtVaCreateManagedWidget(
	"pathlabel",
	labelWidgetClass,
	form,
	NULL);
    pathText = XtVaCreateManagedWidget(
	"pathtext",
	asciiTextWidgetClass,
	form,
	XtNeditType, XawtextEdit,
	XtNtranslations, textTrans,
	NULL);


    XtAddEventHandler(
	pathText,
	ButtonPressMask,
	False,
	buttonOnText,
	NULL);
    okButton = XtVaCreateManagedWidget(
	"OK",
	commandWidgetClass,
	form,
	NULL);
    XtAddCallback(
	okButton,
	XtNcallback, callbackMove,
	(XtPointer)True);
    tmp = XtVaCreateManagedWidget(
	"Cancel",
	commandWidgetClass,
	form,
	NULL);
    XtAddCallback(
	tmp,
	XtNcallback, callbackMove,
	(XtPointer)False);

    XtRealizeWidget(movePopup);
}

/* ARGSUSED */
static void
moveEditAdd(Bool active, XawTextBlock *textBlock, Bool async)
{
    assert(async==False);
    if (textBlock) {
	if (currentEditWidget == numberText) return;
	
	XtVaSetValues(
	    currentEditWidget,
	    XtNstring, textBlock->ptr,
	    NULL);
	XawTextSetInsertionPoint(currentEditWidget, strlen(textBlock->ptr));
    } else {
	XawTextDisplayCaret(currentEditWidget, active);
	if (active) {
	    XtSetKeyboardFocus(form, currentEditWidget);
	    XtSetKeyboardFocus(vertPane, currentEditWidget);
	}
    }
}

static void
callbackMove(Widget widget, XtPointer closure, XtPointer callData)
{
    if ((Bool)closure) {
	char *fromSector;
	char *number;
	char *path;
	int x, y, n;
	char comm;
	
	XtVaGetValues(
	    fromText,
	    XtNstring, &fromSector,
	    NULL);
	XtVaGetValues(
	    numberText,
	    XtNstring, &number,
	    NULL);
	XtVaGetValues(
	    pathText,
	    XtNstring, &path,
	    NULL);
	comm = (char)(int)XawToggleGetCurrent(firstToggle);

	if (strToCoord(&fromSector, &x, &y) &&
	    strToInt(&number, &n)) {
	    int ex = x, ey = y;

	    if (addDirToCoord(&path, &ex, &ey) &&
		empire.map[MAP2ARR(ex,ey)].status == OwnSector) {
		moveCommand(comm, x, y, n, ex, ey);

		prepareMoveEdit();
	    }
	}
    } else {
	/* close */
	XtPopdown(movePopup);

	currentEditAdd(False, NULL, False);
	currentEditAdd = oldEditAdd;
	oldEditAdd(True, NULL, False);
    }
}

static void
prepareMoveEdit(void)
{
    /* prepare for next input */
    XtVaSetValues(
	fromText,
        XtNstring, "",
        NULL);
    XtVaSetValues(
	numberText,
        XtNstring, "",
        NULL);
    XtVaSetValues(
	pathText,
        XtNstring, "",
        NULL);

    currentEditAdd(False, NULL, False);
    currentEditWidget = fromText;
    currentEditAdd(True, NULL, False);

    XawTextSetInsertionPoint(fromText, 0);
    XawTextDisplayCaret(numberText, False);
    XawTextSetInsertionPoint(numberText, 0);
    XawTextDisplayCaret(pathText, False);
    XawTextSetInsertionPoint(pathText, 0);
}

/* ARGSUSED */
static void
prevEdit(Widget w, XEvent *event, String *params, Cardinal *numParams)
{
    XawTextDisplayCaret(currentEditWidget, False);

    if (currentEditWidget == numberText) {
	currentEditWidget = fromText;
    } else if (currentEditWidget == pathText) {
	currentEditWidget = numberText;
    } else {
	currentEditWidget = pathText;
    }

    XawTextDisplayCaret(currentEditWidget, True);
    XtSetKeyboardFocus(form, currentEditWidget);
    XtSetKeyboardFocus(vertPane, currentEditWidget);
}

/* ARGSUSED */
static void
nextEdit(Widget w, XEvent *event, String *params, Cardinal *numParams)
{
    if (numParams != 0 && !strcmp(params[0], "True")) {
	XtCallActionProc(okButton, "set", NULL, NULL, 0);
	XtCallActionProc(okButton, "notify", NULL, NULL, 0);
	XtCallActionProc(okButton, "unset", NULL, NULL, 0);
    } else {
	XawTextDisplayCaret(currentEditWidget, False);

	if (currentEditWidget == fromText) {
	    currentEditWidget = numberText;
	} else if (currentEditWidget == pathText) {
	    currentEditWidget = fromText;
	} else {
	    currentEditWidget = pathText;
	}

	XawTextDisplayCaret(currentEditWidget, True);
	XtSetKeyboardFocus(form, currentEditWidget);	
	XtSetKeyboardFocus(vertPane, currentEditWidget);
    }
}

/* ARGSUSED */
static void
buttonOnText(Widget widget, XtPointer closure, XEvent *event, Boolean *cont)
{
    if (widget != currentEditWidget) {
	XawTextDisplayCaret(currentEditWidget, False);

	currentEditWidget = widget;
	
	XawTextDisplayCaret(currentEditWidget, True);
	XtSetKeyboardFocus(form, currentEditWidget);	
	XtSetKeyboardFocus(vertPane, currentEditWidget);
    }
}

static Bool
processMove(Bool first, Bool last, char *str, void *data)
{
    static Bool correctMove = True;
    MoveData *moveData = (MoveData*)data;
    int mob, x, y;
    
    if (first) {
	correctMove = (last==False);   /* could be aborted command */
    }

    if (last) {
	if (correctMove) {
	    charToProduct(moveData->commodity,
			  empire.map[MAP2ARR(moveData->fromX,
					     moveData->fromY)]) -=
		moveData->number;
	    charToProduct(moveData->commodity,
			  empire.map[MAP2ARR(moveData->toX,
					     moveData->toY)]) +=
		 moveData->number;
	}
	return True;
    }

    if (sscanf(str, "%d mob left in %d,%d", &mob, &x, &y) == 3) {
	empire.map[MAP2ARR(x,y)].mobility = mob;
	return True;
    }

    if (!strncmp(str, "Move aborted", 12) ||
	strstr(str, "command failed")) {
	correctMove = False;
	return True;
    }

    return True;
}

/*
 * Local Variables:
 *  mode:c
 *  c-indent-level:4
 *  c-continued-statement-offset:4
 *  c-continued-brace-offset:0
 *  c-brace-offset:0
 *  c-imaginary-offset:0
 *  c-argdecl-indent:4
 *  c-label-offset:-2
 * End:
 */
