/*****************************************************************************/
/*									     */
/*									     */
/*	X patience - layout.c						     */
/*									     */
/*	written by Heiko Eissfeldt and Michael Bischoff			     */
/*									     */
/*	24-Feb-1993: First release (0.1)				     */
/*									     */
/*									     */
/*****************************************************************************/
#include "xpat.h"
#include "xpatx11.h"

#define YOFF			(graphic.yoff)


int arrange_buttons(struct singlebutton *b, int *startx, int *starty,
    XSize_t maxx, long *magic)
{   int x, y, num;
    long mask;
    x = *startx;
    y = *starty;
    num = 0;
    *magic = 0;
    mask = 1L;
    while (b->text) {		/* weiterer button vorhanden */
	++num;
	b->x = x;
	b->y = y;
	b->w = 2 * button.bx + XTextWidth(button.font, b->text, strlen(b->text));
	b->h = 2 * button.by + button.fontheight;

	while (x > graphic.xgap && x + b->w + graphic.xgap >= maxx) {
	    /* line is full, next line */
	    *magic |= mask;
	    x = *startx;
	    y += 2 * button.by + button.fontheight + graphic.ygap;
	    b->x = x;
	    b->y = y;
	}
	mask <<= 1;
	x += b->w + graphic.xgap;
	++b;
    }
    *startx = x;
    *starty = y + 2 * button.by + button.fontheight + graphic.ygap;
    return num;
}




static int calc_yoff(int w)
{   long dummy;
    int x, y;
    x = graphic.xgap;
    y = graphic.ygap;
    button.num = arrange_buttons(button.b, &x, &y, w, &dummy);
    return y + 2 * TABLE_BW + message_font->ascent + message_font->descent;
}

/* nasty function to get the number of cols needed for small layout */

static int getsmallx(void)
{
    if (rules.numstacks > rules.numslots)
	return rules.numstacks;
    else if (rules.numstacks == rules.numslots)
	return rules.numstacks + (rules.variant & DECK_SOURCE ? 1 : 0);
    else  /* stacks < slots */ if (rules.variant & DECK_SOURCE)
	return max(rules.numstacks+2, rules.numslots);
    else
	return rules.numslots;
}

void generic_minwindow(XSize_t *x, XSize_t *y)
{	
    int min1;
    XSize_t x1, y1, x2, y2;
    
    min1 = getsmallx();
    x1 = graphic.xgap * (min1+1) + CARD_WIDTH * min1;
    y1 = calc_yoff(x1) + (CARD_HEIGHT + graphic.ygap) * 3;
    
    min1 = rules.numslots + rules.numdecks;
    if (rules.variant & DECK_SOURCE)
	++min1;
    x2 = graphic.xgap * (min1+1) + CARD_WIDTH * min1;
    y2 = calc_yoff(x2) + (CARD_HEIGHT + graphic.ygap) * 4;
    
    /* as a selection rule, minimize the size of the window needed */
    if (x1 * y1 < x2 * y2) {	/* stacks on the top */
	*x = x1;
	*y = y1;
    } else {	/* stacks on the right side */
	*x = x2;
	*y = y2;
    }
}

void FreeCell_minwindow(XSize_t *x, XSize_t *y)
{	
    int min1;
    
    min1 = max(rules.numslots, rules.numstacks + rules.numtmps);
    *x = graphic.xgap * (min1+1) + CARD_WIDTH * min1;
    *y = calc_yoff(*x) + (CARD_HEIGHT + graphic.ygap) * 3;
}

