
/*
 *                       Life v1.0
 *
 *                     by Phil Gibbs
 *
 *                © 1989 Amiga Computing
 *
 *             Complied with Lattice C v4.0
 */

#include <stdio.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <proto/dos.h>
#include <hardware/custom.h>
#include <hardware/dmabits.h>
#include <hardware/blit.h>
#include <graphics/gfx.h>
#include <graphics/gfxbase.h>
#include <graphics/text.h>
#include <proto/graphics.h>
#include <intuition/screens.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/preferences.h>
#include <proto/intuition.h>


#define WIDTH 320      /* maximum dimensions of WINDOW */
#define HEIGHT 250
#define DEPTH 5
#define NCOLOURS 32


struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;

struct TextAttr font =       { (STRPTR) "topaz.font",
                               TOPAZ_EIGHTY,0,0
                             };

struct NewScreen newscreen = { 0,0,
                               WIDTH,HEIGHT,DEPTH,
                               3,20,
                               0,
                               CUSTOMSCREEN,
                               &font,
                               "           The game of Life",
                               NULL,
                               NULL
                             };

struct NewWindow newwindow = { 0,0,
                               WIDTH,HEIGHT,
                               25,6,
                               MENUPICK | MENUVERIFY | MOUSEBUTTONS,
                               SIMPLE_REFRESH|BACKDROP|BORDERLESS|ACTIVATE,
                               NULL,NULL,
                               "",
                               NULL,NULL,0,0,0,0,
                               CUSTOMSCREEN
                             };

struct Screen *screen = NULL;
struct Window *window = NULL;

struct IntuiMessage *message;

USHORT colours[] = { 0x0044,0x0f00,0x0000,0x0fff,
                     0x0ff8,0x0ff0,0x0fa0,0x0f60,
                     0x0f30,0x0f10,0x0f00,0x0e01,
                     0x0d02,0x0c03,0x0b04,0x0a05,
                     0x0906,0x0807,0x0708,0x0609,
                     0x050a,0x040b,0x030c,0x020d,
                     0x010e,0x000f,0x000e,0x000d,
                     0x000c,0x000b,0x000a,0x0009 };

/* some defines for the blitter minterms */

#define A    0xf0
#define notA 0x0f
#define B    0xcc
#define notB 0x33
#define C    0xaa
#define notC 0x55
#define On   0xff
#define Off  0x00

/* locate the hardware registers for the blitter */

extern struct Custom custom;
struct Custom *customptr = &custom;
#define custom (*customptr)
#define horizontal (WIDTH/16 + 64)
long blitsize;

#define NPLANE 7

short *(pl[NPLANE]);
short *(plane[DEPTH]);
short *p;
short colour = TRUE;
short randommouse = TRUE;
long SEED = 1;
short randomseed = TRUE;
short finish = FALSE;
short cyclic = FALSE;
short repeat = FALSE;
short down = FALSE;
short sym = 0;

short pic[16] = { 0x400e, 0x8009, 0x8808, 0xf008,
                  0x0384, 0x0200, 0x0130, 0x0850,
                  0x0a10, 0x0c80, 0x0040, 0x21c0,
                  0x100f, 0x1011, 0x9001, 0x7002 };

long StartLib(void);
void FinishLib(void);
void ShowError(char *);
long Random(long);
void RandomEdge(short *);
void RandomPlane(short *);
void Add3(short *,short *,short *,short *,short *);
void Add1(short *,short *,short *);
void ClearEdge(short *);
void CycleEdge(short *);
void ClearPlane(short *);
void CopyPlane(short *, short *);
void SetPlane(short *);
void ThreeBlit(short *, short *, short *, short *, short);
void TwoBlit(short *, short *, short *, short);
void Update(short *);
void Randomise(long *);
void MenuServe(void);
void MenuInit(void);
void MenuEnd(void);
void EditMouse(void);
void ChangeBit(long,long,short (*)[16]);
void SquirtMouse(long,long);
void SymmetricMouse(void);

