/*
# GENERIC X-BASED TETRIS (altered)
#
#	utils.c
#
###
#
#  Copyright (c) 1993 - 2001		David A. Bagley, bagleyd@tux.org
#
#  Taken from GENERIC X-BASED TETRIS
#
#  Copyright (c) 1992 - 1995		Q. Alex Zhao, azhao@cc.gatech.edu
#
#			All Rights Reserved
#
#  Permission to use, copy, modify, and distribute this software and
#  its documentation for any purpose and without fee is hereby granted,
#  provided that the above copyright notice appear in all copies and
#  that both that copyright notice and this permission notice appear in
#  supporting documentation, and that the name of the author not be
#  used in advertising or publicity pertaining to distribution of the
#  software without specific, written prior permission.
#
#  This program is distributed in the hope that it will be "playable",
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
#
#  create_images() Copyright (c) 2000 by Stephen Montgomery-Smith
*/

#include	"tetris.h"
#include	"polyomino.h"

#include	"ticon.xbm"

#define BITMAPS 256

Atom            delw;

static GC       revGC, bigGC, tinyGC, xorGC;
static GC       thingGCs[MAX_START_POLYOMINOES];

static char    *winName = "GENERIC TETRIS";
static char    *iconName = "TETRIS";

static int      titleLen, titleWidth, authorLen, authorWidth;
static int      titleX, titleY, authorX, authorY;
static int      sX;
static int      sLevelY, sRowsY, sScoreY;

static int      topRWidth, topWidth, topHeight, topMidX, topMidY;
static int      frameX, frameY, frameW, frameH;

static XImage *images[2][BITMAPS];

typedef struct {
    int             squares, polyomino_number;
    int             xpos, ypos;
    int             size, color_number;
    int             bonus, random_rotation, random_reflection;
    long            random_number;
}               thing_t;
typedef struct {
    int             rotation;
    int             reflection;
    int             start_height;
    int             shape[MAX_SQUARES][MAX_SQUARES];
    int             size;
}               Polyominoes;
typedef struct {
    int             number[MAX_MODES][MAX_TYPES];
    int             start[MAX_START_POLYOMINOES][MAX_TYPES];
    int             turn_style;
}               Mode;
typedef struct {
    Polyominoes     polyomino[MAX_POLYOMINOES];
    Mode            mode[MAX_MODES];
    int             diagonal_switch;
}               Polytris;
static Polytris tris[MAX_SQUARES - MIN_SQUARES + 1];

static char    *thingFGs[MAX_START_POLYOMINOES] = {
    "Red", "Green", "Blue", "Yellow", 
    "Magenta", "Cyan", "FireBrick", "GreenYellow",
    "SlateBlue", "Khaki", "Plum", "Violet",
    "DarkTurquoise", "Gold", "Orchid", "Turquoise",
    "Orange", "OrangeRed", "VioletRed", "BlueViolet",
    "SeaGreen", "Pink", "ForestGreen", "SkyBlue",
    "Coral", "Wheat", "Goldenrod", "IndianRed",
    "SpringGreen", "CornflowerBlue", "Thistle", "Aquamarine",
    "CadetBlue", "LightSteelBlue", "NavyBlue", "SteelBlue",
    "YellowGreen", "DarkViolet", "MediumSeaGreen", "DarkSlateGray",
    "LightGray", "MediumVioletRed", "Sienna", "MediumAquamarine",
    "MediumBlue", "Navy", "DarkOliveGreen", "DarkGreen",
    "DimGray", "Tan", "MediumTurquoise", "DarkSlateBlue",
    "Maroon", "Gray", "Black"
};

static thing_t  curThing, nextThing;
static struct {
    int         pmid, cid;
}               field[ROWS][COLS];

#define ARR(i,j,t) (((i)<0||(i)>=t.size||\
(j)<0||(j)>=t.size)?-2:\
(tris[t.squares].polyomino[t.polyomino_number].shape[j][i]>0))

#define ROUND8(n) ((((n)+7)/8)*8)

/* Defines to index the bitmaps.  A set bit indicates that an edge or
   corner is required. */
#define LEFT 1 /* (1<<0) */
#define DOWN 2 /* (1<<1) */
#define RIGHT 4 /* (1<<2) */
#define UP 8 /* (1<<3) */
#define LEFT_DOWN 16 /* (1<<4) */
#define RIGHT_DOWN 32 /* (1<<5) */
#define RIGHT_UP 64 /* (1<<6) */
#define LEFT_UP 128 /* (1<<7) */
#define IS_LEFT(n) ((n)&LEFT)
#define IS_DOWN(n) ((n)&DOWN)
#define IS_RIGHT(n) ((n)&RIGHT)
#define IS_UP(n) ((n)&UP)
#define IS_LEFT_DOWN(n) ((n)&LEFT_DOWN)
#define IS_RIGHT_DOWN(n) ((n)&RIGHT_DOWN)
#define IS_RIGHT_UP(n) ((n)&RIGHT_UP)
#define IS_LEFT_UP(n) ((n)&LEFT_UP)

#define CHECKDOWN(x) ((x)|DOWN)
#define CHECKUP(x) ((x)|UP)

/* Defines to access the bitmaps. */
#define BITNO(x,y) ((x)+(y)*ROUND8(boxsize))
#define SETBIT(x,y) {data[BITNO(x,y)/8] |= 1<<(BITNO(x,y)%8);}
#define RESBIT(x,y) {data[BITNO(x,y)/8] &= ~(1<<(BITNO(x,y)%8));}
#define TWOTHIRDSBIT(x,y) {if ((x+y+2)%3) SETBIT(x,y) else RESBIT(x,y)}
#define HALFBIT(x,y) {if ((x+y)%2) SETBIT(x,y) else RESBIT(x,y)}
#define THIRDBIT(x,y) {if (!((x+y+2)%3)) SETBIT(x,y) else RESBIT(x,y)}
#define THREEQUARTERSBIT(x,y) \
  {if ((y%2)||((x+2+y/2+1)%2)) SETBIT(x,y) else RESBIT(x,y)}
#define NOTHALFBIT(x,y) {if ((x+y)%2) RESBIT(x,y) else SETBIT(x,y)}

/* Parameters for bitmaps. */
#define G ((boxsize/45)+1)         /* 1/2 of gap between pentominoes. */
#define T ((boxsize<=12)?1:(G*2))  /* Thickness of walls of pentominoes. */
#define R ((boxsize<=12)?1:(G*6))  /* Amount of rounding. */
#define RT ((boxsize<=12)?1:(G*3)) /* Thickness of wall of rounded parts.
                                  Here 3 is an approximation to 2 sqrt(2).  */
