/*
 *                          D E L I V E R . C
 *
 *  Implements the deliver command
 *
 *  Version      : $Revision: 1.6 $
 *
 *  Created      : Sat Jul  2 10:33:05 1994
 *  Author       : Ulrich Drepper <drepper@mydec>
 *
 *  Last modified: Mon Jul 11 20:05:20 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: deliver.c,v 1.6 1994/07/11 19:10:28 drepper Exp $";
#endif /* lint */

#include <assert.h>
#include <ctype.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 type definitions
 */
typedef struct {
    int xl;
    int yu;
    int xr;
    int yl;
    char comm;
    char dir;
    int thres;
} DeliverData;

/*
 * prototypes for local functions
 */
static void commandInitDeliver(void);
static void callbackDeliver(Widget widget, XtPointer closure,
			    XtPointer callData);
static void prepareDeliverEdit(void);
static void deliverEditAdd(Bool active, XawTextBlock *textBlock, Bool async);
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 processInfoDeliver(Bool first, Bool last, char *str, void *data);
static Bool processIgnThres(Bool first, Bool last, char *str, void *data);

/*
 * local variables
 */
static Widget deliverPopup;
static Widget form;
static Widget fromText;
static Widget toText;
static Widget thresText;
static Widget okButton;
static Widget currentEditWidget;
static Widget firstToggle;
static void (*oldEditAdd)(Bool, XawTextBlock *, Bool);

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

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

    if (firstCall) {
	commandInitDeliver();
	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(
        deliverPopup,
        XtNx, x,
        XtNy, y,
        NULL);

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

    currentEditAdd = deliverEditAdd;
    currentEditWidget = fromText;

    prepareDeliverEdit();

    XtPopup(deliverPopup, XtGrabNonexclusive);
    XtAddGrab(showViewport, False, False);
}

Bool
deliverCommand(char comm, int xl, int yu, int xr, int yl, char dir, int thres,
	       Bool ignDir)
{
    DeliverData *deliverData = (DeliverData*)malloc(sizeof(DeliverData));

    if (!deliverData) {
	message(ERROR, "deliver: out of memory");
	return False;
    }

    deliverData->xl = xl;
    deliverData->yu = yu;
    deliverData->xr = xr;
    deliverData->yl = yl;
    deliverData->comm = comm;
    deliverData->dir = dir;
    deliverData->thres = thres;

    if (ignDir) {
	return sendCmdStr(thres<0 ? processInfoDeliver
			          : processIgnThres,
			  deliverData, NULL,
			  "deliver %c %d:%d,%d:%d %c %d\n",
			  comm, xl, xr, yu, yl, dir, thres);
    } else {
	assert(xl==xr && yu==yl);    
	return sendCmdStr(processIgnThres, deliverData,  NULL,
			  "deliver %c %d,%d %c (%d)\n",
	                  comm, xl, yu, dir, thres);
    }
}

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

    deliverPopup = XtVaCreatePopupShell(
	"deliverpopupshell",
        topLevelShellWidgetClass,
        topLevel,
        NULL);
    form = XtVaCreateManagedWidget(
	"form",
        formWidgetClass,
        deliverPopup,
        NULL);
    tmp = XtVaCreateManagedWidget(
	"deliver",
        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);
    tmp = XtVaCreateManagedWidget(
	"tolabel",
        labelWidgetClass,
        form,
        NULL);
    toText = XtVaCreateManagedWidget(
        "totext",
        asciiTextWidgetClass,
        form,
        XtNeditType, XawtextEdit,
        XtNtranslations, textTrans,
        NULL);
    XtAddEventHandler(
        toText,
        ButtonPressMask,
        False,
        buttonOnText,
        NULL);
    tmp = XtVaCreateManagedWidget(
	"threslabel",
        labelWidgetClass,
        form,
        NULL);
    thresText = XtVaCreateManagedWidget(
        "threstext",
        asciiTextWidgetClass,
        form,
        XtNeditType, XawtextEdit,
        XtNtranslations, textTrans,
        NULL);
    XtAddEventHandler(
        thresText,
        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);
    }
    okButton = XtVaCreateManagedWidget(
        "OK",
        commandWidgetClass,
        form,
        NULL);
    XtAddCallback(
        okButton,
        XtNcallback, callbackDeliver,
        (XtPointer)True);
    tmp = XtVaCreateManagedWidget(
        "Cancel",
        commandWidgetClass,
        form,
        NULL);
    XtAddCallback(
        tmp,
        XtNcallback, callbackDeliver,
        (XtPointer)False);

    XtRealizeWidget(deliverPopup);
}