void _main()
{
  long status;
  long class,code;
  long i;
  short (*plan)[333];
  long planesize;
  short minterm1,minterm2;
  long x,y;

  for(i=0;i<NPLANE;i++)
    pl[i] = NULL;

  status = StartLib();
  if(status != 0)
    goto CleanUp;

  screen = OpenScreen(&newscreen);
  if(screen == NULL)
  {
    ShowError("screen would not open");
    goto CleanUp;
  }

  newwindow.Screen = screen;
  window = OpenWindow(&newwindow);
  if(window == NULL)
  {
    ShowError("window would not open");
    goto CleanUp;
  }

  LoadRGB4(&(screen->ViewPort),(short *)&colours,NCOLOURS);

  planesize = (HEIGHT-10)*WIDTH/8;
  for(i=0;i<NPLANE;i++)
  {
    pl[i] = (short *) AllocMem( planesize,MEMF_CHIP );
    if(pl[i] == NULL)
    {
      ShowError("Insufficient memory available");
      goto CleanUp;
    }
  }

  MenuInit();

  for(i=0;i<DEPTH;i++)
  {
    plan = (short (*)[333]) screen->BitMap.Planes[i];
    plane[i] = &(*plan)[11*WIDTH/16];
  }

  if(colour)
    p = pl[6];
  else
    p = plane[0];

  RandomPlane( p );
  ClearEdge( p );

  if(colour)
    SetPlane( plane[1] );

  for(;;)
  {
    Update( p );

    if(repeat && down)
    {
      x = screen->MouseX-8;
      y = screen->MouseY-19;
      SquirtMouse(x,y);
    }

    if(colour)
    {
      blitsize = WIDTH/16 + (HEIGHT-11)*64;
      minterm1 = A&B;                              /* gives a carry */
      TwoBlit( p, plane[0], pl[5], minterm1);      /* pl[0] is carry */
      minterm2 = (A^B)&A;                          /* first add */
      TwoBlit( p, plane[0], pl[0], minterm2);
      minterm1 = (A&notB);                         /* gives carry */
      minterm2 = (A^B)|notC;
      ThreeBlit( pl[5], plane[1], p, pl[1], minterm2);
      TwoBlit( pl[5], pl[1], pl[5], minterm1);
      minterm2 = (A^B)&C;                          /* masked add */
      for(i=2;i<DEPTH;i++)
      {
        ThreeBlit( pl[5], plane[i], p, pl[i], minterm2);
        TwoBlit( pl[5], pl[i], pl[5], minterm1);
      }
      minterm1 = A|B;                              /* if overflow */
      for(i=0;i<DEPTH;i++)
        TwoBlit( pl[5], pl[i], pl[i], minterm1);
      for(i=0;i<DEPTH;i++)
        CopyPlane( pl[i], plane[i] );
    }

    if(window->UserPort->mp_SigBit)
    {
      message = (struct IntuiMessage *) GetMsg(window->UserPort);
      if(message != NULL)
      {
        class = message->Class;
        code = message->Code;
        x = message->MouseX-8;
        y = message->MouseY-19;
        ReplyMsg((struct Message *)message);

        if(class == MENUVERIFY)
        {
          MenuServe();
          down = FALSE;
          if(finish) goto Finish;
        }
        else if(class == MOUSEBUTTONS)
        {
          if(code == SELECTDOWN)
          {
            SquirtMouse(x,y);
            down = TRUE;
          }
          else if(code == SELECTUP)
            down = FALSE;
        }
      }
    }
  }

Finish:

  MenuEnd();

CleanUp:

  for(i=0;i<NPLANE;i++)
    if(pl[i] != NULL)
      FreeMem((char *)pl[i],planesize);

  if(window != NULL)
    CloseWindow(window);

  if(screen != NULL)
    CloseScreen(screen);

  FinishLib();

  exit(0);

}

void SquirtMouse( x, y )
long x,y;
{
  short w,r;
  short k,i;
  short s1,s2;
  long lw;
  long Seed;
  short bits[16];

  bits[0] = 1;

  for(i=1;i<16;i++)
    bits[i] = bits[i-1]<<1;

  if( y > 2 && y < HEIGHT-30 )
  {
    w = x/16;
    r = x-w*16;
    if(randommouse)
    {
      Randomise(&Seed);
      for(k=0;k<16;k++)
      {
        Seed = Random(Seed);
        pic[k] = Seed & 0xffff;
      }
    }

    SymmetricMouse();

    WaitBlit();
    for(k=0;k<16;k++)
    {
      lw = (pic[k]&0xffff) << (15-r);
      s1 = lw >> 16;
      s2 = lw & 0xffff;
      (*(( short (*)[] )p))[(y+k)*WIDTH/16+w]   |= s1;
      (*(( short (*)[] )p))[(y+k)*WIDTH/16+w+1] |= s2;
    }
  }
}

