/*
 *                            X D R A W . C
 *
 *  Drawing routines for the map widget.
 *
 *  Version      : $Revision: 1.7 $
 *
 *  Created      : Sat May 28 02:02:11 1994
 *  Author       : Ulrich Drepper <drepper@mydec>
 *
 *  Last modified: Sat Jul 16 23:08: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: xdraw.c,v 1.7 1994/07/17 22:23:06 drepper Exp $";
#endif /* lint */

#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/AsciiText.h>

#include "empire.h"
#include "Content.h"
#include "scheme.h"

/*
 * exported variables
 */
Bool gotMap = False;            /* be sure a map to draw has already arrived */

/*
 * local functions
 */
static void hilitMap(Bool doHilit);
static Bool coordScreenToArr(int x, int y, int *c, int *cr, int *r, int *rr);
static void rangeToEdit(int begX, int begY, int endX, int endY);

/*
 * exported functions
 */
void
exposeMap(Widget w, XtPointer ptr, XExposeEvent *ev, Boolean *cont)
{
    static Bool first = True;
    static Region region;

    if (first) {
	region = XCreateRegion();
	first = False;
    }

    XtAddExposureToRegion((XEvent*)ev, region);

    if (ev->count == 0) {
	XRectangle rectangle;

	/* last expose event */
	XClipBox(region, &rectangle);
	/* destroy old region */
	XDestroyRegion(region);
	/* create new region */
	region = XCreateRegion();

	/* paint the exposed rectangle */
	paintMap(w, &rectangle, mapGC);
    }
}

void
paintMap(Widget widget, XRectangle *rectangle, GC gc)
{
#define x rectangle->x
#define y rectangle->y
#define w rectangle->width
#define h rectangle->height
#define W HEXAPIXMAPWIDTH
#define H HEXATILEPIXMAPHEIGHT

    /* no work to be done */
    if (!XtIsRealized(widget)) return;

    if (!gotMap) {
	XClearArea(
	    XtDisplay(widget),
	    XtWindow(widget),
	    x, y,
	    w, h,
	    False);
	return;
    }
	
    {
	/*
	 * FIX ME: a better computation of these variables for a less
	 *         redundant redraw (-> quicker)
	 */
	int r=(y/H)-1;
	int c=(x-odd(r)*W/2)/W-2;
	int nc=((x-odd(r)*W/2)%W+w+(W-1))/W+3;
	int nr=(y-H*r+h+H-1)/H+1;
	int ld=0;
	int rd=0;

	int rc;

	for (rc=0; rc<nr; rc++) {
	    int ar = r+rc;

	    if (ar>=0 && ar<empire.yMapSize) {
		int cc;

		for (cc=(rc&1)*ld; cc<nc+(rc&1)*rd; cc++) {
		    int ac = c+cc;

		    if (ac>=0 && ac<empire.xMapSize/2) {
			paintHexa(widget, ac, ar, True);
		    }
		}
	    }
	}
    }
#undef x
#undef y
#undef w
#undef h
#undef W
#undef H
}

void
paintHexa(Widget widget, int c, int r, Bool checked)
{
    Pixmap pixmap = empire.map[r*empire.rowBytes+c].typePixmap;

    if ((!checked && !XtIsRealized(widget)) ||
	pixmap == XtUnspecifiedPixmap) {
	return;
    }

    XCopyArea(
	XtDisplay(widget),
	pixmap,
	XtWindow(widget),
	mapGC,
	0, 0,
	HEXAPIXMAPWIDTH+1, 
	HEXAPIXMAPHEIGHT+1,
	(odd(r)?HEXAPIXMAPWIDTH/2:0)+HEXAPIXMAPWIDTH*c, 
	HEXATILEPIXMAPHEIGHT*r);
}

