echo;  /* Self-compilation stuff ala John Toebes for Lattice users:
lc -M bdemon.c bdintui.c bdblit.c bdwin.c
blink with bdemon.with
quit

*****************************************************************************
*   BlitDemons by Walter Strickler
*   This program and all its source code are in the public domain and are
* freely distributable and usable for any purpose, private or commercial.
*
*   This program is a port of a program by Loren Blaney which runs under
* Apex, an unknown operating system on the Amiga.  It is based on the 
* cellular automaton called 'Demons' described in the August, 1989,
* issue of Scientific American.  
*
*   It is not my fault that it busy-waits.  This is usually inexcusable in
* an Amiga program, but it is the call to WaitBlit() that causes it.  It is
* really a shame, since this program should require only a small portion of
* the 68000.  As it is, it hogs all of it.
****************************************************************************/


#include <math.h>
#include "bdemon.h"


int main()
    { 
    int                 IntuiReturn,
                        IPRet,
                        RetVal;
    struct BDMainStruct BDStuff;

    IntuiReturn = InitIntui();
    if (IntuiReturn == INTUI_OK)
        {
        IPRet = InitPlanes(&BDStuff, BDWindow);
        if (IPRet != PLANES_CHOKE)
            {
            BDStuff.BDBlitNodes = InitBlits(&BDStuff);
            if (BDStuff.BDBlitNodes != NULL)
                {
                RetVal = MainLoop(&BDStuff);
                }
            else
                {
                RetVal = INIT_BLIT_CHOKE;
                }
            }   /* End BDStuff OK */
        else
            {
            RetVal = PLANES_CHOKE;        
            }      /* End BDStuff not OK */
        KillPlanes(&BDStuff);
        }   
    else  /* Single entry, single exit:  so there, Guy! */
        {
        RetVal = IntuiReturn;   
        }
    CloseIntui();
    ErrorMsgs(RetVal);
    return RetVal;
    }   /* End main() */


void ErrorMsgs(ErrorNum)
    int ErrorNum;
    {
    switch (ErrorNum)
        {
        case 0:
            break;  /* A-OK */
        case NO_INTUI:
            printf("Couldn't open Intuition!\n");
            break;
        case NO_GFX:
            printf("Couldn't open Graphics!\n");
            break;
        case NO_SCREEN:
            printf("Couldn't open the Screen.\n");
            break;
        case NO_WIN:
            printf("Couldn't open the Window.\n");
            break;
        case PLANES_CHOKE:
            printf("Couldn't allocate the temp. bit planes.\n");
            break;
        case ABOUT_CHOKE:
            printf("Couldn't open the about window.\n");            
            break;
        case INIT_BLIT_CHOKE:
            printf("Couldn't allocate the blitter nodes.\n");
            break;
        case WBI_CHOKE:
            printf("Couldn't start the blitter interrupt.\n");
            break;
        default:
            printf("Unknown error: %d\n", ErrorNum); 
            break;
        }
    }


/************************************************************************/
int MainLoop(BDSchtoff)
    struct BDMainStruct  *BDSchtoff;

    {
    int RetVal,
        LeavingNow,
        MsgVal,
        MsgControl,
        RandReturn,
        First;

    RetVal = ML_OK;
    LeavingNow = FALSE;
    MsgControl = NO_WAIT;
    First = TRUE;
    while (LeavingNow == FALSE) 
        {
        if (First)
            {
            First = FALSE;  /* Do this once only */
            MsgVal = NEW;  /* Hotwire MsgVal on first time through */
            }   /* End First */
        else
            {
            if (MsgControl == NO_WAIT) /* If we do WAIT, hurry up and wait! */
                {
                OneGen(BDSchtoff);
                }
            MsgVal = CheckMsg(MsgControl);  
            }   /* End not First */

        switch (MsgVal)
            {
            case CLOSE_WIN:
            case QUIT:
                LeavingNow = TRUE;
                break;
            case NEW:
                RandReturn = Randomize(BDSchtoff, BDWindow);
                switch (RandReturn)
                    {
                    case RAND_OK:
                        OffStart();          /* Set menu items */
                        OnStop();
                        MsgControl = NO_WAIT;
                        break;
                    case RAND_QUIT:
                        LeavingNow = TRUE;
                        break;
                    case RAND_ABOUT_CHOKE:
                        LeavingNow = TRUE;
                        RetVal = ABOUT_CHOKE;
                        break;
                    }
                break;
            case STOP:
                MsgControl = WAIT;
                OffStop();
                OnStart();
                break;
            case START:
                MsgControl = NO_WAIT;
                OffStart();
                OnStop();
                break;
            case ABOUT:
                if (DisplayAbout() == DA_CHOKE)
                    {
                    LeavingNow = TRUE;
                    RetVal = ABOUT_CHOKE;
                    }
                break;
            case NO_MSG:
                /* One more generation. */
                break;
            default:
                assert (FALSE);  /* Shouldn't be here */
                break;
            }   /* End case CheckMsg() */
        }  /* End while not leaving */
    return RetVal;
    }   /* End MainLoop() */