void SymmetricMouse()
{

  short i,j;
  short bits[16];

  bits[0] = 1;

  for(i=1;i<16;i++)
    bits[i] = bits[i-1]<<1;

  {
    if((sym == 1) || (sym == 7))          /* reflect horizontal */
    {
      for(i=0;i<8;i++)
        pic[15-i] = pic[i];
    }
    if((sym == 6) || (sym == 2))         /* reflect main diagonal */
    {
      for(i=0;i<15;i++)
        for(j=i;j<16;j++)
          if(pic[i] & bits[j])
            pic[j] |= bits[i];
          else
            pic[j] &= ~bits[i];
    }
    if((sym == 3) || (sym == 6))         /* reflect other diagonal */
    {
      for(i=1;i<16;i++)
        for(j=15-i;j<16;j++)
          if(pic[i] & bits[j])
            pic[15-j] |= bits[15-i];
          else
            pic[15-j] &= ~bits[15-i];
    }
    if(sym == 7)                         /* reflect vertical */
    {
      for(i=0;i<16;i++)
        for(j=8;j<16;j++)
          if(pic[i] & bits[j])
            pic[i] |= bits[15-j];
          else
            pic[i] &= ~bits[15-j];
    }
    if((sym == 3) || (sym == 5))         /* rotate 90 */
    {
      for(i=0;i<8;i++)
        for(j=8;j<16;j++)
          if(pic[i] & bits[j])
            pic[15-j] |= bits[i];
          else
            pic[15-j] &= ~bits[i];
    }
    if((sym == 3) || (sym == 4) ||
       (sym == 6) || (sym == 5))         /* rotate 180 */
    {
      for(i=0;i<8;i++)
        for(j=0;j<16;j++)
          if(pic[i] & bits[j])
            pic[15-i] |= bits[15-j];
          else
            pic[15-i] &= ~bits[15-j];
    }
  }
}

void Update( p )
short *p;
{
  short minterm;
  short *p1, *p2;

  blitsize = (WIDTH/16) +(HEIGHT-13)*64;

  p1 = &(*(( short (*)[] ) p))[WIDTH/16];
  p2 = &(*(( short (*)[] ) p))[2*WIDTH/16];

  Add3( p1,p,p2,pl[0],pl[1] );

  Add1( pl[0],pl[2],pl[3] );
  Add1( pl[1],pl[4],pl[5] );

  minterm = (A^B) & C;
  ThreeBlit( pl[4],pl[3],pl[2],pl[0],minterm);  /* detects sum to 3 */

  minterm = (A & notB & notC) | (notA & B & C);
  ThreeBlit( pl[5],pl[4],pl[3],pl[1],minterm);  /* detects sum to 4 */

  minterm = A & notB & C;
  ThreeBlit( pl[1], pl[2], p1, pl[4], minterm);  /* live cell plus 3 */

  minterm = (A & notB) | C;
  ThreeBlit( pl[0], pl[5], pl[4], p1, minterm);  /* and finish       */

  if(cyclic)
    CycleEdge( p );
}

void ClearPlane( p )
short *p;
{
  short minterm;

  blitsize = WIDTH/16 + (HEIGHT-11)*64;

  minterm = Off;
  ThreeBlit( p,p,p,p,minterm);
}

void SetPlane( p )
short *p;
{
  short minterm;

  blitsize = (WIDTH/16) + (HEIGHT-11)*64;

  minterm = On;
  ThreeBlit( p,p,p,p,minterm);
}

char title[80];

void Randomise(s)
long *s;
{
  long seconds,micros;

  if(randomseed)
  {
    CurrentTime(&seconds, &micros);
    SEED = seconds%100000 + micros*444;
    SEED = SEED % 100000000;
    if(SEED == 0) SEED = 1;
  }
  sprintf(&title[0],"     The game of Life %d \0",SEED);
  screen->Title = &title[0];
  SetWindowTitles(window,"",&title[0]);
  *s = SEED;
}

void RandomPlane( plane )
short *plane;
{
  long i;
  long Seed;
  long size;

  size = WIDTH*(HEIGHT-11)/16;

  Randomise(&Seed);
  for(i=0;i<size;i++)
  {
    Seed = Random(Seed);
    (*((short (*)[]) plane))[i] =  (Seed & 0x0000ffff);
  }
}

void RandomEdge( plane )
short *plane;
{
  short *p1;
  long i;
  long Seed;

  Randomise(&Seed);

  for(i=0;i<WIDTH/16;i++)
  {
    Seed = Random(Seed);
    (*((short (*)[]) plane))[i] =  (Seed & 0x0000ffff);
    p1 = &(*(( short (*)[] ) plane))[(HEIGHT-12)*WIDTH/16];
    Seed = Random(Seed);
    (*((short (*)[]) p1))[i] =  (Seed & 0x0000ffff);
  }
}