/* ARGSUSED */
static void
deliverEditAdd(Bool active, XawTextBlock *textBlock, Bool async)
{
    assert(async==False);
    if (textBlock) {
        if (currentEditWidget == thresText) 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
callbackDeliver(Widget widget, XtPointer closure, XtPointer callData)
{
    if ((Bool)closure) {
        char *fromSector;
	char *toSector;
        char *thres;
        int xls, xrs, yus, yls, xe, ye, thresVal;
	char dir;
        char comm;
	char ignDir = True;

        XtVaGetValues(
            fromText,
            XtNstring, &fromSector,
            NULL);
        XtVaGetValues(
            toText,
            XtNstring, &toSector,
            NULL);
        XtVaGetValues(
            thresText,
            XtNstring, &thres,
            NULL);
        comm = (char)(int)XawToggleGetCurrent(firstToggle);

	if (!strToRange(&fromSector, &xls, &yus, &xrs, &yls)) {
	    message(WARN, "deliver: illegal from coord");
	    return;
	}

	while (isspace(*toSector)) toSector++;
	if (toSector[0] && !toSector[1]) { /* only one character */
	    if (toSector[0] != 'y' && toSector[0] != 'u' &&
		toSector[0] != 'g' && toSector[0] != 'h' &&
		toSector[0] != 'j' && toSector[0] != 'b' &&
		toSector[0] != 'n') {
		message(WARN, "deliver: illegal path");
		return;
	    }
	    dir = toSector[0];
	} else {
	    if (xls != xrs || yus != yls ||    /* only direction when range */
		!strToCoord(&toSector, &xe, &ye)) {
		message(WARN, "deliver: illegal destination coord");
		return;
	    }
	    xe -= xls;
	    ye -= yus;
	    if (abs(xe)+abs(ye)>2 ||
		(dir = ((char[]){'X','X','X','X','X',
			 	 'X','y','X','u','X',
			         'g','X','h','X','j',
			         'X','b','X','n','X',
				 'X','X','X','X','X'})[xe+2+5*(ye+2)])=='X') {
		message(WARN, "deliver: dest sector not neighbor");
		return;
	    }
	}

	if (*thres) {
	    while (isspace(*thres)) thres++;
	    
	    if (*thres == '(') {
		ignDir = False;
		thres++;
	    }
	    if (!strToInt(&thres, &thresVal) ||
		(!ignDir && *thres !=')')) {
		message(WARN, "deliver: illegal threshold value");
		return;
	    }

	    if (!ignDir && (xls != xrs || yus != yls)) {
		message(WARN, "deliver: only single sector allowed "
			      "when ()-threshold");
		return;
	    }
	} else {
	    message(WARN, "deliver: illegal threshold value");
	    return;
	}

	deliverCommand(comm, xls, yus, xrs, yls, dir, thresVal, ignDir);

	prepareDeliverEdit();
    } else {
	XtPopdown(deliverPopup);

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

static void
prepareDeliverEdit(void)
{
    /* prepare new input */
    XtVaSetValues(
	fromText,
        XtNstring, "",
        NULL);
    XtVaSetValues(
	thresText,
        XtNstring, "",
        NULL);
    XtVaSetValues(
	toText,
        XtNstring, "",
        NULL);

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

    XawTextSetInsertionPoint(fromText, 0);
    XawTextDisplayCaret(toText, False);
    XawTextSetInsertionPoint(toText, 0);
    XawTextDisplayCaret(thresText, False);
    XawTextSetInsertionPoint(thresText, 0);
}

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

    if (currentEditWidget == thresText) {
        currentEditWidget = fromText;
    } else if (currentEditWidget == toText) {
        currentEditWidget = thresText;
    } else {
        currentEditWidget = toText;
    }

    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 = toText;
        } else if (currentEditWidget == toText) {
            currentEditWidget = thresText;
        } else {
            currentEditWidget = fromText;
        }

        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
processInfoDeliver(Bool first, Bool last, char *str, void *data)
{
    static Bool ignore = False;
    DeliverData *deliverData = (DeliverData*)data;
    int fromX, fromY;
    int toX, toY;
    int thres;
    int idx;
    EmpireSector *sector;
    char dir;

    if (last) {
	free(deliverData);
	return True;
    }

    if (ignore) return True;

    if (strncmp(str, "Deliver", 7)) {
	ignore = True;
	return True;
    }

    while (*str && *str!='@') str++;
    assert(*str);

    str++;
    if (!strToCoord(&str, &fromX, &fromY)) {
	ignore = True;
	return True;
    }

    while (isspace(*str)) str++;
    while (*str && !isspace(*str)) str++;
    assert(*str);

    if (!strToCoord(&str, &toX, &toY)) {
	ignore = True;
	return True;
    }

    while (isspace(*str)) str++;
    if (*str) {
	if (sscanf(str, " (cutoff %d", &thres) != 1) {
	    ignore = True;
	    return True;
	}
    } else {
	thres = 0;
    }

    idx = MAP2ARR(fromX, fromY);
    sector = &empire.map[idx];
    
    toX -= fromX;
    toY -= fromY;

    assert(abs(toX)+abs(toY)<=2);
    
    dir = ((char[]){'X','X','X','X','X',
		    'X','y','X','u','X',
		    'g','X','h','X','j',
		    'X','b','X','n','X',
		    'X','X','X','X','X'})[(toX+2)+5*(toY+2)];
    assert(dir != 'X');

    charToPath(deliverData->comm, *sector)   = dir;
    charToCutoff(deliverData->comm, *sector) = thres;

    return True;
}

static Bool
processIgnThres(Bool first, Bool last, char *str, void *data)
{
    DeliverData *deliverData = (DeliverData*)data;
    
    if (last) {
	free(deliverData);
	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:
 */
