/*
 *                            X I N I T . C
 *
 *  Initialize the X data structures.  The windows are created but not
 *  mapped.  To be called before the main loop.
 *
 *  Version      : $Revision: 1.16 $
 *
 *  Created      : Fri May 27 10:48:05 1994
 *  Author       : Ulrich Drepper <drepper@mydec>
 *
 *  Last modified: Wed Jul 13 23:00:59 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: xinit.c,v 1.16 1994/07/14 09:44:08 drepper Exp $";
#endif /* lint */

#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <memory.h>
#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Dialog.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/Paned.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/SmeLine.h>
#include <X11/Xaw/Viewport.h>
#include "Content.h"

#include "empire.h"
#include "bitmaps.h"
#include "scheme.h"

#if defined(NEED_XPM)
#include "xpm.h"
#else
#include <X11/xpm.h>
#endif /* else NEED_XPM */

#ifdef sun
int on_exit(void(*procp)(int,caddr_t), caddr_t arg);
#define atexit(fct) on_exit((void(*)(int,caddr_t))fct, NULL)
#endif /* sun */

/*
 * exported variables
 */
Bool isMonoApp = False;
Bool xIsUp = False;
Scheme_Object *mapSelection;
XtAppContext appContext;
Widget topLevel;                /* the toplevel window */
Widget vertPane;
Widget messageText;             /* message in the menu line */
Widget commandForm;
Widget showViewport;            /* viewport for text/map */
Widget mapWidget;               /* widget for map */
Widget outputWidget;
Widget editWidget;
Widget connIcon;
Widget breakCommandWidget;
Widget horizPane;
Widget contentWidget;
Widget coordMenuLabel;
int actMenuCoordC;
int actMenuCoordR;
GC mapGC;   
GC mapXorGC;   
char messageStr[80];
Pixmap iconConnected;
Pixmap iconNotConnected;
void (*currentEditAdd)(Bool, XawTextBlock *, Bool);

#define PIXMAPDATA(name) \
       #name, name##_xpm, enemy_##name##_xpm, hilit_##name##_xpm
XpmPixmap hexaPixmap[] = {
    { ' ', False, PIXMAPDATA(empty), empty_xpm, },
    { '.', False, PIXMAPDATA(sea), free_sea_xpm, },
    { '-', False, PIXMAPDATA(wilderness), free_wilderness_xpm, },
    { '+', True,  PIXMAPDATA(highway), NULL, },
    { '#', True,  PIXMAPDATA(bridgehead), NULL, },
    { '=', False, PIXMAPDATA(bridgespan), NULL, },
    { 'j', True,  PIXMAPDATA(light), NULL, },
    { 'k', True,  PIXMAPDATA(heavy), NULL, },
    { 'f', True,  PIXMAPDATA(fort), NULL, },
    { 'l', True,  PIXMAPDATA(library), NULL, },
    { 'o', True,  PIXMAPDATA(oil), NULL, },
    { 'b', True,  PIXMAPDATA(bank), NULL, },
    { 'c', True,  PIXMAPDATA(capitol), NULL, },
    { '^', False, PIXMAPDATA(mountain), free_mountain_xpm, },
    { 'n', True,  PIXMAPDATA(nuclearplant), NULL, },
    { ')', True,  PIXMAPDATA(radar), NULL, },
    { 's', False, PIXMAPDATA(sanctuary), sanctuary_xpm, },
    { 't', True,  PIXMAPDATA(technical), NULL, },
    { '/', False, PIXMAPDATA(wasteland), NULL, },
    { '?', False, PIXMAPDATA(unknown), NULL, },
    { 'm', True,  PIXMAPDATA(mine), NULL, },
    { 'g', True,  PIXMAPDATA(goldmine), NULL, },
    { 'h', True,  PIXMAPDATA(harbor), NULL, },
    { 'w', True,  PIXMAPDATA(warehouse), NULL, },
    { 'a', True,  PIXMAPDATA(agri), NULL, },
    { 'i', True,  PIXMAPDATA(shell), NULL, },
    { 'd', True,  PIXMAPDATA(gun), NULL, },
    { 'u', True,  PIXMAPDATA(uranmine), NULL, },
    { 'p', True,  PIXMAPDATA(park), NULL, },
    { 'r', True,  PIXMAPDATA(research), NULL, },
    { '!', True,  PIXMAPDATA(headquarter), NULL, },
    { '*', True,  PIXMAPDATA(airbase), NULL, },
    { 'e', True,  PIXMAPDATA(enlist), NULL, },
    { '%', True,  PIXMAPDATA(refinery), NULL, },
    { '\0', False, NULL, }
};
#undef PIXMAPDATA