void ClearEdge( plane )
short *plane;
{
  short bltcon0;
  short *p1;

  OwnBlitter();
  bltcon0 = ( DEST | A );
  WaitBlit();
  custom.bltcon0 = bltcon0;
  custom.bltcon1 = 0;
  custom.bltafwm = 0xffff;
  custom.bltalwm = 0xffff;
  custom.bltadat = 0;
  custom.bltamod = 0;
  custom.bltdmod = 0;
  custom.bltdpt = (APTR) plane;
  custom.bltsize = horizontal;
  p1 = &(*(( short (*)[] ) plane))[(HEIGHT-12)*WIDTH/16];
  WaitBlit();
  custom.bltdpt = (APTR) p1;
  custom.bltsize = horizontal;
  DisownBlitter();
}

void CycleEdge( plane )
short *plane;
{
  short bltcon0;
  short *p1,*p2;

  OwnBlitter();
  bltcon0 = ( SRCA | DEST | A );
  p1 = &(*(( short (*)[] ) plane))[(HEIGHT-13)*WIDTH/16];
  WaitBlit();
  custom.bltcon0 = bltcon0;
  custom.bltcon1 = 0;
  custom.bltafwm = 0xffff;
  custom.bltalwm = 0xffff;
  custom.bltamod = 0;
  custom.bltdmod = 0;
  custom.bltapt = (APTR) p1;
  custom.bltdpt = (APTR) plane;
  custom.bltsize = horizontal;
  p1 = &(*(( short (*)[] ) plane))[(HEIGHT-12)*WIDTH/16];
  p2 = &(*(( short (*)[] ) plane))[WIDTH/16];
  WaitBlit();
  custom.bltapt = (APTR) p2;
  custom.bltdpt = (APTR) p1;
  custom.bltsize = horizontal;
  DisownBlitter();
}

void Add3(p1,p2,p3,p4,p5)
short *p1, *p2, *p3, *p4, *p5;
{
  short minterm;

  minterm = (A^B^C);
  ThreeBlit(p1,p2,p3,p4,minterm);          /* low bit of sum */

  minterm = (A&B) | (B&C) | (C&A);
  ThreeBlit(p1,p2,p3,p5,minterm);          /* high bit of sum */
}

void ThreeBlit(p1,p2,p3,p4,minterm)
short *p1, *p2, *p3, *p4;
short minterm;
{
  short bltcon0;

  OwnBlitter();
  bltcon0 = (SRCA|SRCB|SRCC | DEST) | minterm;
  WaitBlit();
  custom.bltcon0 = bltcon0;
  custom.bltcon1 = 0;
  custom.bltafwm = 0xffff;
  custom.bltalwm = 0xffff;
  custom.bltamod = 0;
  custom.bltbmod = 0;
  custom.bltcmod = 0;
  custom.bltdmod = 0;
  custom.bltapt = (APTR) p1;
  custom.bltbpt = (APTR) p2;
  custom.bltcpt = (APTR) p3;
  custom.bltdpt = (APTR) p4;
  custom.bltsize = blitsize;
  DisownBlitter();
}

void TwoBlit(p1,p2,p4,minterm)
short *p1, *p2, *p4;
short minterm;
{
  short bltcon0;

  OwnBlitter();
  bltcon0 = (SRCA|SRCB | DEST) | minterm;
  WaitBlit();
  custom.bltcon0 = bltcon0;
  custom.bltcon1 = 0;
  custom.bltafwm = 0xffff;
  custom.bltalwm = 0xffff;
  custom.bltamod = 0;
  custom.bltbmod = 0;
  custom.bltdmod = 0;
  custom.bltapt = (APTR) p1;
  custom.bltbpt = (APTR) p2;
  custom.bltdpt = (APTR) p4;
  custom.bltsize = blitsize;
  DisownBlitter();
}

void CopyPlane(p1,p4)
short *p1, *p4;
{
  short bltcon0;

  OwnBlitter();
  blitsize = WIDTH/16 + (HEIGHT-11)*64;

  bltcon0 = (SRCA|SRCB | DEST) | A;
  WaitBlit();
  custom.bltcon0 = bltcon0;
  custom.bltcon1 = 0;
  custom.bltafwm = 0xffff;
  custom.bltalwm = 0xffff;
  custom.bltamod = 0;
  custom.bltdmod = 0;
  custom.bltapt = (APTR) p1;
  custom.bltdpt = (APTR) p4;
  custom.bltsize = blitsize;
  DisownBlitter();
}