void
buttonOnMap(Widget widget, XtPointer ptr, XEvent *ev, Boolean *cont)
{
#define draw \
	    XDrawRectangle(			\
		XtDisplay(mapWidget),		\
		XtWindow(mapWidget),		\
		mapXorGC,			\
		min(begX,endX), min(begY,endY),	\
		abs(begX-endX)+1, abs(begY-endY)+1)

    static Bool selectRange = False;
    static int begX, begY, endX, endY;
    int column, row;
    char buffer[10];

    switch (ev->type) {
    case ButtonPress:
	if (selectRange) return;

	switch (ev->xbutton.button) {
	case Button1:
	    if (coordScreenToArr(ev->xbutton.x, ev->xbutton.y,
				 &column, NULL, &row, NULL)) {
		XtVaSetValues(
		    contentWidget,
		    XtNsector, &empire.map[row*empire.rowBytes+column],
		    NULL);
		XClearArea(
		    XtDisplay(contentWidget),
		    XtWindow(contentWidget),
		    0, 0, 0, 0, True);
	    }
	    break;
	case Button2:
	    XGrabServer(XtDisplay(mapWidget));
	    selectRange = True;
	    begX = endX = ev->xbutton.x;
	    begY = endY = ev->xbutton.y;
	    draw;
	    break;
	}
	break;
    case ButtonRelease:
	switch (ev->xbutton.button) {
	case Button2:
	    draw;
	    XUngrabServer(XtDisplay(mapWidget));
	    selectRange = False;
	    rangeToEdit(begX, begY, ev->xbutton.x, ev->xbutton.y);
	    break;
	}
	break;
    case MotionNotify:
	if (selectRange) {
	    draw;
	}
	while (XCheckTypedEvent(XtDisplay(mapWidget),
				MotionNotify,
				ev));
	endX = ev->xmotion.x;
	endY = ev->xmotion.y;
        if (coordScreenToArr(endX, endY, &column, NULL, &row, NULL)) {
	    column = ARR2MAPX(column, row);
	    row = ARR2MAPY(column, row);
            if (column >= empire.xMapSize/2) column -= empire.xMapSize;
            if (row >= empire.yMapSize/2) row -= empire.yMapSize;
	    if (column != actMenuCoordC || row != actMenuCoordR) {
		actMenuCoordC = column;
		actMenuCoordR = row;
		sprintf(buffer, "%4d,%d", actMenuCoordC, actMenuCoordR);
		XtVaSetValues(
		    coordMenuLabel,
		    XtNlabel, buffer,
	            NULL);
	    }
	} else {
	    if (actMenuCoordC!=INT_MIN && actMenuCoordR!=INT_MIN) {
		actMenuCoordC = actMenuCoordR = INT_MIN;
		strcpy(buffer, "         ");
		XtVaSetValues(
		    coordMenuLabel,
	            XtNlabel, buffer,
	            NULL);
	    }
	}
	if (selectRange) {
	    draw;
	}
	break;
    case LeaveNotify:
	XtVaSetValues(
	    coordMenuLabel,
	    XtNlabel, "         ",
	    NULL);
	actMenuCoordC = actMenuCoordR = INT_MIN;
	break;
    }
}

#define HT (HEXAPIXMAPHEIGHT-HEXATILEPIXMAPHEIGHT)
void
redisplaySectors(Widget widget, int x1, int y1, int x2, int y2)
{
    int bx1, bx2, by1, by2;

    assert(x1<=x2 && y1<=y2);
    
    while (x1<-empire.xMapSize/2) {
	x1 += empire.xMapSize;
	x2 += empire.xMapSize;
    }
    while (x1>=empire.xMapSize/2) {
	x1 -= empire.xMapSize;
	x2 -= empire.xMapSize;
    }
    if (x2-x1+1 >= empire.xMapSize) {
	x1 = -empire.xMapSize/2;
	x2 =  empire.xMapSize/2-1;
    }
    
    x2 = (x2+empire.xMapSize/2)%empire.xMapSize-empire.xMapSize/2;
    if (x2 < x1) {
	x1 = -empire.xMapSize/2;
	x2 =  empire.xMapSize/2-1;
    }
    
    while (y1<-empire.yMapSize/2) {
	y1 += empire.yMapSize;
	y2 += empire.yMapSize;
    }
    while (y1>=empire.yMapSize/2) {
	y1 -= empire.yMapSize;
	y2 -= empire.yMapSize;
    }
    if (y2-y1+1 >= empire.yMapSize) {
	y1 = -empire.yMapSize/2;
	y2 =  empire.yMapSize/2-1;
    }
    
    y2 = (y2+empire.yMapSize/2)%empire.yMapSize-empire.yMapSize/2;
    if (y2 < y1) {
	y1 = -empire.yMapSize/2;
	y2 =  empire.yMapSize/2-1;
    }

    x1 += empire.xMapSize/2;
    x2 += empire.xMapSize/2;
    y1 += empire.yMapSize/2;
    y2 += empire.yMapSize/2;

    assert(x1>=0 && x1<empire.xMapSize);
    assert(x2>=0 && x2<empire.xMapSize);
    assert(y1>=0 && y1<empire.yMapSize);
    assert(y2>=0 && y2<empire.yMapSize);
    assert(x1<=x2);
    assert(y1<=y2);

    bx1 = (x1*HEXAPIXMAPWIDTH)/2;
    by1 = y1*HEXATILEPIXMAPHEIGHT;

    bx2 = (x2*HEXAPIXMAPWIDTH)/2+HEXAPIXMAPWIDTH-1;
    by2 = y2*HEXATILEPIXMAPHEIGHT+HEXATILEPIXMAPHEIGHT-1+HT;
    
    XClearArea(
	XtDisplay(widget),
	XtWindow(widget),
        bx1, by1,
        bx2-bx1+1, by2-by1+1,
	True);
}

void
showMessageLine(void)
{
    char outBuf[90];

    sprintf(outBuf, "[%d:%d] %s",
	    restMin, restBTU, messageStr);
    XtVaSetValues(messageText, XtNlabel, outBuf, NULL);
}

/*
 * local functions
 */
static void
hilitMap(Bool doHilit)
{
    Scheme_Object *arg[1] = { doHilit ? scheme_true : scheme_false };
    int rc;
    int cc;

    scheme_hilit(1, arg);
}