int FreeCell_layout(int xnum, int ynum, int h, int y, int bottomgap)
{   int i, cols, off;
    struct pile *p = graphic.pile;
    int new_layout_type;

    cols = max(rules.numslots, rules.numstacks + rules.numtmps);
    new_layout_type = 2;

    off = cols - rules.numstacks;
    for (i = 0; i < rules.numstacks; ++i) {
	p->x = graphic.xgap + (graphic.xgap + CARD_WIDTH) * (i+off);
	p->y = YOFF;
	p->totalheight = CARD_HEIGHT;
	p->maxheight = CARD_HEIGHT;
	++p;
    }
    off = cols - rules.numslots;
    for (i = 0; i < rules.numslots; ++i) {
	p->x = graphic.xgap + (graphic.xgap + CARD_WIDTH) * (i+off);
	p->y = YOFF + graphic.ygap + CARD_HEIGHT;
	p->totalheight = CARD_HEIGHT;
	p->maxheight = h - CARD_HEIGHT - 2 * graphic.ygap - bottomgap;
	++p;
    }
    off = 0;
    for (i = 0; i < rules.numtmps; ++i) {
	p->x = graphic.xgap + (graphic.xgap + CARD_WIDTH) * (i+off);
	p->y = YOFF;
	p->totalheight = CARD_HEIGHT;
	p->maxheight = CARD_HEIGHT;
	++p;
    }
    p->x = p->y = -1;
    return new_layout_type;
}


static int gen_layout(int xnum, int ynum, int h, int y, int bottomgap)
{   int i, cols, off;
    struct pile *p = graphic.pile;
    int new_layout_type;

    /* try to do big layout */
    cols = rules.numslots + rules.numdecks;
    if (rules.variant & DECK_SOURCE)
	++cols;

    if (xnum >= cols && ynum >= 4) {
	/* big layout is OK */
	new_layout_type = 0;
	cols -= rules.numdecks;
	if (xnum >= rules.numslots + rules.numdecks + 1) {
	    /* big layout with deck OK */
	    cols = rules.numslots + 1;
	    ++new_layout_type;
	}
	for (i = 0; i < rules.numstacks; ++i) {
	    p->x = graphic.xgap + (graphic.xgap + CARD_WIDTH) * (cols + i/4);
	    p->y = YOFF + SUIT(i) * (CARD_HEIGHT+graphic.ygap);
	    p->totalheight = CARD_HEIGHT;
	    p->maxheight = CARD_HEIGHT;
	    ++p;
	}
	off = cols - rules.numslots;
	for (i = 0; i < rules.numslots; ++i) {
	    p->x = graphic.xgap + (graphic.xgap + CARD_WIDTH) * (off+i);
	    p->y = YOFF;
	    p->totalheight = CARD_HEIGHT;
	    p->maxheight = h - graphic.ygap - bottomgap;
	    ++p;
	}
    } else {
	/* small layout */
	if (rules.numstacks > rules.numslots) {
	    new_layout_type = 2;
	    cols = rules.numstacks;
	} else if (rules.numstacks == rules.numslots) {
	    if (rules.variant & DECK_SOURCE) {
		new_layout_type = 5;
		cols = rules.numstacks + 1;
	    } else {
		new_layout_type = 4;	/* only case without a deck */
		cols = rules.numstacks;
		if (xnum >= cols + 1) {	/* have a deck anyway */
		    ++cols;
		    ++new_layout_type;	
		}
	    }
	} else {
	    new_layout_type = 6;
	    if (rules.variant & DECK_SOURCE)
		cols = max(rules.numstacks + 2, rules.numslots);
	    else
		cols = rules.numslots;
	}

	off = cols - rules.numstacks;
	for (i = 0; i < rules.numstacks; ++i) {
	    p->x = graphic.xgap + (graphic.xgap + CARD_WIDTH) * (i+off);
	    p->y = YOFF;
	    p->totalheight = CARD_HEIGHT;
	    p->maxheight = CARD_HEIGHT;
	    ++p;
	}
	off = cols - rules.numslots;
	for (i = 0; i < rules.numslots; ++i) {
	    p->x = graphic.xgap + (graphic.xgap + CARD_WIDTH) * (i+off);
	    p->y = YOFF + graphic.ygap + CARD_HEIGHT;
	    p->totalheight = CARD_HEIGHT;
	    p->maxheight = h - CARD_HEIGHT - 2 * graphic.ygap - bottomgap;
	    ++p;
	}
    }
    /* fine. now do the deck(s) (if any) */
    /* p is pointer to first deck pile */

    p->totalheight = CARD_HEIGHT;
    p->maxheight = CARD_HEIGHT;
    switch (new_layout_type) {
    case 2: /* case 3: */
	p->x = graphic.xgap;
	p->y = YOFF + graphic.ygap + CARD_HEIGHT;
	break;
    case 0:	case 4:
	p->x = p->y = -1;	/* no deck */
	break;
    case 1: case 5:
	p->x = graphic.xgap;
	p->y = YOFF + (graphic.ygap + CARD_HEIGHT) / 2;
	break;
    case 6: /* case 7: */
	p->x = graphic.xgap;
	p->y = YOFF;
	break;
    default:
	abort();
    }
    /* do a second deck? */
    if (rules.variant & DECK_SOURCE) {
	p[1] = p[0];
	if (new_layout_type == 6)
	    p->x += graphic.xgap + CARD_WIDTH;
	else
	    p->y += graphic.ygap + CARD_HEIGHT;
    }
    return new_layout_type;
}