#define RR 0   /* Roof ridge thickness */

/* A list of those bitmaps we need to create to display any polyomino. */
static int images_needed[] =
{
/* This needed for mononimo*/
 LEFT|RIGHT|UP|DOWN|LEFT_UP|LEFT_DOWN|RIGHT_UP|RIGHT_DOWN,

 LEFT_UP|LEFT_DOWN|RIGHT_UP|RIGHT_DOWN,

 LEFT|RIGHT_UP|RIGHT_DOWN,
 RIGHT|LEFT_UP|LEFT_DOWN,
 UP|LEFT_DOWN|RIGHT_DOWN,
 DOWN|LEFT_UP|RIGHT_UP,
 LEFT|RIGHT_UP,
 RIGHT|LEFT_UP,
 UP|LEFT_DOWN,
 DOWN|LEFT_UP,
 LEFT|RIGHT_DOWN,
 RIGHT|LEFT_DOWN,
 UP|RIGHT_DOWN,
 DOWN|RIGHT_UP,


/* These needed for hexonimoes*/
 LEFT,
 RIGHT,
 UP,
 DOWN,
 LEFT_DOWN|RIGHT_UP|RIGHT_DOWN,
 LEFT_UP|RIGHT_UP|RIGHT_DOWN,
 LEFT_UP|LEFT_DOWN|RIGHT_DOWN,
 LEFT_UP|LEFT_DOWN|RIGHT_UP,
 

 LEFT|UP|RIGHT_DOWN,
 LEFT|DOWN|RIGHT_UP,
 RIGHT|UP|LEFT_DOWN,
 RIGHT|DOWN|LEFT_UP,
 LEFT|UP,
 LEFT|DOWN,
 RIGHT|UP,
 RIGHT|DOWN,

 LEFT|RIGHT,
 UP|DOWN,

 RIGHT|UP|DOWN,
 LEFT|UP|DOWN,
 LEFT|RIGHT|DOWN,
 LEFT|RIGHT|UP,

 -1
};

static int      readInt();
static void     readPolyominoes();

/* ------------------------------------------------------------------ */

static unsigned long
getColor(name)
    char            name[];
{
    XColor          tmp;

    if (!useColor)
	return fg;
    if (XParseColor(display, colormap, name, &tmp) == 0) {
	(void) fprintf(stderr, "Tetris: invalid color '%s'.\n", name);
	return fg;
    }
    if (XAllocColor(display, colormap, &tmp) == 0) {
	(void) fprintf(stderr, "Tetris: can't allocate color '%s'.\n", name);
	return fg;
    }
    return tmp.pixel;
}

/* ------------------------------------------------------------------ */

static Bool
image_needed(n)
    int n;
{
    int i;

    for (i = 0; images_needed[i] != -1; i++)
	if (n == images_needed[i])
	    return True;
    return False;
}

/* ------------------------------------------------------------------ */