void Add1(p1,p2,p3)
short *p1, *p2, *p3;
{
  short bltcon0;
  short *p0,*p4,*p5,*p6,*p7;
  short a,b,c;

  OwnBlitter();
  p0 =  &(*((short (*)[2]) p1))[1];
  bltcon0 = (0xf000 | SRCA|SRCB|SRCC | DEST | (A^B^C));
  WaitBlit();
  custom.bltcon0 = bltcon0;
  custom.bltcon1 = 0x1000;
  custom.bltafwm = 0xffff;
  custom.bltalwm = 0xffff;
  custom.bltamod = 0;
  custom.bltbmod = 0;
  custom.bltcmod = 0;
  custom.bltdmod = 0;
  custom.bltapt = (APTR) p0;
  custom.bltbpt = (APTR) p1;
  custom.bltcpt = (APTR) p1;
  custom.bltdpt = (APTR) p2;
  custom.bltsize = blitsize;
  bltcon0 = (0xf000|SRCA|SRCB|SRCC | DEST | ((A&B) | (B&C) | (C&A)));
  WaitBlit();
  custom.bltcon0 = bltcon0;
  custom.bltapt = (APTR) p0;
  custom.bltbpt = (APTR) p1;
  custom.bltcpt = (APTR) p1;
  custom.bltdpt = (APTR) p3;
  custom.bltsize = blitsize;
  p0 =  &(*((short (*)[]) p1))[1];
  p4 =  &(*((short (*)[]) p1))[WIDTH/16*(HEIGHT-13)-1];
  p5 =  &(*((short (*)[]) p1))[WIDTH/16*(HEIGHT-13)-2];
  p6 =  &(*((short (*)[]) p2))[WIDTH/16*(HEIGHT-13)-1];
  p7 =  &(*((short (*)[]) p3))[WIDTH/16*(HEIGHT-13)-1];
  a = *p1;
  b = ((*p1 >> 1)&0x7fff) | ((*p4 << 15)&0x8000);
  c = ((*p1 << 1)&0xfffe) | ((*p0 >> 15)&0x0001);
  WaitBlit();
  *p2 = a^b^c;
  *p3 = (a&b) | (b&c) | (c&a);
  a = *p4;
  b = ((*p4 >> 1)&0x7fff) | ((*p5 << 15)&0x8000);
  c = ((*p4 << 1)&0xfffe) | ((*p1 >> 15)&0x0001);
  *p6 = a^b^c;
  *p7 = (a&b) | (b&c) | (c&a);
  DisownBlitter();
}


long StartLib()
{

  /* open graphics library */

  GfxBase = NULL;
  IntuitionBase = NULL;

  GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0);
  if(GfxBase == NULL)
  {
    ShowError("can't open graphics library");
    return(-1);
  }

  /* open intuition library */

  IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
  if(IntuitionBase == NULL)
  {
    ShowError("can't open intuition library");
    return(-1);
  }

  return(0);
}

void FinishLib()
{

  if( IntuitionBase != NULL )
    CloseLibrary( IntuitionBase );

  if( GfxBase != NULL )
    CloseLibrary( GfxBase );

}

struct IntuiText errortext = { 3,2,JAM1,8,8,&font,NULL,NULL };
struct IntuiText goontext = { 3,2,JAM1,4,4,&font,"continue",NULL };

void ShowError( errmesg )
char *errmesg;
{
  long count;
  long size;
  short openIntui;

  openIntui = (IntuitionBase == NULL);
  if(openIntui)
  {
     IntuitionBase = (struct IntuitionBase *)
                             OpenLibrary("intuition.library",0);
     if(IntuitionBase == NULL) return;
  }

  errortext.IText = errmesg;
  size = IntuiTextLength(&errortext)+38;
  if(size < 100) size = 100;
  if(size > 640) size = 640;
  count = AutoRequest(NULL,&errortext,NULL,&goontext,0,0,size,56);

  if(openIntui)
    CloseLibrary( (struct Library *)IntuitionBase );
}


long Random( seed )
long seed;
{
  long lo,hi,test;
  hi = seed/127773;
  lo = seed - hi*127773;
  test = 16807*lo - 2836*hi;
  if(test > 0)
    return(test);
  else
    return(test + 2147483647);
}

/* project menu items */