/*
 * prototypes for local functions
 */
static void createMenus(Widget form);
static Bool createInfoForm(Widget pane);
static void commandButtonAction(Widget w, XEvent *event, String *params,
				Cardinal *numParams);
static void askExitProc(Widget widget, XtPointer closure, XtPointer callData);
static void answerExitProc(Widget widget, XtPointer closure, XtPointer callData);
static void mainEditAdd(Bool active, XawTextBlock *textBloc, Bool asynck);
static void freePixmaps(void);
static struct {
    char *name;
    XtCallbackProc proc;
    Widget widget;
} commands[] = {
    { "explore", (XtCallbackProc)queryCommandExplore, },
    { "move", (XtCallbackProc)queryCommandMove, },
    { "p4", NULL, },
    { "designate", (XtCallbackProc)queryCommandDesignate, },
    { "census", (XtCallbackProc)queryCommandCensus, },
    { "p5", NULL, },
    { "map", (XtCallbackProc)queryCommandMap, },
    { "resource", (XtCallbackProc)queryCommandResource, },
    { "p6", NULL, }
};

/*
 * local variables
 */
static Widget exitShell;
static Widget menuLine;
static struct {
    char *name;
    Widget widget;
} contentField[] = {
    { "Content", },
    { "contentCivilian", },
    { "contentMilitary", },
    { "contentUWorkers", },
    { "contentFood", },
    { "contentWork", },
    { "contentAvailable", }
};
	
/* bitmap data */
#include "bitmaps/connected"
#include "bitmaps/notconnected"

/*
 * exported functions
 */