static Bool
create_an_image(n, use3D, next)
    int n;
    Bool use3D;
    int next;
{
    unsigned int x, y, boxsize;
    unsigned char * data;

  
    boxsize = (next) ? NEXTBOXSIZE : BOXSIZE;

    if ((data = (unsigned char *) malloc(sizeof(char)*(boxsize*ROUND8(boxsize)/8))) == NULL) {
	return False;  /* no biggie, will not use images */
    }
    for (y=0;y<boxsize;y++) for (x=0;x<boxsize;x++) {
	if (!use3D) {
#ifdef SMALL_BELLYBUTTON
	    if (x >= boxsize / 2 && x <= boxsize / 2 + 1 &&
		    y >= boxsize / 2 && y <= boxsize / 2 + 1)
		NOTHALFBIT(x,y)
	    else
#endif
	    HALFBIT(x,y)
	} else if ((x>=y && x<=boxsize-y-1 && IS_UP(n))
		|| (x<=y && x<=boxsize-y-1 && y<boxsize/2 && !IS_LEFT(n))
		|| (x>=y && x>=boxsize-y-1 && y<boxsize/2 && !IS_RIGHT(n)))
	    SETBIT(x,y)
	else if ((x<=y && x<=boxsize-y-1 && IS_LEFT(n))
		|| (x>=y && x<=boxsize-y-1 && x<boxsize/2 && !IS_UP(n))
		|| (x<=y && x>=boxsize-y-1 && x<boxsize/2 && !IS_DOWN(n)))
	    TWOTHIRDSBIT(x,y)
	else if ((x>=y && x>=boxsize-y-1 && IS_RIGHT(n))
		|| (x>=y && x<=boxsize-y-1 && x>=boxsize/2 && !IS_UP(n))
		|| (x<=y && x>=boxsize-y-1 && x>=boxsize/2 && !IS_DOWN(n)))
	    HALFBIT(x,y)
	else if ((x<=y && x>=boxsize-y-1 && IS_DOWN(n))
		|| (x<=y && x<=boxsize-y-1 && y>=boxsize/2 && !IS_LEFT(n))
		|| (x>=y && x>=boxsize-y-1 && y>=boxsize/2 && !IS_RIGHT(n)))
	    THIRDBIT(x,y)
    }

    if (IS_LEFT(n))
        for (y=0;y<boxsize;y++) for (x=G;x<G+T;x++)
          SETBIT(x,y)
    if (IS_RIGHT(n))
        for (y=0;y<boxsize;y++) for (x=G;x<G+T;x++)
          SETBIT(boxsize-1-x,y)
    if (IS_UP(n))
        for (x=0;x<boxsize;x++) for (y=G;y<G+T;y++)
          SETBIT(x,y)
    if (IS_DOWN(n))
        for (x=0;x<boxsize;x++) for (y=G;y<G+T;y++)
          SETBIT(x,boxsize-1-y)
    if (IS_LEFT(n))
        for (y=0;y<boxsize;y++) for (x=0;x<G;x++)
          RESBIT(x,y)
    if (IS_RIGHT(n))
        for (y=0;y<boxsize;y++) for (x=0;x<G;x++)
          RESBIT(boxsize-1-x,y)
    if (IS_UP(n))
        for (x=0;x<boxsize;x++) for (y=0;y<G;y++)
          RESBIT(x,y)
    if (IS_DOWN(n))
        for (x=0;x<boxsize;x++) for (y=0;y<G;y++)
          RESBIT(x,boxsize-1-y)

    if (IS_LEFT(n) && IS_UP(n))
        for (x=G;x<=G+R;x++)
          for (y=G;y<=R+2*G-x;y++) {
            if (x+y>R+2*G-RT)
              SETBIT(x,y)
            else
              RESBIT(x,y)
          }
    if (IS_LEFT(n) && IS_DOWN(n))
        for (x=G;x<=G+R;x++)
          for (y=G;y<=R+2*G-x;y++) {
            if (x+y>R+2*G-RT)
              SETBIT(x,boxsize-1-y)
            else
              RESBIT(x,boxsize-1-y)
          }
    if (IS_RIGHT(n) && IS_UP(n))
        for (x=G;x<=G+R;x++)
          for (y=G;y<=R+2*G-x;y++) {
            if (x+y>R+2*G-RT)
              SETBIT(boxsize-1-x,y)
            else
              RESBIT(boxsize-1-x,y)
          }
    if (IS_RIGHT(n) && IS_DOWN(n))
        for (x=G;x<=G+R;x++)
          for (y=G;y<=R+2*G-x;y++) {
            if (x+y>R+2*G-RT)
              SETBIT(boxsize-1-x,boxsize-1-y)
            else
              RESBIT(boxsize-1-x,boxsize-1-y)
          }

    if (!IS_LEFT(n) && !IS_UP(n) && IS_LEFT_UP(n)) {
        for (x=0;x<G;x++) for(y=0;y<G;y++)
          RESBIT(x,y)
        for (x=G;x<G+T;x++) for(y=0;y<G;y++)
          SETBIT(x,y)
        for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
          SETBIT(x,y)
    }
    if (!IS_LEFT(n) && !IS_DOWN(n) && IS_LEFT_DOWN(n)) {
        for (x=0;x<G;x++) for(y=0;y<G;y++)
          RESBIT(x,boxsize-1-y)
        for (x=G;x<G+T;x++) for(y=0;y<G;y++)
          SETBIT(x,boxsize-1-y)
        for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
          SETBIT(x,boxsize-1-y)
    }
    if (!IS_RIGHT(n) && !IS_UP(n) && IS_RIGHT_UP(n)) {
        for (x=0;x<G;x++) for(y=0;y<G;y++)
          RESBIT(boxsize-1-x,y)
        for (x=G;x<G+T;x++) for(y=0;y<G;y++)
          SETBIT(boxsize-1-x,y)
        for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
          SETBIT(boxsize-1-x,y)
    }
    if (!IS_RIGHT(n) && !IS_DOWN(n) && IS_RIGHT_DOWN(n)) {
        for (x=0;x<G;x++) for(y=0;y<G;y++)
          RESBIT(boxsize-1-x,boxsize-1-y)
        for (x=G;x<G+T;x++) for(y=0;y<G;y++)
          SETBIT(boxsize-1-x,boxsize-1-y)
        for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
          SETBIT(boxsize-1-x,boxsize-1-y)
    }

#ifdef LARGE_BELLYBUTTON
    if (!use3D) {
        if (!IS_LEFT(n) && !IS_UP(n) && !IS_LEFT_UP(n))
          for (x=0;x<G+T;x++) for(y=0;y<G+T;y++)
            SETBIT(x,y)
        if (!IS_LEFT(n) && !IS_DOWN(n) && !IS_LEFT_DOWN(n))
          for (x=0;x<G+T;x++) for(y=boxsize-G-T;y<boxsize;y++)
            SETBIT(x,y)
        if (!IS_RIGHT(n) && !IS_UP(n) && !IS_RIGHT_UP(n))
          for (x=boxsize-G-T;x<boxsize;x++) for(y=0;y<G+T;y++)
            SETBIT(x,y)
        if (!IS_RIGHT(n) && !IS_DOWN(n) && !IS_RIGHT_DOWN(n))
          for (x=boxsize-G-T;x<boxsize;x++) for(y=boxsize-G-T;y<boxsize;y++)
            SETBIT(x,y)
    } else
#else
    if (use3D)
#endif
    {
        if (!IS_LEFT(n) && !IS_UP(n) && !IS_LEFT_UP(n))
          for (x=0;x<boxsize/2-RR;x++) for(y=0;y<boxsize/2-RR;y++)
            THREEQUARTERSBIT(x,y)
        if (!IS_LEFT(n) && !IS_DOWN(n) && !IS_LEFT_DOWN(n))
          for (x=0;x<boxsize/2-RR;x++) for(y=boxsize/2+RR;y<boxsize;y++)
            THREEQUARTERSBIT(x,y)
        if (!IS_RIGHT(n) && !IS_UP(n) && !IS_RIGHT_UP(n))
          for (x=boxsize/2+RR;x<boxsize;x++) for(y=0;y<boxsize/2-RR;y++)
            THREEQUARTERSBIT(x,y)
        if (!IS_RIGHT(n) && !IS_DOWN(n) && !IS_RIGHT_DOWN(n))
          for (x=boxsize/2+RR;x<boxsize;x++) for(y=boxsize/2+RR;y<boxsize;y++)
            THREEQUARTERSBIT(x,y)
    }

    if ((images[next][n] = XCreateImage(display, visual,
	    1, XYBitmap, 0, (char *) data, boxsize, boxsize, 8, 0)) == None) {
	(void) free((void *) data);
	return False;
    }
    images[next][n]->byte_order = MSBFirst;
    images[next][n]->bitmap_unit = 8;
    images[next][n]->bitmap_bit_order = LSBFirst;
    return True;
}

/* ------------------------------------------------------------------ */

static Bool
create_images(use3D, next)
  Bool use3D;
  int next;
{
  int n;
  
  if (!create_an_image(BITMAPS - 1, use3D, next))
    return False;
  for (n = 0; n < BITMAPS - 1; n++) {
    /* Avoid duplication of identical images. */
    if (IS_UP(n) && IS_DOWN(n) && IS_LEFT(n) && IS_RIGHT(n))
      images[next][n] = images[next][BITMAPS - 1];
    else if (IS_LEFT_UP(n) && (IS_LEFT(n) || IS_UP(n)))
      images[next][n] = images[next][n & ~LEFT_UP];
    else if (IS_LEFT_DOWN(n) && (IS_LEFT(n) || IS_DOWN(n)))
      images[next][n] = images[next][n & ~LEFT_DOWN];
    else if (IS_RIGHT_UP(n) && (IS_RIGHT(n) || IS_UP(n)))
      images[next][n] = images[next][n & ~RIGHT_UP];
    else if (IS_RIGHT_DOWN(n) && (IS_RIGHT(n) || IS_DOWN(n)))
      images[next][n] = images[next][n & ~RIGHT_DOWN];
    else if (image_needed(n)) {
      if (!create_an_image(n, use3D, next))
	return False;
    }
  }
  return True;
}