struct IntuiText quittext = { 30,20,JAM1,5,1,&font,"Quit",NULL };

struct MenuItem Quit = { NULL,
                         5,0,56,10,
                         ITEMTEXT|ITEMENABLED|HIGHBOX,
                         0,(APTR)&quittext,NULL,NULL,0
                       };

/* screen menu items */

struct IntuiText colourtext = { 30,20,JAM1,15,1,&font,"Colour",NULL };

struct MenuItem Colour =   { NULL,
                            5,30,66,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX|CHECKED|CHECKIT,
                            4,(APTR)&colourtext,NULL,NULL,0
                          };

struct IntuiText monotext = { 30,20,JAM1,15,1,&font,"Fast",NULL };

struct MenuItem Mono    = { &Colour,
                            5,20,66,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX|CHECKIT,
                            8,(APTR)&monotext,NULL,NULL,0
                          };

struct IntuiText cleartext = { 30,20,JAM1,5,1,&font,"Clear",NULL };

struct MenuItem Clear =   { &Mono,
                            15,10,56,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX,
                            0,(APTR)&cleartext,NULL,NULL,0
                          };

struct IntuiText randomtext = { 30,20,JAM1,5,1,&font,"Soup",NULL };

struct MenuItem Restart = { &Clear,
                            15,0,56,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX,
                            0,(APTR)&randomtext,NULL,NULL,0
                          };

/* Border menu items */

struct IntuiText deadtext = { 30,20,JAM1,15,1,&font,"Dead",NULL };

struct MenuItem Dead  =   { NULL,
                            5,30,66,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX|CHECKED|CHECKIT,
                            4,(APTR)&deadtext,NULL,NULL,0
                          };

struct IntuiText cyclictext = { 30,20,JAM1,15,1,&font,"Cyclic",NULL };

struct MenuItem Cyclic  = { &Dead,
                            5,20,66,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX|CHECKIT,
                            8,(APTR)&cyclictext,NULL,NULL,0
                          };

struct MenuItem Clean =   { &Cyclic,
                            15,10,56,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX,
                            0,(APTR)&cleartext,NULL,NULL,0
                          };

struct MenuItem random = { &Clean,
                            15,0,56,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX,
                            0,(APTR)&randomtext,NULL,NULL,0
                          };

/* mouse menu items */


short *image[8] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
short idat[8] = {0x0662,0x2662,0x2760,0x6ff6,0x4662,0x47e2,0x27e4,0x6666};

struct Image symimage[8];
struct Image symimage0 = { 13,2,16,16,1,NULL,1,1,NULL };

struct MenuItem symm[8];
struct MenuItem symm0   = { NULL,
                            60,0,32,20,
                            ITEMENABLED|HIGHBOX|CHECKIT,
                            6,NULL,NULL,NULL,NULL,MENUNULL
      /* symm[8] */        };

long symx[8] = { 254,253,251,247,239,223,191,127 };

struct IntuiText symmetrytext = { 30,20,JAM1,2,1,&font,"Symmetry",NULL };

struct MenuItem Symmetry = { NULL,
                            5,40,66,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX,
                            0,(APTR)&symmetrytext,NULL,
                            NULL,&symm[0],
                            0
    /* symmetry */        };

struct IntuiText singletext = { 30,20,JAM1,15,1,&font,"Single",NULL };

struct MenuItem Single  = { &Symmetry,
                            5,30,66,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX|CHECKED|CHECKIT,
                            4,(APTR)&singletext,NULL,NULL,0
   /* single */           };

struct IntuiText repeattext = { 30,20,JAM1,15,1,&font,"Repeat",NULL };

struct MenuItem Repeat  = { &Single,
                            5,20,66,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX|CHECKIT,
                            8,(APTR)&repeattext,NULL,NULL,0
   /* repeat */           };

struct IntuiText edittext = { 30,20,JAM1,15,1,&font,"Edit",NULL };

struct MenuItem Edit  =   { &Repeat,
                            5,10,66,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX|CHECKIT,
                            1,(APTR)&edittext,NULL,NULL,0
                          };

struct IntuiText randmstext = { 30,20,JAM1,15,1,&font,"Random",NULL };

struct MenuItem Randms  = { &Edit,
                            5,0,66,10,
                            ITEMTEXT|ITEMENABLED|HIGHBOX|CHECKED|CHECKIT,
                            2,(APTR)&randmstext,NULL,NULL,0
                          };

struct Menu Border  = { NULL,
                        238,0,76,12,
                        MENUENABLED,
                        "border",
                        &random
                      };