void init_layout(void)
{   int w, h, x, y;
    static int layout_type = -1, yoff = -1;
    static long magic = -1;
    int new_layout_type;
    long new_magic;
    int bottomgap;
    int xnum, ynum;	/* number of cards in either direction */

    /* first, decide where to put the buttons */
    w = graphic.width;
    h = graphic.height;
    x = graphic.xgap;
    y = graphic.ygap;
    button.num = arrange_buttons(button.b, &x, &y, w, &new_magic);
    YOFF = y;
    y += 2 * TABLE_BW + message_font->ascent + message_font->descent;
    /* y is used space in vertical direction */

    /* bottomgap specifies how much place to leave for the last card */
    bottomgap = 2 * TABLE_BW + message_font->ascent + message_font->descent;
    bottomgap += CARD_HEIGHT / 3;

    xnum = (w - graphic.xgap) / (CARD_WIDTH+graphic.xgap);
    ynum = (h - y) / (CARD_HEIGHT+graphic.ygap);

    new_layout_type = 
	rules.layout ? (*rules.layout)(xnum, ynum, h, y, bottomgap) :
	    gen_layout(xnum, ynum, h, y, bottomgap);

    /* printf("deck %d position: (%d,%d), first slot at (%d,%d)\n",
	p - graphic.pile, p->x, p->y,
	graphic.pile[rules.numstacks].x, graphic.pile[rules.numstacks].y);
    printf("cols = %d, off = %d\n", cols, off);
	printf("xxdeck at pos %d,%d\n", graphic.pile[DECK].x, graphic.pile[DECK].y); */
    if (new_layout_type != layout_type || YOFF != yoff) {	/* everything changed */
	layout_type = new_layout_type;
	yoff = YOFF;
	magic = new_magic;
	XClearArea(dpy, table, 0, 0, 0, 0, True);
    } else if (magic != new_magic) {		/* at least one button did move */
	magic = new_magic;
	XClearArea(dpy, table, 0, 0, 0, YOFF-2, True);	/* just redraw the buttons */
    }
}

int pile_resize(int i)
{   int j, delta;
    struct pile *p;

    p = graphic.pile + i;
    p->totalheight = CARD_HEIGHT;
    if (game.piletype[i] != Slot || EMPTY(i))
	return 0;
    j = game.ind[i];
    if (game.ind[i+1] - j - 1 == 0)
	delta = STD_DELTA;
    else
	delta = (p->maxheight-CARD_HEIGHT/2) / (game.ind[i+1] - j - 1);
    /* printf("maxh=%d, numcards=%d, delta1=%d\n", p->maxheight, game.ind[i+1]-j-1, delta); */
    if (delta > STD_DELTA)          /* respect maximum value */
	delta = STD_DELTA;
    if (delta < 2)                  /* respect minimum value */
        delta = 2;
    graphic.cardy[j] = 0;
    while (j != INDEX_OF_LAST_CARD(i)) {	/* not topmost card */
	p->totalheight += delta;
	graphic.cardy[j+1] = graphic.cardy[j] + delta;
	++j;
    }
    j = graphic.pile[i].delta != delta;	/* change of shrink factor? */
    graphic.pile[i].delta = delta;
    return j;
}