/* ------------------------------------------------------------------ */

static int
polyPixmap(i, j, cur)
   int i, j;
   Bool cur;
{
  unsigned int pmid = 0;
  thing_t  *aThing;

  aThing = (cur) ? &curThing : &nextThing;
  if (ARR(i,j,(*aThing)) != ARR(i-1,j,(*aThing)))
    pmid |= LEFT;
  if (ARR(i,j,(*aThing)) != ARR(i,j+1,(*aThing)))
    pmid |= DOWN;
  if (ARR(i,j,(*aThing)) != ARR(i+1,j,(*aThing)))
    pmid |= RIGHT;
  if (ARR(i,j,(*aThing)) != ARR(i,j-1,(*aThing)))
    pmid |= UP;
  if (ARR(i,j,(*aThing)) != ARR(i-1,j+1,(*aThing)))
    pmid |= LEFT_DOWN;
  if (ARR(i,j,(*aThing)) != ARR(i+1,j+1,(*aThing)))
    pmid |= RIGHT_DOWN;
  if (ARR(i,j,(*aThing)) != ARR(i+1,j-1,(*aThing)))
    pmid |= RIGHT_UP;
  if (ARR(i,j,(*aThing)) != ARR(i-1,j-1,(*aThing)))
    pmid |= LEFT_UP;
  return pmid;
}

/* ------------------------------------------------------------------ */