struct Menu Mouse  =  { &Border,
                        157,0,76,12,
                        MENUENABLED,
                        "mouse",
                        &Randms
                      };

struct Menu Screen =  { &Mouse,
                        76,0,76,12,
                        MENUENABLED,
                        "screen",
                        &Restart
                      };

struct Menu Project = { &Screen,
                        5,0,66,12,
                        MENUENABLED,
                        "project",
                        &Quit
                      };

void MenuInit()
{
  long i,j,k,l;
  short pick;
  short put;

  for(i=0;i<8;i++)
  {
    symm[i] = symm0;
    if(i<7)
      symm[i].NextItem = &symm[i+1];
    symm[i].ItemFill = (APTR) &symimage[i];
    symm[i].MutualExclude = symx[i];
    symimage[i] = symimage0;
    image[i] = (short *)AllocMem(32,MEMF_CHIP);
    symimage[i].ImageData = image[i];
    if(i<4)
    {
      symm[i].LeftEdge = 60;
      symm[i].TopEdge = i*20;
    }
    else
    {
      symm[i].LeftEdge = 92;
      symm[i].TopEdge = (i-4)*20;
    }
    for(j=0;j<16;j++)
      (*((short (*)[])image[i]))[j] = 0;
    pick = 0x8000;

    for(j=0;j<4;j++)
    {
      put = 0xf000;
      for(k=0;k<4;k++)
      {
        if( idat[i] & pick )
          for(l=0;l<4;l++)
            (*((short (*)[])image[i]))[j*4+l] |= put;
        pick = (pick >> 1)&0x7fff;
        put  = (put  >> 4)&0x0fff;
      }
    }
  }
  symm[0].Flags |= CHECKED;

  SetMenuStrip(window,&Project);
}

void MenuEnd()
{
  long i;
  ClearMenuStrip(window);
  for(i=0;i<8;i++)
    if(image[i] != NULL)
      FreeMem((char *)image[i],32);
}

void MenuServe()
{
  USHORT class,code;
  long i;
  for(;;)
  {
    WaitTOF();
    if(!window->UserPort->mp_SigBit) continue;
    message = (struct IntuiMessage *) GetMsg(window->UserPort);
    if(message != NULL)
    {
      class = message->Class;
      code = message->Code;
      ReplyMsg((struct Message *)message);

      if(class == MENUPICK)
      {
        for(;;)
        {
          if(MENUNUM(code) == 0)          /* project */
          {
            if(ITEMNUM(code) == 0)           /* quit */
            {
              finish = TRUE;
              code = Quit.NextSelect;
            }
          }
          else if(MENUNUM(code) == 1)     /* screen */
          {
            if(ITEMNUM(code) == 0)           /* restart */
            {
              RandomPlane( p );
              ClearEdge( p );
              code = Restart.NextSelect;
            }
            else if(ITEMNUM(code) == 1)      /* clear */
            {
              ClearPlane( p );
              code = Clear.NextSelect;
            }
            else if(ITEMNUM(code) == 3)      /* colour */
            {
              if(!colour)
              {
                CopyPlane( p, pl[6] );
                p = pl[6];
                colour = TRUE;
              }
              code = Colour.NextSelect;
            }
            else if(ITEMNUM(code) == 2)      /* fast */
            {
              if(colour)
              {
                for(i=0;i<DEPTH;i++)
                  ClearPlane(plane[i]);
                CopyPlane( p, plane[0] );
                p = plane[0];
                colour = FALSE;
              }
              code = Mono.NextSelect;
            }
          }
          else if(MENUNUM(code) == 3)     /* border */
          {
            if(ITEMNUM(code) == 0)           /* random */
            {
              RandomEdge( p );
              code = random.NextSelect;
            }
            else if(ITEMNUM(code) == 1)      /* clear */
            {
              ClearEdge( p );
              code = Clean.NextSelect;
            }
            else if(ITEMNUM(code) == 2)      /* cyclic */
            {
              cyclic = TRUE;
              code = Cyclic.NextSelect;
            }
            else if(ITEMNUM(code) == 3)      /* dead */
            {
              cyclic = FALSE;
              code = Dead.NextSelect;
            }
          }
          else if(MENUNUM(code) == 2)     /* mouse */
          {
            if(ITEMNUM(code) == 0)           /* random */
            {
              randommouse = TRUE;
              code = Randms.NextSelect;
            }
            else if(ITEMNUM(code) == 1)      /* edit */
            {
              randommouse = FALSE;
              ClearMenuStrip(window);
              ModifyIDCMP(window,0);
              CopyPlane( p, pl[2] );
              EditMouse();
              CopyPlane( pl[2], p );
              ModifyIDCMP(window,MENUVERIFY|MENUPICK|MOUSEBUTTONS);
              SetMenuStrip(window,&Project);
              code = Edit.NextSelect;
            }
            else if(ITEMNUM(code) == 2)      /* repeat */
            {
              repeat = TRUE;
              code = Repeat.NextSelect;
            }
            else if(ITEMNUM(code) == 3)      /* single */
            {
              repeat = FALSE;
              code = Single.NextSelect;
            }
            else if(ITEMNUM(code) == 4)      /* symmetry */
            {
              for(i=0;i<8;i++)
              {
                if(SUBNUM(code) == i)
                {
                  sym = i;
                  code = symm[i].NextSelect;
                }
              }
              SymmetricMouse();
            }
          }
          if(code == MENUNULL)break;
        }
        break;
      }
      else if(class == MOUSEBUTTONS)
      {
        if(code == SELECTDOWN)
          down = TRUE;
        else if(code == SELECTUP)
          down = FALSE;
      }
    }
  }
}