/*******************************************************************************
     InitPlanes() fills in a structure of type BDMainStruct, pointed at by
   Strcut, given the pointer to a window, *Win.  Returns PLANES_CHOKE if any 
   allocations weren't successful.
*******************************************************************************/
int InitPlanes(Struct, Win)
    struct BDMainStruct *Struct;
    struct Window *Win;

    {
    int i,
        DispStartOffset,
        RetVal;

    RetVal = PLANES_OK;
    /* Find the relative start of the blitfield in words */
    /* Note that BorderTop and BorderBottom are 1 too large */
    DispStartOffset = (((Win -> BorderTop) - 1 ) + Win -> TopEdge)
     * ((Win -> WScreen -> Width) / 16) + (Win -> LeftEdge) / 16;
    for (i=0; i < NUM_PLANES; i++)
        {
        (Struct -> Display)[i] =  DispStartOffset +
         (WORD *) Win -> RPort -> BitMap -> Planes[i];  /* Whew! */
        }
    Struct -> XSize = Win -> Width;
    Struct -> YSize = (Win -> Height) - ((Win -> BorderTop) - 1) - 
     ((Win -> BorderBottom) - 1);     /* Sub. borders */
    Struct -> Mod = (Win -> WScreen -> Width) - (Win -> Width);/* Blit Modulo */
    /* Note that BorderLeft and BorderRight are 2 too large */
    Struct -> LRBorder = max (Win -> BorderLeft - 2, Win -> BorderLeft - 2);

    /* Now allocate some bitplanes for temporaries... */
    Struct -> Temp = (WORD *) AllocRaster(Struct -> XSize, Struct -> YSize);
    if (Struct -> Temp == 0)
        {
        RetVal = PLANES_CHOKE;
        }    /* End Temp Choke */
    else
        {
        Struct -> Equal = (WORD *)AllocRaster(Struct -> XSize, Struct -> YSize);
        if (Struct -> Equal == 0)
            {
            RetVal = PLANES_CHOKE;
            }  /* End Equal choke */
        else
            {
            i = 0;
            while ((RetVal != PLANES_CHOKE) && (i < NUM_PLANES))
                {
                Struct -> Incr[i] = (WORD *) AllocRaster(Struct -> XSize, 
                 Struct -> YSize);
                if (Struct -> Incr[i] == 0)
                    {
                    RetVal = PLANES_CHOKE;
                    }
                i++;
                }  /* End While allocating Incr */
	    }    /* End Equal OK */
        }  /* End Temp OK */
    return RetVal;
    } /* End InitPlanes */


/*******************************************************************************
     KillPlanes() deallocates all of the temporary bitplanes allocated
   in InitPlanes.
*******************************************************************************/

void KillPlanes(Struct)
    struct BDMainStruct *Struct;

    {
    int i;
    
    if (Struct -> Temp != 0)
        {
        FreeRaster((PLANEPTR)Struct -> Temp, Struct -> XSize, Struct -> YSize);
        Struct -> Temp = 0;
        }
    if (Struct -> Equal != 0)
        {
        FreeRaster((PLANEPTR)Struct -> Equal, Struct -> XSize, Struct -> YSize);
        Struct -> Equal = 0;
        }
    for (i = 0; i < NUM_PLANES; i++)
        {
        if (Struct -> Incr[i] != 0)
            {
            FreeRaster((PLANEPTR) Struct -> Incr[i], Struct -> XSize, 
             Struct -> YSize);
            Struct -> Incr[i] = NULL;
            }
        }
    }  /* End KillPlanes() */