void
inits(argc, argv)
    int             argc;
    char           *argv[];
{
    XSetWindowAttributes att;
    unsigned int    attvm;
    XTextProperty   wName, iName;
    XClassHint      classhints;
    XEvent          ev;
    XGCValues       gcv;
    unsigned long   gcvm;
    int             i, j, col, turn_style, number_polyominoes; 

    (void) SRAND(time(NULL));
    readPolyominoes();

    turn_style = tris[next.squares - MIN_SQUARES].mode[next.diagonal].turn_style;
    if (turn_style < 0 || turn_style >= MAX_TYPES)
    {
      (void) fprintf(stderr, "Tetris: corrupted polyomino file\n");
      (void) fprintf(stderr, "\tturn_style = %d.\n", turn_style);
      exit(1);
    }
    number_polyominoes = tris[next.squares - MIN_SQUARES].mode
      [next.diagonal].number[next.mixed][turn_style];
    if (number_polyominoes <= 0 || number_polyominoes > MAX_START_POLYOMINOES)
    {
      (void) fprintf(stderr, "Tetris: corrupted polyomino file\n");
      (void) fprintf(stderr, "\tnumber_polyominoes = %d.\n",
               number_polyominoes);
      exit(1);
    }
    /* prefilled */
    if (randomFill)
      /* First Algorithm randomly fills boxes to a certain level 
         The problem with this is that it could potentially fill up
         a whole row */
      for (i = 0; i < COLS; i++)
        for (j = 0; j < ROWS; j++)
            if ((j >= ROWS - prefilled) && (NRAND(2) == 0)) {
                field[j][i].pmid = BITMAPS - 1;
                field[j][i].cid = NRAND(number_polyominoes);
            } else {
                field[j][i].pmid = -1;
                field[j][i].cid = 0;
            }
    else
      /* Second Algorithm randomly picks SQUARES_PER_ROW boxes per row 
         This is more in keeping with the original */
      for (j = 0; j < ROWS; j++) {
         for (i = 0; i < COLS; i++) {
                field[j][i].pmid = -1;
                field[j][i].cid = 0;
	 }
	 if (j >= ROWS - prefilled)
           for (i = 0; i < SQUARES_PER_ROW; i++) {
                do
                    col = NRAND(COLS);
                while (field[j][col].pmid != -1);
                field[j][col].pmid = 0;
                field[j][col].cid = NRAND(number_polyominoes);
	   }
      }

    titleLen = strlen(MSG_TITLE);
    titleWidth = XTextWidth(bigFont, MSG_TITLE, titleLen);
    authorLen = strlen(MSG_AUTHOR);
    authorWidth = XTextWidth(tinyFont, MSG_AUTHOR, authorLen);

    frameW = BOXSIZE * COLS;
    frameH = BOXSIZE * ROWS;
    topRWidth = NEXTBOXSIZE * MAX_SQUARES + OFFSET;
    topHeight = frameH + OFFSET * 2 + 4;
    if (titleWidth > topRWidth)
	topRWidth = titleWidth;
    if (authorWidth > topRWidth)
	topRWidth = authorWidth;
    topMidX = frameW + OFFSET + 4;
    topMidY = topHeight / 2 + bigFont->ascent;
    topWidth = topMidX + topRWidth;
    frameX = frameY = OFFSET + 2;

    titleX = (topRWidth - titleWidth) / 2 + topMidX;
    titleY = OFFSET + 2 + bigFont->ascent;
    authorX = (topRWidth - authorWidth) / 2 + topMidX;
    authorY = OFFSET + 2 + bigFont->ascent + bigFont->descent +
	tinyFont->ascent;

    sX = topMidX + OFFSET;
    sScoreY = topHeight - OFFSET - 2 - tinyFont->descent;
    sRowsY = sScoreY - tinyFont->descent - tinyFont->ascent - 2;
    sLevelY = sRowsY - tinyFont->descent - tinyFont->ascent - 2;

    sizehints.width = (sizehints.min_width =
	(sizehints.max_width = topWidth));
    sizehints.height = (sizehints.min_height =
	(sizehints.max_height = topHeight));

    theCursor = XCreateFontCursor(display, XC_exchange);

    /* arrow keys or number pad */
    XRebindKeysym(display, XK_R10, NULL, 0,
	(unsigned char *) "j", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_4, NULL, 0,
	(unsigned char *) "j", sizeof(unsigned char));
    XRebindKeysym(display, XK_Left, NULL, 0,
	(unsigned char *) "j", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_Left, NULL, 0,
	(unsigned char *) "j", sizeof(unsigned char));
    XRebindKeysym(display, XK_R8, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_8, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
#if	defined(UP_REFLECT)
    /* UP key to reflect */
    XRebindKeysym(display, XK_Up, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
#else
    /* UP key to rotate */
    XRebindKeysym(display, XK_Up, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
#endif
    XRebindKeysym(display, XK_KP_Up, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
    XRebindKeysym(display, XK_R5, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_Divide, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
    XRebindKeysym(display, XK_R11, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_5, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
    XRebindKeysym(display, XK_Begin, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_Begin, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
    XRebindKeysym(display, XK_R12, NULL, 0,
	(unsigned char *) "l", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_6, NULL, 0,
	(unsigned char *) "l", sizeof(unsigned char));
    XRebindKeysym(display, XK_Right, NULL, 0,
	(unsigned char *) "l", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_Right, NULL, 0,
	(unsigned char *) "l", sizeof(unsigned char));
    XRebindKeysym(display, XK_R14, NULL, 0,
	(unsigned char *) " ", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_2, NULL, 0,
	(unsigned char *) " ", sizeof(unsigned char));
#if	defined(DOWN_ROTATE)
    /* DOWN key to rotate */
    XRebindKeysym(display, XK_Down, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
#else
    /* DOWN key to drop */
    XRebindKeysym(display, XK_Down, NULL, 0,
	(unsigned char *) " ", sizeof(unsigned char));
#endif
    XRebindKeysym(display, XK_KP_Down, NULL, 0,
	(unsigned char *) " ", sizeof(unsigned char));

    /* create windows */
    attvm = CWBackPixel | CWEventMask | CWDontPropagate | CWCursor;
    att.background_pixel = bg;
    att.event_mask = ExposureMask | KeyPressMask |
	StructureNotifyMask | FocusChangeMask;
    att.do_not_propagate_mask = KeyReleaseMask |
	ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
	ButtonMotionMask | Button1MotionMask | Button2MotionMask |
	Button3MotionMask | Button4MotionMask | Button5MotionMask;
    att.cursor = theCursor;

    mainWin = XCreateWindow(display, DefaultRootWindow(display),
	sizehints.x, sizehints.y, topWidth, topHeight, 0,
	CopyFromParent, InputOutput, CopyFromParent, attvm, &att);

    attvm = CWBackPixel | CWBorderPixel | CWEventMask;
    att.border_pixel = fg;
    att.event_mask = ExposureMask;

    blockWin = XCreateWindow(display, mainWin,
	frameX-2, frameY-2, frameW, frameH, 2,
	CopyFromParent, InputOutput, CopyFromParent, attvm, &att);

    /* WM hints */
    XStringListToTextProperty(&winName, 1, &wName);
    XStringListToTextProperty(&iconName, 1, &iName);

    wmhints.icon_pixmap = XCreateBitmapFromData(display,
	mainWin, (char *) ticon_bits, ticon_width, ticon_height);
    classhints.res_name = "tetris";
    classhints.res_class = "Tetris";

    XSetWMProperties(display, mainWin, &wName, &iName,
	argv, argc, &sizehints, &wmhints, &classhints);

    delw = XInternAtom(display, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(display, mainWin, &delw, 1);

    /* GC's */

    gcvm = GCForeground | GCBackground | GCFunction |
	GCFont | GCGraphicsExposures;

    gcv.function = GXcopy;
    gcv.foreground = fg;
    gcv.background = bg;
    gcv.font = bigFont->fid;
    gcv.graphics_exposures = False;
    bigGC = XCreateGC(display, mainWin, gcvm, &gcv);

    gcv.font = tinyFont->fid;
    tinyGC = XCreateGC(display, mainWin, gcvm, &gcv);

    gcv.foreground = bg;
    gcv.background = fg;
    revGC = XCreateGC(display, mainWin, gcvm, &gcv);

    gcv.background = bg;
    for (i = 0; i < MAX_START_POLYOMINOES; i++) {
	gcv.foreground = getColor(thingFGs[i]);
	if (gcv.foreground == bg)
	    gcv.foreground = fg;
	thingGCs[i] = XCreateGC(display, blockWin, gcvm, &gcv);
    }

    gcv.foreground = fg;
    gcv.function = GXxor;
    xorGC = XCreateGC(display, blockWin, gcvm, &gcv);

    (void) create_images(use3D, 0);
    (void) create_images(use3D, 1);

    /* new things */
    newThing();
    newThing();

    /* the last thing is to wait for mapped */
    XMapWindow(display, blockWin);
    XMapRaised(display, mainWin);
    XNextEvent(display, &ev);
}

/* ------------------------------------------------------------------ */

void
newThing()
{
    curThing = nextThing;
    current = next;
    nextThing.random_number = LRAND();
    nextThing.random_rotation = NRAND(MAX_SIDES);
    nextThing.random_reflection = NRAND(2);
    nextThing.bonus = next.bonus;
    redoNext();
}

/* ------------------------------------------------------------------ */

void
drawTitle()
{
    XDrawString(display, mainWin, bigGC,
	titleX, titleY, MSG_TITLE, titleLen);
    XDrawString(display, mainWin, tinyGC,
	authorX, authorY, MSG_AUTHOR, authorLen);
}

/* ------------------------------------------------------------------ */

void
drawStatus()
{
    char            buf[30];

    (void) sprintf(buf, "Score: %d", score);
    XDrawImageString(display, mainWin, tinyGC, sX, sScoreY, buf, strlen(buf));

    (void) sprintf(buf, "Level: %d    ", level);
    XDrawImageString(display, mainWin, tinyGC, sX, sLevelY, buf, strlen(buf));

    (void) sprintf(buf, "Rows: %d", rows);
    XDrawImageString(display, mainWin, tinyGC, sX, sRowsY, buf, strlen(buf));
}

/* ------------------------------------------------------------------ */

static void
drawSquare(pmid, cid, i, j)
    int             pmid, cid, i, j;
{
    (void) XPutImage(display, blockWin,
      thingGCs[cid], images[0][pmid % BITMAPS],
      0, 0, BOXSIZE * i, BOXSIZE * j, BOXSIZE, BOXSIZE);
}

/* ------------------------------------------------------------------ */

static void
clearSquare(i, j)
    int             i, j;
{
    XFillRectangle(display, blockWin, revGC,
	i * BOXSIZE, j * BOXSIZE, BOXSIZE, BOXSIZE);
}

/* ------------------------------------------------------------------ */

void
drawField()
{
    int             i, j;

    for (i = 0; i < COLS; i++)
	for (j = 0; j < ROWS; j++)
	    if (field[j][i].pmid >= 0)
		drawSquare(field[j][i].pmid, field[j][i].cid, i, j);
}

/* ------------------------------------------------------------------ */

void
drawThing()
{
    int             i, j;

    for (i = 0; i < curThing.size; i++)
	for (j = 0; j < curThing.size; j++)
	    if (tris[curThing.squares].polyomino
		    [curThing.polyomino_number].shape[j][i])
		drawSquare(polyPixmap(i, j, True), curThing.color_number,
		    curThing.xpos + i, curThing.ypos + j);
}

/* ------------------------------------------------------------------ */

static void
drawThingDiff(old)
    thing_t        *old;
{
    int             i, j, ox, oy;

    for (i = 0; i < curThing.size; i++)
	for (j = 0; j < curThing.size; j++)
	    if ((curThing.ypos + j >= 0) && tris[curThing.squares].polyomino
		    [curThing.polyomino_number].shape[j][i])
		drawSquare(polyPixmap(i, j, True), curThing.color_number,
		    curThing.xpos + i, curThing.ypos + j);

    for (i = 0; i < curThing.size; i++)
	for (j = 0; j < curThing.size; j++) {
	    ox = old->xpos + i - curThing.xpos;
	    oy = old->ypos + j - curThing.ypos;
	    if (tris[old->squares].polyomino
		    [old->polyomino_number].shape[j][i] &&
		((ox < 0) || (ox >= curThing.size) ||
		 (oy < 0) || (oy >= curThing.size) ||
		 !tris[curThing.squares].polyomino
		    [curThing.polyomino_number].shape[oy][ox]))
		clearSquare((old->xpos + i), (old->ypos + j));
	}
}

/* ------------------------------------------------------------------ */

void
drawNext()
{
    int             x, y;
    int             i, j;

    x = topMidX + (topRWidth - nextThing.size * NEXTBOXSIZE) / 2;
    y = topMidY - nextThing.size * NEXTBOXSIZE / 2;
    for (i = 0; i < nextThing.size; i++)
	for (j = 0; j < nextThing.size; j++)
	    if (tris[nextThing.squares].polyomino
		    [nextThing.polyomino_number].shape[j][i]) {
	    	(void) XPutImage(display, mainWin,
		    thingGCs[nextThing.color_number],
		    images[1][polyPixmap(i, j, False)],
		    0, 0, x + NEXTBOXSIZE * i, y + NEXTBOXSIZE * j,
		    NEXTBOXSIZE, NEXTBOXSIZE);
	    }
}

/* ------------------------------------------------------------------ */

void
clearNext()
{
    XFillRectangle(display, mainWin, revGC,
	topMidX, topMidY - NEXTBOXSIZE * MAX_SQUARES / 2,
	topRWidth, NEXTBOXSIZE * MAX_SQUARES);
}

/* ------------------------------------------------------------------ */

void
redoNext()
{
    int turn_style = tris[next.squares - MIN_SQUARES].mode[next.diagonal].turn_style;
    int next_start, i;

    nextThing.squares = next.squares - MIN_SQUARES + nextThing.bonus;
    next_start = nextThing.random_number % tris[nextThing.squares].mode
	[next.diagonal].number[(nextThing.bonus) ? NOMIX : next.mixed][turn_style];
    nextThing.color_number = next_start;

    if (next.mixed && !nextThing.bonus)
    {
	nextThing.squares = 0;
	while (next_start >= tris[nextThing.squares].mode
	    [next.diagonal].number[NOMIX][turn_style])
	{
	    next_start -= tris[nextThing.squares].mode
		[next.diagonal].number[NOMIX][turn_style];
	    nextThing.squares++;
	}
    }
    nextThing.polyomino_number = tris[nextThing.squares].mode
	[next.diagonal].start[next_start][turn_style];
    if (tris[nextThing.squares].mode[next.diagonal].turn_style > NONE)
	for (i = 0; i < nextThing.random_rotation; i++)
	    nextThing.polyomino_number = tris[nextThing.squares].polyomino
		[nextThing.polyomino_number].rotation;
    if (tris[nextThing.squares].mode[next.diagonal].turn_style == ALL)
	for (i = 0; i < nextThing.random_reflection; i++)
	    nextThing.polyomino_number = tris[nextThing.squares].polyomino
		[nextThing.polyomino_number].reflection;
    nextThing.size = tris[nextThing.squares].polyomino
	[nextThing.polyomino_number].size;
    nextThing.xpos = NRAND(COLS - nextThing.size + 1);
    /* nextThing.xpos = (COLS - nextThing.size) / 2; */
    nextThing.ypos = -tris[nextThing.squares].polyomino
	[nextThing.polyomino_number].start_height;
}

/* ------------------------------------------------------------------ */

void
banner(msg)
    char            msg[];
{
    int             mlen = strlen(msg);
    int             w = XTextWidth(bigFont, msg, mlen);
    int             x = (topRWidth - w)/2 + topMidX;

    XFillRectangle(display, mainWin, revGC,
	x - 60, topMidY - bigFont->ascent - 5,
	w + 120, bigFont->ascent + bigFont->descent + 10);
    XDrawString(display, mainWin, bigGC, x, topMidY, msg, mlen);
}

/* ------------------------------------------------------------------ */

void
putBox()
{
    int             i, j;
    int             x = curThing.xpos, y = curThing.ypos;

    for (i = 0; i < curThing.size; i++)
      for (j = 0; j < curThing.size; j++)
        if ((y + j >= 0) && tris[curThing.squares].polyomino
              [curThing.polyomino_number].shape[j][i]) {
          field[y + j][x + i].pmid = polyPixmap(i, j, True) % BITMAPS;
	  field[y + j][x + i].cid = curThing.color_number;
	}
}

/* ------------------------------------------------------------------ */

Bool
overlapping()
{
    int             i, j;
    int             x = curThing.xpos, y = curThing.ypos;

    for (i = 0; i < curThing.size; i++)
	for (j = 0; j < curThing.size; j++)
	    if (tris[curThing.squares].polyomino
		    [curThing.polyomino_number].shape[j][i]) {
		if ((y + j >= ROWS) || (x + i < 0) || (x + i >= COLS))
		    return True;
		if (gradualAppear) {
/* This method one can turn polyomino to an area above of screen, also
   part of the polyomino may not be visible initially */          
		    if ((y + j >= 0) && (field[y + j][x + i].pmid >= 0))
			return True;
		} else {
/* This method does not allow turning polyomino to an area above screen */
		    if ((y + j < 0) || (field[y + j][x + i].pmid >= 0))
			return True;
		}
	    }

    return False;
}

/* ------------------------------------------------------------------ */

Bool
atBottom()
{
    int             i, j;
    int             x = curThing.xpos, y = curThing.ypos;

    for (i = 0; i < curThing.size; i++)
      for (j = 0; j < curThing.size; j++)
	if ((y + j >= -1) && tris[curThing.squares].polyomino
              [curThing.polyomino_number].shape[j][i])
	  if ((y + j >= ROWS - 1) || (x + i < 0) || (x + i >= COLS) ||
	      (field[y + j + 1][x + i].pmid >= 0))
	    return True;

    return False;
}

/* ------------------------------------------------------------------ */

void
tryMove(move)
    move_t          move;
{
    thing_t         old;
    int             i;

    old = curThing;

    switch (move) {
    case FALL:
	curThing.ypos++;
	break;

    case DROP:
	do {
	    curThing.ypos++;
	    score += level + prefilled;
	} while (!overlapping());
	curThing.ypos--;
	break;

    case MOVE_LEFT:
	curThing.xpos--;
	break;

    case MOVE_RIGHT:
	curThing.xpos++;
	break;

    case ROTATE:
        if (tris[current.squares - MIN_SQUARES].mode[current.diagonal].turn_style > NONE)
        {
          if (cw)
            for (i = 0; i < MAX_SIDES - 1; i++)
              curThing.polyomino_number = tris[curThing.squares].polyomino
                [curThing.polyomino_number].rotation;
          else /* ccw */
            curThing.polyomino_number = tris[curThing.squares].polyomino
              [curThing.polyomino_number].rotation;
	  curThing.xpos = old.xpos;
	  curThing.ypos = old.ypos;
	}
	break;

    case REFLECT: /* reflect on y axis */
        if (tris[current.squares - MIN_SQUARES].mode[current.diagonal].turn_style == ALL)
        {
          curThing.polyomino_number = tris[curThing.squares].polyomino
            [curThing.polyomino_number].reflection;
	  curThing.xpos = old.xpos;
	  curThing.ypos = old.ypos;
	}
	break;
    }

    if (!overlapping())
	drawThingDiff(&old);
    else
	curThing = old;
}

/* ------------------------------------------------------------------ */

void
fillLines()
{
    int             i, j;
    int turn_style = tris[current.squares - MIN_SQUARES].mode[current.diagonal].turn_style;
    
    XSync(display, True);
    for (j = 0; j <= ROWS / 2; j++) {
      for (i = 0; i < COLS; i++) {
	drawSquare(BITMAPS - 1, NRAND(tris[current.squares - MIN_SQUARES].mode
            [current.diagonal].number[current.mixed][turn_style]), i, j);
	drawSquare(BITMAPS - 1, NRAND(tris[current.squares - MIN_SQUARES].mode
            [current.diagonal].number[current.mixed][turn_style]), i, ROWS - j - 1);
        }
	XSync(display, True);
    }

}
							
/* ------------------------------------------------------------------ */

int
checkLines()
{
    int             lSet[ROWS], nset = 0;
    int             i, j, y;

    /* Find filled rows */
    for (j = 0; j < ROWS; j++) {
	lSet[j] = 0;
	for (i = 0; i < COLS; i++)
	    if (field[j][i].pmid >= 0)
		lSet[j]++;
	if (lSet[j] == COLS)
	    nset++;
    }

    if (nset) {
	for (i = 0; i < ((NUM_FLASHES / nset) % 2) * 2; i++) {
	    for (j = 0; j < ROWS; j++) {
		if (lSet[j] == COLS)
		    XFillRectangle(display, blockWin, xorGC,
			0, j * BOXSIZE, frameW, BOXSIZE);
	    }
	    XFlush(display);
	}

	/* Shrink */
	for (j = ROWS - 1; j >= 0; j--) {
	    if (lSet[j] == COLS) {
		for (y = j; y > 0; y--)
		    for (i = 0; i < COLS; i++)
			field[y][i] = field[y-1][i];
		for (i = 0; i < COLS; i++)
		    field[0][i].pmid = -1;

		XCopyArea(display, blockWin, blockWin, tinyGC,
			0, 0, frameW, j * BOXSIZE, 0, BOXSIZE);
		
		XFillRectangle(display, blockWin, revGC,
			0, 0, frameW, BOXSIZE);

		for (i = j; i > 0; i--)
		    lSet[i] = lSet[i-1];
		lSet[0] = 0;

		if (j > 0)
		    for (i = 0; i < COLS; i++) {
			int        tmp = field[j][i].pmid;
			if ((tmp >= 0) && (tmp != CHECKDOWN((unsigned int) tmp))) {
			    field[j][i].pmid = CHECKDOWN((unsigned int) tmp);
			    drawSquare(field[j][i].pmid, field[j][i].cid,
                                       i, j);
			}
		    }

		j++;

		if (j < ROWS)
		    for (i = 0; i < COLS; i++) {
			int        tmp = field[j][i].pmid;
			if ((tmp >= 0) && (tmp != CHECKUP((unsigned int) tmp))) {
			    field[j][i].pmid = CHECKUP((unsigned int) tmp);
			    drawSquare(field[j][i].pmid, field[j][i].cid,
                                       i, j);
			}
		    }

		XFlush(display);
	    }
	}

	if (beep)
	    XBell(display, BVOLUME);
	XSync(display, False);
    }

    return nset;
}

/* ------------------------------------------------------------------ */

void
realTime(tv)
    struct timeval *tv;
{
    while (tv->tv_usec < 0) {
	tv->tv_sec--;
	tv->tv_usec += MILLION;
    }
    while (tv->tv_usec >= MILLION) {
	tv->tv_sec++;
	tv->tv_usec -= MILLION;
    }
}

/* ------------------------------------------------------------------ */

static char
readChar(lc, cc)
int *lc;
int *cc;
{
  int lcold = *lc, ccold = *cc;

  if (*lc >= sizeof(polyomino_data)/sizeof(polyomino_data[0]))
    return ' ';
  if (*cc == strlen(polyomino_data[*lc])) {
    (*lc)++;
    *cc = 0;
  } else
    (*cc)++;
  return polyomino_data[lcold][ccold];
}

/* ------------------------------------------------------------------ */

#define BASE 10
#define MAXLOOP 100

static int
readInt(lc, cc, c)
int *lc;
int *cc;
char *c;
{
  Bool first = False;
  int loopcount = 0;
  int number;

  while (!first) {
    *c = readChar(lc, cc);
    if (*c >= '0' && *c <='9')
      first = True;
    if (loopcount++ >= MAXLOOP)
    {
      (void) fprintf(stderr, "Tetris: readInt1 infinite loop.\n");
      exit(1);
    }
  }
  number = *c - '0';
  first = False;
  loopcount = 0;
  while (!first) {
    *c = readChar(lc, cc);
    if (*c < '0' || *c > '9')
      first = True;
    else
      number = BASE * number + *c - '0';
    if (loopcount++ >= MAXLOOP)
    {
      (void) fprintf(stderr, "Tetris: readInt2 infinite loop.\n");
      exit(1);
    }
  }
  return number;
}

/* ------------------------------------------------------------------ */

static void
readPolyominoes()
{
  char c;
  int i, j, k, sq, polyomino, sum, start, n, size = 0, height, diag,
    game, toss = 0;
  int counter[MAX_SQUARES - MIN_SQUARES + 1];
  int start_counter[MAX_SQUARES - MIN_SQUARES + 1][MAX_MODES][MAX_TYPES];
  int lc = 0, cc = 0;

  for (sq = 0; sq <= MAX_SQUARES - MIN_SQUARES; sq++)
  {
    counter[sq] = 0;
    for (polyomino = 0; polyomino < MAX_POLYOMINOES; polyomino++)
      for (j = 0; j < MAX_SQUARES; j++)
        for (i = 0; i < MAX_SQUARES; i++)
          tris[sq].polyomino[polyomino].shape[j][i] = 0;
    for (j = 0; j < MAX_TYPES; j++)
    {
      for (polyomino = 0; polyomino < MAX_START_POLYOMINOES; polyomino++)
      {
        tris[sq].mode[NODIAG].start[polyomino][j] = 0;
        tris[sq].mode[DIAGONAL].start[polyomino][j] = 0;
      }
      start_counter[sq][NODIAG][j] = 0;
      start_counter[sq][DIAGONAL][j] = 0;
      tris[sq].mode[NODIAG].number[NOMIX][j] = 0;
      tris[sq].mode[DIAGONAL].number[NOMIX][j] = 0;
      tris[sq].mode[NODIAG].number[MIXED][j] = 0;
      tris[sq].mode[DIAGONAL].number[MIXED][j] = 0;
    }
    tris[sq].mode[NODIAG].turn_style = NONE;
    tris[sq].mode[DIAGONAL].turn_style = NONE;
  }
    c = readChar(&lc, &cc);
    while (lc < sizeof(polyomino_data)/sizeof(polyomino_data[0]) - 1)
    {
      if (c == '+')
      {
        k = readInt(&lc, &cc, &c);
        sq = k - MIN_SQUARES;
        for (i = 0; i < MAX_TYPES; i++)
        {
       	  k = readInt(&lc, &cc, &c);
          tris[sq].mode[NODIAG].number[NOMIX][i] = k;
        }
       	k = readInt(&lc, &cc, &c);
        tris[sq].mode[NODIAG].turn_style = k;
       	k = readInt(&lc, &cc, &c);
        tris[sq].diagonal_switch = k;
        if (tris[sq].diagonal_switch == True)
        {
          for (i = 0; i < MAX_TYPES; i++)
          {
       	    k = readInt(&lc, &cc, &c);
            tris[sq].mode[DIAGONAL].number[NOMIX][i] = k;
          }
       	  k = readInt(&lc, &cc, &c);
          tris[sq].mode[DIAGONAL].turn_style = k;
        }
        c = readChar(&lc, &cc);
      }
      if (c == '*')
      {
       	size = readInt(&lc, &cc, &c);
        c = readChar(&lc, &cc);
      }
#if 0
      if (c == '~') /* Useful for debugging Things */
      {
       	toss = readInt(&lc, &cc, &c);
      }
#endif
      if (c == '#')
      {
       	k = readInt(&lc, &cc, &c);
        sq = k - MIN_SQUARES;
       	n = readInt(&lc, &cc, &c);
        if (tris[sq].diagonal_switch == True)
       	  n = readInt(&lc, &cc, &c);
#if 0
        for (polyomino = 0; polyomino <= toss; polyomino++)
          while ((c = readChar(&lc, &cc) && c != '\n');
#endif
        for (polyomino = 0; polyomino < n - toss; polyomino++)
        {
          sum = polyomino + counter[sq];
            /* This is only there to "read" input file */
       	  k = readInt(&lc, &cc, &c);
       	  k = readInt(&lc, &cc, &c);
          tris[sq].polyomino[sum].rotation =
            k + counter[sq] - toss;
       	  k = readInt(&lc, &cc, &c);
          tris[sq].polyomino[sum].reflection =
            k + counter[sq] - toss;
          for (game = JUMPIN; game <= GRADUAL; game++)
          {
       	    height = readInt(&lc, &cc, &c);
            if (!gradualAppear && game == JUMPIN)
              tris[sq].polyomino[sum].start_height = height;
            else if (gradualAppear && game == GRADUAL)
              tris[sq].polyomino[sum].start_height = height;
            for (diag = NODIAG; diag <= tris[sq].diagonal_switch; diag++)
            {
       	      start = readInt(&lc, &cc, &c);
              if (game == JUMPIN)
              {
                if (sq == 0 ||
                    tris[sq - 1].mode[diag].turn_style == NONE)
                {
                  i = start_counter[sq][diag][NONE];
                  tris[sq].mode[diag].start[i][NONE] = sum;
                  start_counter[sq][diag][NONE]++;
                }
                if ((sq == 0 ||
                     tris[sq - 1].mode[diag].turn_style < ALL) &&
                    start != NONE)
                {
                  i = start_counter[sq][diag][NOREFL];
                  tris[sq].mode[diag].start[i][NOREFL] = sum;
                  start_counter[sq][diag][NOREFL]++;
                }
                if (start == ALL)
                {
                  i= start_counter[sq][diag][ALL];
                  tris[sq].mode[diag].start[i][ALL] = sum;
                  start_counter[sq][diag][ALL]++;
                }
              }
            }
          }
	  tris[sq].polyomino[sum].size = size;
          for (j = 0; j < size; j++)
            for (i = 0; i < size; i++)
            {
       	      k = readInt(&lc, &cc, &c);
              tris[sq].polyomino[sum].shape[j][i] = k;
            }
        }
        counter[sq] += n - toss;
        toss = 0;
        c = readChar(&lc, &cc);
      }
    }
    for (i = 0; i <= MAX_SQUARES - MIN_SQUARES; i++)
      for (j = 0; j < MAX_TYPES; j++)
        for (k = 0; k <= i; k++)
        {
          tris[i].mode[NODIAG].number[MIXED][j] +=
            tris[k].mode[NODIAG].number[NOMIX][j];
          if (tris[i].diagonal_switch == True)
             /*since k & i are not independent*/
            tris[i].mode[DIAGONAL].number[MIXED][j] +=
              tris[k].mode[DIAGONAL].number[NOMIX][j];
        }
}