#define SIDE 10

struct NewWindow neweditwind={ 80,25,
                               (SIDE*16+8),(SIDE*16+15),
                               1,5,
                               MOUSEBUTTONS | CLOSEWINDOW,
                               SIMPLE_REFRESH | ACTIVATE |
                               GIMMEZEROZERO | WINDOWCLOSE,
                               NULL,NULL,
                               "",
                               NULL,NULL,0,0,0,0,
                               CUSTOMSCREEN
                             };

struct Window *editwindow = NULL;

void EditMouse()
{
  short bits[16];
  long i,j;
  long class,code;
  long x,y;

  neweditwind.Screen = screen;
  editwindow = OpenWindow(&neweditwind);
  if(editwindow == NULL)
  {
    ShowError("Edit Window will not open");
    return;
  }

  bits[0] = 1;

  for(i=1;i<16;i++)
    bits[i] = bits[i-1]<<1;

  SetAPen(editwindow->RPort,1);
  for(i=0;i<16;i++)
    for(j=0;j<16;j++)
      if(pic[j] & bits[15-i])
        RectFill(editwindow->RPort,i*SIDE,j*SIDE,(i+1)*SIDE-1,(j+1)*SIDE-1);

  for(;;)
  {
    Wait(1<<editwindow->UserPort->mp_SigBit);
    x = editwindow->GZZMouseX;
    y = editwindow->GZZMouseY;
    message = (struct IntuiMessage *) GetMsg(editwindow->UserPort);
    if(message != NULL)
    {
      class = message->Class;
      code = message->Code;
      ReplyMsg((struct Message *)message);
      if(class == CLOSEWINDOW)
        break;
      else if(class == MOUSEBUTTONS)
      {
        if(code == SELECTDOWN)
        {
          i = x/SIDE;
          j = y/SIDE;
          if(i>=0 && i<16 && j>=0 && j<16)
          {
            if(pic[j] & bits[15-i])
              SetAPen(editwindow->RPort,0);
            else
              SetAPen(editwindow->RPort,1);

            ChangeBit(i,j,&bits);
            if((sym == 1) || (sym == 3) || (sym == 7))
              ChangeBit(i,15-j,&bits);
            if((sym == 3) || (sym == 7))
              ChangeBit(15-i,j,&bits);
            if(((sym == 3) || (sym == 6)) && (i!=j))
              ChangeBit(j,i,&bits);
            if(((sym == 2) || (sym == 3) || (sym == 6)) && (i+j!=15))
              ChangeBit(15-j,15-i,&bits);
            if((sym == 5) || ((sym == 3) && (i!=j) && (i+j!=15)))
            {
              ChangeBit(j,15-i,&bits);
              ChangeBit(15-j,i,&bits);
            }
            if((((sym == 3) || (sym == 6)) && (i!=j) && (i+j!=15))||
                     (sym == 4) || (sym == 5) || (sym == 7))
              ChangeBit(15-i,15-j,&bits);

          }
        }
      }
    }
  }

  CloseWindow(editwindow);
}

void ChangeBit(i,j,bs)
long i,j;
short (*bs)[16];
{
  pic[j] ^= (*bs)[15-i];
  RectFill(editwindow->RPort,i*SIDE,j*SIDE,(i+1)*SIDE-1,(j+1)*SIDE-1);
}