Bool
xinit(int *pargc, char *argv[])
{
    Widget mapTextPane;
    static XtActionsRec commButAction[1] = {
	{ "commandButton", commandButtonAction },
    };

#if XtSpecificationRelease>=6
    topLevel = XtVaOpenApplication(
#else
    topLevel = XtVaAppInitialize(
#endif
	&appContext,
	"Myxec",
	NULL,
	0,
	pargc,
	argv,
	NULL,
#if XtSpecificationRelease>=6
	sessionShellWidgetClass,
#endif
	NULL);

    vertPane = XtVaCreateManagedWidget(
	"vertpane",
	panedWidgetClass,
	topLevel,
	NULL);

    menuLine = XtVaCreateManagedWidget(
	"menuline",
	formWidgetClass,
	vertPane,
	NULL);

    iconConnected = XCreateBitmapFromData(
	XtDisplay(menuLine),
	RootWindowOfScreen(XtScreen(menuLine)),
	connected_bits,
	connected_width, connected_height);
    iconNotConnected = XCreateBitmapFromData(
	XtDisplay(menuLine),
	RootWindowOfScreen(XtScreen(menuLine)),
	notconnected_bits,
	notconnected_width, notconnected_height);

    connIcon = XtVaCreateManagedWidget(
	"connicon",
	labelWidgetClass,
	menuLine,
	XtNbitmap, connected ? iconConnected : iconNotConnected,
	NULL);

    createMenus(menuLine);

    coordMenuLabel = XtVaCreateManagedWidget(
	"coordmenulabel",
        labelWidgetClass,
        menuLine,
        XtNlabel, "         ",
        NULL);
    actMenuCoordC = actMenuCoordR = INT_MIN;
    
    horizPane = XtVaCreateManagedWidget(
	"horizpane",
	panedWidgetClass,
	vertPane,
	NULL);

    createInfoForm(horizPane);

    mapTextPane = XtVaCreateManagedWidget(
	"maptextpane",
        panedWidgetClass,
        horizPane,
        NULL);
    
    showViewport = XtVaCreateManagedWidget(
	"showviewport",
	viewportWidgetClass,
	mapTextPane,
	NULL);

    /* actions for the edit widget */
    XtAppAddActions(appContext, commButAction, 1);

    outputWidget = XtVaCreateManagedWidget(
	"outputwidget",
        asciiTextWidgetClass,
        mapTextPane,
        XtNeditType, XawtextAppend,
        NULL);
    XawTextDisplayCaret(outputWidget, False);
    
    editWidget = XtVaCreateManagedWidget(
	"editwidget",
	asciiTextWidgetClass,
	mapTextPane,
	XtNeditType, XawtextEdit,
	NULL);
    XtSetKeyboardFocus(
	vertPane,
	editWidget);
    currentEditAdd = mainEditAdd;
    
    messageText = XtVaCreateManagedWidget(
	"message",
	labelWidgetClass,
	vertPane,
 	NULL);
    messageStr[0] = '\0';
    XtVaSetValues(
	messageText,
        XtNstring, "[??:??]",
        NULL);

    /* add actions */
    registerActions();

    XtRealizeWidget(topLevel);

    return True;
}

Bool
initXMap(void)
{
    int i;
    int xpmStatus;
    XpmAttributes xpmAttributes;
    Dimension visWidth  = 0;
    Dimension visHeight = 0;
    Dimension realWidth, realHeight;

    /* now the empire structure is available */
    XtVaSetValues(
	contentWidget,
        XtNsector, &empire.map[empire.xMapSize/4+
	                       empire.yMapSize/2*empire.rowBytes],
        NULL);

    /* at least clear the structure */
    memset(&xpmAttributes, 0, sizeof(XpmAttributes));

    realWidth = HEXAPIXMAPWIDTH*(empire.xMapSize+1)/2+1;
    realHeight = HEXATILEPIXMAPHEIGHT*empire.yMapSize+
	           (HEXAPIXMAPHEIGHT-HEXATILEPIXMAPHEIGHT)+1;
    
    mapWidget = XtVaCreateWidget(
	"mapwidget",
	simpleWidgetClass,
	showViewport,
	XtNwidth, realWidth,
	XtNheight, realHeight,
	NULL);

    /* widget must be realized to serve for picture widget */
    XtRealizeWidget(mapWidget);
    
    /* we may need the pixmap for initializing the empire data */
    for (i=0; hexaPixmap[i].name!=NULL; i++) {
#define CREATEPIXMAP(field) \
	if (hexaPixmap[i].data_##field!=NULL) { \
	    if ((xpmStatus = XpmCreatePixmapFromData( \
		XtDisplay(mapWidget), \
	        XtWindow(mapWidget), \
	        hexaPixmap[i].data_##field, \
	        &hexaPixmap[i].pixmap_##field, \
	        NULL, \
	        &xpmAttributes))) { \
	        message(ERROR, "%s", XpmGetErrorString(xpmStatus)); \
	        return False; \
	    } \
	    if (xpmAttributes.width != HEXAPIXMAPWIDTH+1 || \
	        xpmAttributes.height != HEXAPIXMAPHEIGHT+1) { \
	        message(FATAL, \
			"this is not the intended size of the pixmaps\n" \
			"I expect %d x %d", \
			HEXAPIXMAPWIDTH+1, HEXAPIXMAPHEIGHT+1); \
		return False; \
	    } \
	} else { \
	    hexaPixmap[i].pixmap_##field = 0; \
	}
	CREATEPIXMAP(normal);
	CREATEPIXMAP(enemy);
	CREATEPIXMAP(hilit);
	CREATEPIXMAP(free);
#undef CREATEPIXMAP
    }
    
    atexit(freePixmaps);

    mapGC = XtGetGC(
	mapWidget,
	0,
	NULL);
    XSetFunction(XtDisplay(mapWidget), mapGC, GXor);
    mapXorGC = XtGetGC(
	mapWidget,
	0,
	NULL);
    XSetFunction(XtDisplay(mapWidget), mapXorGC, GXxor);
    XSetForeground(
	XtDisplay(mapWidget), 
	mapXorGC, 
	XBlackPixelOfScreen(XtScreen(mapWidget)));

    XtAddEventHandler(
	mapWidget,
	ExposureMask,
	False,
	(XtEventHandler)exposeMap,
	NULL);
    XtAddEventHandler(
	mapWidget,
	ButtonPressMask|ButtonReleaseMask|Button2MotionMask|LeaveWindowMask|
        PointerMotionMask,
	False,
	(XtEventHandler)buttonOnMap,
	NULL);

    XtManageChild(mapWidget);

    XtVaGetValues(
	showViewport,
        XtNwidth, &visWidth,
        XtNheight, &visHeight,
        NULL);
    
    XawViewportSetLocation(   /* center viewport */
	showViewport,
        max(0.0, 0.5-(double)visWidth/((double)realWidth*2.0)),
        max(0.0, 0.5-(double)visHeight/((double)realHeight*2.0)));

    /* now that the window are up and the first update is necessary we
     * have to initialize the *.typePixmap fields
     */
    for (i=0; i<empire.yMapSize*empire.rowBytes; i++) {
	empire.map[i].typePixmap =
	    getTypePixmap(i%empire.rowBytes,
			  i/empire.rowBytes,
			  empire.map[i].status,
			  False);
    }

    XtVaSetValues(
	breakCommandWidget,
        XtNsensitive, !empire.isBroken,
        NULL);

    xIsUp = True;
    
    return True;
}

Pixmap
getTypePixmap(int x, int y, SectorStatus status, Bool doHilit)
{
    int idx;
    char typeChar;
    int i;

    idx = y*empire.rowBytes+x;
    typeChar = empire.map[idx].typeChar;

    for (i=0; hexaPixmap[i].name!=NULL; i++) {
	if (hexaPixmap[i].typeChar == typeChar) break;
    }

    if (hexaPixmap[i].name!=NULL) {
	/* status is not important when highlighting */
	if (doHilit) {
	    return hexaPixmap[i].pixmap_hilit;
	} 

	switch (status) {
	case OwnSector:
	    return hexaPixmap[i].pixmap_normal;
	case EnemySector:
	    return hexaPixmap[i].pixmap_enemy;
	case NobodiesSector:
	    assert(typeChar == ' ' || typeChar == '-' || 
		   typeChar == 's' || typeChar == '.' ||
		   typeChar == '^');
	    if (hexaPixmap[i].data_free != NULL) {
		return hexaPixmap[i].pixmap_free;
	    } else {
		return hexaPixmap[i].pixmap_normal;
	    }
	default:
	    assert(True==False);
	}
    }

    return XtUnspecifiedPixmap;
}

/*
 * local functions
 */
static void
createMenus(Widget form)
{
    static struct MenuEntry {
	Bool line;
	char *name;
	XtCallbackProc callback;
	Widget widget;
    } fileMenu[] = {
	{ False, "Open", NULL, },
	{ True, "filemenuline1", NULL, },
	{ False, "Exit", askExitProc, }
    }, optionMenu[] = {
	{ False, "Server", NULL, } 
    }, commandMenu[] = {
	{ False, "All", (XtCallbackProc)queryCommandAll, },
	{ False, "Break", (XtCallbackProc)breakCommand, } ,
	{ False, "Census", (XtCallbackProc)queryCommandCensus, },
	{ False, "Commodity", (XtCallbackProc)queryCommandCommodity, },
	{ False, "Deliver", (XtCallbackProc)queryCommandDeliver, },
	{ False, "Designate", (XtCallbackProc)queryCommandDesignate, },
	{ False, "Explore", (XtCallbackProc)queryCommandExplore, },
	{ False, "Level", (XtCallbackProc)queryCommandLevel, },
	{ False, "Map", (XtCallbackProc)queryCommandMap, },
	{ False, "Move", (XtCallbackProc)queryCommandMove, },
	{ False, "Nation", (XtCallbackProc)nationCommand, },
	{ False, "Produce", (XtCallbackProc)queryCommandProduce, },
	{ False, "Radar", (XtCallbackProc)queryCommandRadar, },
	{ False, "Resource", (XtCallbackProc)queryCommandResource, }
    }, helpMenu[] = {
	{ False, "Tutorial", NULL, },
	{ False, "On Version", NULL, }
    };
    static struct {
	char *title;
	char *menuName;
	int nrEntries;
	struct MenuEntry *entries;
    } menuButtons[] = {
	{ "File",    "filemenu",   XtNumber(fileMenu), fileMenu },
	{ "Options", "optionmenu", XtNumber(optionMenu), optionMenu },
	{ "Commands", "commandmenu", XtNumber(commandMenu), commandMenu },
	{ NULL, },
	{ "Help",    "helpmenu",   XtNumber(helpMenu), helpMenu }
    };
    Widget tmp;               /* for temporary used widget ids */
    int i;

    for (i=0; i<XtNumber(menuButtons); i++) {
	int j;

	tmp = XtVaCreateManagedWidget(
	    menuButtons[i].title,
	    menuButtonWidgetClass,
	    form,
	    XtNmenuName, menuButtons[i].menuName,
	    NULL);
	tmp = XtCreatePopupShell(
	    menuButtons[i].menuName,
	    simpleMenuWidgetClass,
	    tmp,
	    NULL,
	    0);

	for (j=0; j<menuButtons[i].nrEntries; j++) {
	    menuButtons[i].entries[j].widget  = XtVaCreateManagedWidget(
		menuButtons[i].entries[j].name,
		menuButtons[i].entries[j].line ? smeLineObjectClass 
		                               : smeBSBObjectClass,
		tmp,
		NULL);
	    if (menuButtons[i].entries[j].callback != NULL) {
		XtAddCallback(
		    menuButtons[i].entries[j].widget,
		    XtNcallback, menuButtons[i].entries[j].callback, 
		    NULL);
	    }
	}
    }

    /* DEPENDEND CODE */
    breakCommandWidget = commandMenu[1].widget;

    /* 
     *initialize menu actions 
     */
    /* File|Exit */
    exitShell = XtVaCreatePopupShell(
	"exitshell",
	transientShellWidgetClass,
	topLevel,
	NULL);

    tmp = XtVaCreateManagedWidget(
	"exitdialog",
	dialogWidgetClass,
	exitShell,
	NULL);

    XawDialogAddButton(
	tmp,
	"exityes",
	answerExitProc,
	(XtPointer)True);

    XawDialogAddButton(
	tmp,
	"exitno",
	answerExitProc,
	(XtPointer)False);

    XtRealizeWidget(exitShell);
}

static Bool
createInfoForm(Widget pane)
{
    int i;
    Dimension maxWidth = 0;
    Dimension width;
    Widget infoForm;
    int dist;

    infoForm = XtVaCreateManagedWidget(
	"infoform",
	formWidgetClass,
	pane,
	NULL);
    commandForm = XtVaCreateManagedWidget(
	"commandform",
	formWidgetClass,
	infoForm,
	NULL);
    for (i=0; i<XtNumber(commands); i++) {
	commands[i].widget = XtVaCreateManagedWidget(
	    commands[i].name,
	    commandWidgetClass,
	    commandForm,
	    NULL);
	XtVaGetValues(
	    commands[i].widget,
	    XtNwidth, &width,
	    NULL);
	maxWidth = max(width, maxWidth);
	if (commands[i].proc != NULL) {
	    XtAddCallback(commands[i].widget, 
			  XtNcallback, commands[i].proc, 
			  NULL);
	}
    }
    for (i=0; i<XtNumber(commands); i++) {
	XtVaSetValues(
	    commands[i].widget,
	    XtNwidth, maxWidth,
	    NULL);
    }
    XtVaGetValues(
	commandForm,
        XtNdefaultDistance, &dist,
        NULL);
    maxWidth = maxWidth * 3 + dist * 4;

    contentWidget = XtVaCreateManagedWidget(
	"contentwidget",
        contentWidgetClass,
        infoForm,
        XtNwidth, maxWidth,
        XtNworld, &empire,
        NULL);

    return True;
}

/* ARGSUSED */
static void
commandButtonAction(Widget w, XEvent *event, String *params,
		    Cardinal *numParams)
{
    void (*proc)(void);
    
    assert(*numParams==1);
    assert(params[0][0] && !params[0][1]);
    assert(isdigit(params[0][0]));

    proc = (void(*)(void))commands[(int)(params[0][0]-'1')].proc;

    /* call only if procedure is installed */
    if (proc) proc();
}

/* ARGSUSED */
static void
askExitProc(Widget widget, XtPointer closure, XtPointer callData)
{
    Position x, y;
    Dimension widthTop, heightTop;
    Dimension widthShell, heightShell;

    XRaiseWindow(XtDisplay(vertPane), XtWindow(vertPane));
    
    /* try to place the popup window reasonable */
    XtVaGetValues(
	topLevel,
	XtNwidth, &widthTop,
	XtNheight, &heightTop,
	NULL);

    XtVaGetValues(
	exitShell,
	XtNwidth, &widthShell,
	XtNheight, &heightShell,
	NULL);

    XtTranslateCoords(
	topLevel,
	(Position)(widthTop-widthShell)/2,
	(Position)(heightTop-heightShell)/2,
	&x, &y);

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

    /* ... and show up */
    XtPopup(exitShell, XtGrabNonexclusive);
}

/* ARGSUSED */
static void
answerExitProc(Widget widget, XtPointer closure, XtPointer callData)
{
    XtPopdown(exitShell);

    if ((Bool)closure) {
	saveGameData();
	exit(0);
    }
}

static void
mainEditAdd(Bool active, XawTextBlock *textBlock, Bool async)
{
    if (textBlock) {
	Widget w = async ? outputWidget : editWidget;
	
	XawTextPosition pos = XawTextGetInsertionPoint(w);

	XawTextReplace(
	    w,
	    pos,
	    pos,
	    textBlock);

	XawTextSetInsertionPoint(w, pos+textBlock->length);
    } else {
	XawTextDisplayCaret(editWidget, active);
	if (active) {
	    XtSetKeyboardFocus(vertPane, editWidget);
	}
    }
}

static void
freePixmaps(void)
{
    int i = -1;

    while (hexaPixmap[++i].typeChar) {
	XFreePixmap(XtDisplay(mapWidget), hexaPixmap[i].pixmap_normal);
	XFreePixmap(XtDisplay(mapWidget), hexaPixmap[i].pixmap_enemy);
	XFreePixmap(XtDisplay(mapWidget), hexaPixmap[i].pixmap_hilit);
	if (hexaPixmap[i].data_free) {
	    XFreePixmap(XtDisplay(mapWidget), hexaPixmap[i].pixmap_free);
	}
    }
/*    XFreePixmap(XtDisplay(menuLine), iconConnected);
    XFreePixmap(XtDisplay(menuLine), iconNotConnected); */
}

/*
 * 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:
 */