/*******************************************************************************
    Randomize() sets every pixel within the borders of the window pointed
   at by Win.
*******************************************************************************/
int Randomize(BDStuff, Win)
    struct BDMainStruct *BDStuff;
    struct Window       *Win;

    {
    int  Left, 
         Right, 
         Top, 
         Bottom, 
         XWords,
         YLines,
         i,
         x, 
         y,
         Redo,
         MsgControl,
         MsgVal,
         LeavingNow,
         PlaneNum,
         RetVal;
    WORD LRBorder,
         LMask,
         RMask,
       *(Disp[NUM_PLANES]),
         MyWord,
         RandWord;
    long Seed;
    struct DateStamp *Now;

    MsgControl = NO_WAIT;
    RetVal = RAND_OK;   /* Innocent until proven guilty */
    LeavingNow = FALSE;
    do  /* Loop if Redo gets set to TRUE by the NEW menuitem. */
        {
        Redo = FALSE;  /* Only one more time unless Redo gets set */
        /* Seed the random number generator with number of TICK's today: */
        Now = (struct DateStamp *)  malloc (sizeof (struct DateStamp));
        DateStamp(Now);  /* This Lattice compiler warning is not my fault */
        Seed = (Now -> ds_Tick) + ((Now -> ds_Minute) * TICKS_PER_SECOND * 60);
        free(Now);
        srand(Seed);
        
        /* Set the border masks */
        LRBorder = BDStuff -> LRBorder;
        LMask = 0xffff >> LRBorder;
        RMask = 0xffff << LRBorder;

        /* Set the plane pointers */
        for (i=0; i < NUM_PLANES; i++)
            {
            Disp[i] = BDStuff -> Display[i];
            }        
        
        /* Clear the screen first */
        Left = LRBorder;
        Right = ((BDStuff -> XSize) -1) - LRBorder;
        Top = (BDWindow -> BorderTop) -1;  /* Border off by one */
        Bottom = (BDWindow -> Height) - (BDWindow -> BorderBottom);
        SetAPen (BDWindow -> RPort, 0);
        RectFill (BDWindow -> RPort, Left, Top, Right, Bottom);

        /* Now set pixels Randomly */
        /* Sweep from top to bottom; bail out if Redo or LeavingNow gets set */
        XWords = (BDStuff -> XSize) / 16;
        YLines = (BDStuff -> YSize);
        y = 0;
        while ((y < YLines) && (!Redo) && (!LeavingNow))  
            {
            for (x = 0; x < XWords; x++)
                {
                for (PlaneNum = 0; PlaneNum < NUM_PLANES; PlaneNum++)
                    {
                    RandWord = (rand() >> 8) & 0xffff; /* Use middle 16 bits */
                    /* Set MyWord given LMask and RMask */    
                    if (x == 0)    /* First word */
                        {
                        MyWord = *(Disp[PlaneNum]) & ~LMask;
                        MyWord = MyWord | (RandWord & LMask);
                        }
                    else if (x == (XWords -1))  /* Last word */
                        {
                        MyWord = *(Disp[PlaneNum]) & ~RMask;
                        MyWord = MyWord | (RandWord & RMask);
                        }
                    else
                        { 
                        MyWord = RandWord;
                        }
                    *(Disp[PlaneNum]) = MyWord;
                    (Disp[PlaneNum])++;
                    }  /* End for PlaneNum */
                }  /* End for x */
            MsgVal = CheckMsg(MsgControl);
            switch(MsgVal)
                {
                case CLOSE_WIN:
                case QUIT:
                    RetVal = RAND_QUIT;
                    LeavingNow = TRUE;
                    break;
                case STOP:
                    MsgControl = WAIT;
                    OffStop();
                    OnStart();
                    break;
                case START:
                    MsgControl = NO_WAIT;
                    OffStart();
                    OnStop();
                    break;
                case NEW:
                    Redo = TRUE;
                    MsgControl = NO_WAIT;
                    OffStart();
                    OnStop();
                    break;
                case ABOUT:
                    if (DisplayAbout() == DA_CHOKE)
                        {
                        LeavingNow = TRUE;   /* Let's bomb out now */
                        RetVal = RAND_ABOUT_CHOKE;
                        }
                    break;
                case NO_MSG:
                    break;
                default:
                    assert (FALSE);  /* Shouldn't be here */
                    break;
                }
            y++;  /* Next line */
            }  /* End for y */
        } while (Redo);
    return RetVal;
    }    /* End Randomize() */