static Bool
coordScreenToArr(int x, int y, int *c, int *cr, int *r, int *rr)
{
    int dummy1;
    int dummy2;

    if (cr == NULL) cr = &dummy1;
    if (rr == NULL) rr = &dummy2;

    *r  = y / HEXATILEPIXMAPHEIGHT;
    *rr = y % HEXATILEPIXMAPHEIGHT;

    if ((*r&1) && (x-=HEXAPIXMAPWIDTH/2) < 0) {
	*r = *rr = *c = *cr = -1;
	return False;
    }

    *c  = x / HEXAPIXMAPWIDTH;
    *cr = x % HEXAPIXMAPWIDTH;

    if (*rr > HT) {
	if (*r >= empire.yMapSize) {
	    *r = *rr = *c = *cr = -1;
	    return False;
	}
    } else {
	if (HEXAPIXMAPWIDTH/2*((*rr)-HT)+HT*(*cr) < 0) {
	    if (even(*r)) {
		(*c)--;
		*cr += HEXAPIXMAPWIDTH/2;
	    }
	    (*r)--;
	    *rr += HEXATILEPIXMAPHEIGHT;
	} else if (HEXAPIXMAPWIDTH/2*((*rr)+HT)-HT*(*cr) < 0) {
	    if (odd(*r)) {
		(*c)++;
		*cr -= HEXAPIXMAPWIDTH/2;
	    }
	    (*r)--;
	    *rr += HEXATILEPIXMAPHEIGHT;
	}
    }

    if (*c >= empire.xMapSize/2) {
	*r = *rr = *c = *cr = -1;
	return False;
    }

    if (*r < 0 || *r >=empire.yMapSize || *c < 0 || *c >= empire.xMapSize/2) {
	*r = *rr = *c = *cr = -1;
	return False;
    }

    return True;
#undef HT
}

static void
rangeToEdit(int begX, int begY, int endX, int endY)
{
    char buffer[20];
    char *cp = buffer;
#if XtSpecificationRelease<6 || defined(UseXaw3d)
    XawTextBlock textBlock = { 0, 0, buffer, FMT8BIT };
#else
    XawTextBlock textBlock = { 0, 0, buffer, XawFmt8Bit };
#endif
    int minX, maxX;
    int minY, maxY;
    int luX, luY;   /* left upper corner */
    int llX, llY;   /* left lower corner */
    int ruX, ruY;   /* right upper corner */
    int rlX, rlY;   /* right lower corner */

    if (!coordScreenToArr(begX, begY, &luX, NULL, &luY, NULL) ||
	!coordScreenToArr(begX, endY, &llX, NULL, &llY, NULL) ||
	!coordScreenToArr(endX, begY, &ruX, NULL, &ruY, NULL) ||
	!coordScreenToArr(endX, endY, &rlX, NULL, &rlY, NULL)) return;
    
    luX = ARR2MAPX(luX, luY);
    luY = ARR2MAPY(luX, luY);
    if (luX >= empire.xMapSize/2) luX -= empire.xMapSize;
    if (luY >= empire.yMapSize/2) luY -= empire.yMapSize;
    minX = maxX = luX;
    minY = maxY = luY;
    
    llX = ARR2MAPX(llX, llY);
    llY = ARR2MAPY(llX, llY);
    if (llX >= empire.xMapSize/2) llX -= empire.xMapSize;
    if (llY >= empire.yMapSize/2) llY -= empire.yMapSize;
    minX = min(minX, llX);
    maxX = max(maxX, llX);
    minY = min(minY, llY);
    maxY = max(maxY, llY);
    
    ruX = ARR2MAPX(ruX, ruY);
    ruY = ARR2MAPY(ruX, ruY);
    if (ruX >= empire.xMapSize/2) ruX -= empire.xMapSize;
    if (ruY >= empire.yMapSize/2) ruY -= empire.yMapSize;
    minX = min(minX, ruX);
    maxX = max(maxX, ruX);
    minY = min(minY, ruY);
    maxY = max(maxY, ruY);

    rlX = ARR2MAPX(rlX, rlY);
    rlY = ARR2MAPY(rlX, rlY);
    if (rlX >= empire.xMapSize/2) rlX -= empire.xMapSize;
    if (rlY >= empire.yMapSize/2) rlY -= empire.yMapSize;
    minX = min(minX, rlX);
    maxX = max(maxX, rlX);
    minY = min(minY, rlY);
    maxY = max(maxY, rlY);

    minX = max(-empire.xMapSize/2,minX);
    maxX = min(empire.xMapSize/2-1,maxX);
    minY = max(-empire.yMapSize/2,minY);
    maxY = min(empire.yMapSize/2-1,maxY);
    
    if (minX != maxX) {
	sprintf(cp, "%d:%d,", minX, maxX);
    } else {
	sprintf(cp, "%d,", minX);
    }

    cp += strlen(cp);

    if (minY != maxY) {
	sprintf(cp, "%d:%d", minY, maxY);
    } else {
	sprintf(cp, "%d", minY);
    }

    textBlock.length = strlen(textBlock.ptr);

    currentEditAdd(True, &textBlock, False);
}

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