/*
 *  Copyright (c) 1997 Ingo Schmiegel
 *  All rights reserved.
 *
 */

#include <exec/memory.h>
#include "/includes.h"
#include <clib/intuition_protos.h>
#include <intuition/intuitionbase.h>
#include <math.h>
#include <time.h>

#define PREFS_UPDATE  0                 /* update the pieces or simple shuffle */
#define PREFS_HORIZ   2                 /* number of columns in the puzzle */
#define PREFS_VERT    4                 /* number of rows in the puzzle */
#define PREFS_DIVIS   6                 /* number of steps for one shuffle movement */
#define PREFS_HOLE    8                 /* hole filling mode */
#define PREFS_BORDER  10                /* draw border around the pieces? */
#define PREFS_DELAY   12                /* delay between two such steps */
#define PREFS_SCREEN  14                /* select from which screen cloning should take place */
#define PREFS_FADEPCT 16                /* percent of brightness of the original colours */

#define PREFS_UPDATE_YES        0
#define PREFS_UPDATE_NO         1
#define PREFS_HOLE_BACKGROUND   0
#define PREFS_HOLE_ONCERANDOM   1
#define PREFS_HOLE_RANDOM       2
#define PREFS_BORDER_YES        0
#define PREFS_BORDER_NO         1
#define PREFS_SCREEN_FRONT      0
#define PREFS_SCREEN_PUBLIC     1

#include "LivingShuffle_rev.h"
STATIC const UBYTE VersTag[] = VERSTAG;

/* maximum puzzle pieces */
#define     PUZZLE_MAX_ROWS     50
#define     PUZZLE_MAX_COLUMNS  50
/* minimum piece size (rows and columns values will be adjusted) (MUST BE AT LEAST 4!!) */
#define     PIECE_MIN_WIDTH     8
#define     PIECE_MIN_HEIGHT    8

/*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*
 *  Piece           - describing one piece of the puzzle
 *°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*/
typedef struct _Piece
{
    long    srcx;     /* source topleft coordinate (in columns and rows) */
    long    srcy;     /* the destination coordinate is its position in the puzzle array */
} Piece;

/*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*
 *  Puzzle           - describing the puzzle
 *°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*/
enum   _direction   { up, down, left, right };
typedef enum _direction     direction;

typedef struct _Puzzle
{
    long    columns;  /* Size (columns,rows) */
    long    rows;
    long    steps;    /* steps to do during one piece movement */
    long    pw;       /* Size of one piece in pixels */
    long    ph;

    struct Screen *srcscr;   /* source screen (only screen pointer here for SAFETY!) */
    struct RastPort *destrp;  /* destination screen's rastport */

    Piece   pieces[PUZZLE_MAX_ROWS][PUZZLE_MAX_COLUMNS]; /* Array of pieces */

    BOOL    doborder;

                /* these values are updated before a new shuffle is started */
    long    hx;     /* hole position    */
    long    hy;
    long    step;   /* actual step */
    long    from;   /* direction of actual movement (fill hole with piece from <from> direction) */
} Puzzle;

/************************************************************************************************/
/************************************************************************************************/
/************************************************************************************************/

Puzzle  puzzle;

const long  ColBack = 0;        /* Background colour */
const long  ColDarkEdge = 1;    /* Colour of the bottom right dark edges */
const long  ColBrightEdge = 2;  /* Colour of the top left bright edges */
      long  ColHole;            /* Is once (or more often) randomly selected by main() */
/************************************************************************************************/
/************************************************************************************************/
/************************************************************************************************/

void Defaults(PrefObject *Prefs)
{
    Prefs[PREFS_UPDATE].po_Level = PREFS_UPDATE_YES;
    Prefs[PREFS_HORIZ].po_Level = 10;
    Prefs[PREFS_VERT].po_Level = 8;
    Prefs[PREFS_DIVIS].po_Level = 10;
    Prefs[PREFS_HOLE].po_Active = PREFS_HOLE_BACKGROUND;
    Prefs[PREFS_BORDER].po_Active = PREFS_BORDER_YES;
    Prefs[PREFS_DELAY].po_Level = 1;
    Prefs[PREFS_SCREEN].po_Active = PREFS_SCREEN_FRONT;
    Prefs[PREFS_FADEPCT].po_Level = 25;
}

/*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*
 * OpenLibs          "Open all needed libraries"
 *
 * Result:      TRUE or FALSE
 *
 * Function:    Opens the needed libraries
 *°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*/
BOOL OpenLibs(void)
{
    return TRUE;
}
/*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*
 * CloseLibs          "Close all needed libraries"
 *
 * Function:    Closes all opened libraries
 *°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*/
void CloseLibs(void)
{
}
/*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*
* RndBT                (macro)         "Random from Bottom to Top"
*
* Parameters:  min     The minimum of possible random numbers (any of: int,char,word,long; no floats!)
*              max     The maximum of possible random numbers (dito)
*              (Do not use negative numbers!)
*
* Result:      long    A random number
*
* Function:    Returns a random number in the interval of [min..max] (both included)
*
* Note:        Call srand48() once before using this function to set a seek value,
*              e.g.: srand48(time(NULL))
*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*/
#define RndBT(min,max)  ((min)+(lrand48()%((max)-(min)+1)))


/*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*
 * DrawRectBorder           "Draw the border of one rectangle"
 *
 * Parameters:  left edge, top edge, right edge, bottom edge
 *
 * Result:      none
 *
 * Function:    Draws a frame around a rectangle, light shines from the top left
 *°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*/
void DrawRectBorder (long xl, long yt, long xr, long yb)
{
    SetAPen(puzzle.destrp,ColDarkEdge);
    Move(puzzle.destrp,xr,yt);
    Draw(puzzle.destrp,xr,yb);
    Draw(puzzle.destrp,xl,yb);
    SetAPen(puzzle.destrp,ColBrightEdge);
    Draw(puzzle.destrp,xl,yt);
    Draw(puzzle.destrp,xr,yt);
}
/*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*
 * getNewDir           "Get a new direction"
 *
 * Parameters:  none
 *
 * Result:      none
 *
 * Function:    sets the direction field in the puzzle, so that the next shuffle movement
 *              can be done.
 *°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*/
void getNewDir(void)
{            /*  pos of hole ,    grid dimensions  , last movemetn direction */
    direction dir;
    for(;;)
    {
        dir=(direction)RndBT(0,3);
        switch (dir)
        {
            case left:  if (puzzle.hx == 0)       break;              /* try again */
                        if (puzzle.from == right) break;              /* try again */
                        puzzle.from = dir;        return;
            case right: if (puzzle.hx == (puzzle.columns-1)) break;   /* try again */
                        if (puzzle.from == left)             break;   /* try again */
                        puzzle.from = dir;                   return;
            case up:    if (puzzle.hy == 0)      break;               /* try again */
                        if (puzzle.from == down) break;               /* try again */
                        puzzle.from = dir;       return;
            case down:  if (puzzle.hy == (puzzle.rows-1)) break;      /* try again */
                        if (puzzle.from == up)            break;      /* try again */
                        puzzle.from = dir;                return;
        }
    }
}
/*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*
 * initPuzzle        "Initialize the puzzle"
 *
 * Parameters:
 *
 * Result:      none
 *
 * Function:    Initializes all fields
 *
 * Note:    Ever call this function with valid screen pointers !!!
 *°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*/
void initPuzzle (struct Screen *src, struct Screen *dest, long columns, long rows, long steps)
{
    long    r,c;

    puzzle.columns = columns;
    puzzle.rows = rows;
    puzzle.steps = steps;
    puzzle.pw = dest->Width / columns;
    puzzle.ph = dest->Height / rows;

    puzzle.srcscr = src;
    puzzle.destrp = &(dest->RastPort);

    puzzle.hx = RndBT(0,columns-1);
    puzzle.hy = RndBT(0,rows-1);
    puzzle.step = 0;
    puzzle.from = (direction)RndBT(0,3);
    getNewDir();  /* to do bordercorrections */

    /* Initialize the pieces */
    for (r=0;r<rows;r++)
    {
        for (c=0;c<columns;c++)
        {
            puzzle.pieces[r][c].srcx = c;
            puzzle.pieces[r][c].srcy = r;
            if (puzzle.doborder)
                DrawRectBorder( c*puzzle.pw, r*puzzle.ph, (c+1)*puzzle.pw-1, (r+1)*puzzle.ph-1 );
        }
    }
    if (puzzle.doborder)
    {
        /* Now draw a nice right border puzzle (which isn't shuffled, because it's too small) */
        if ((columns*puzzle.pw) < (dest->Width-2))
        {   /* enough room for a nice right border */
            for (r=0;r<rows;r++)
                DrawRectBorder(columns*puzzle.pw, r*puzzle.ph,
                               dest->Width-1,     (r+1)*puzzle.ph-1);
        }
        else if ((columns*puzzle.pw) < dest->Width)
        {   /* not enough room for a nice right border but a background strip */
            SetAPen(puzzle.destrp,ColBack);
            RectFill(puzzle.destrp,
                     columns*puzzle.pw, 0,
                     dest->Width-1,     dest->Height-1);
        }
        /* Now draw a nice bottom border puzzle (which isn't shuffled, because it's too small) */
        if ((rows*puzzle.ph) < (dest->Height-2))
        {   /* enough room for a nice bottom border */
            for (c=0;c<columns;c++)
                DrawRectBorder(c*puzzle.pw,       rows*puzzle.ph,
                               (c+1)*puzzle.pw-1, dest->Height-1);
            /* test if the bottom right edge can be drawn with a border */
            if ((columns*puzzle.pw) < (dest->Width-2))
            {   /* enough room for a nice bottom right border */
                DrawRectBorder(columns*puzzle.pw, rows*puzzle.ph,
                               dest->Width-1,     dest->Height-1);
            }
        }
        else if ((rows*puzzle.ph) < dest->Height)
        {   /* not enough room for a nice bottom border but a background strip */
            SetAPen(puzzle.destrp,ColBack);
            RectFill(puzzle.destrp,
                     0,             rows*puzzle.ph,
                     dest->Width-1, dest->Height-1);
        }
    }

    /* At last clear the hole piece */
    SetAPen(puzzle.destrp,ColHole);
    RectFill(puzzle.destrp, puzzle.hx*puzzle.pw, puzzle.hy*puzzle.ph,
                            (puzzle.hx+1)*puzzle.pw-1, (puzzle.hy+1)*puzzle.ph-1);
}
/*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*
 * doStep        "Do one step of movement"
 *
 * Parameters:  none
 *
 * Result:      none
 *
 * Function:    Redraws the whole puzzle with one step of movement done
 *°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*/
void doStep (void)
{
    long  r,c;      /* row, column counter */
    long  px,py;    /* piece which is currently moving */
    long  dx,dy;             /* piece's moving offset */
    long  pxl,pxr,pyt,pyb;   /* dest coordinates */

    BOOL  onemovecompleted = FALSE;

    switch (puzzle.from)
    {
        case down:  dx = 0;
                    dy = - puzzle.ph * puzzle.step / puzzle.steps;
                    px = puzzle.hx;
                    py = puzzle.hy+1;
                    break;
        case up:    dx = 0;
                    dy = puzzle.ph * puzzle.step / puzzle.steps;
                    px = puzzle.hx;
                    py = puzzle.hy-1;
                    break;
        case right: dx = - puzzle.pw * puzzle.step / puzzle.steps;
                    dy = 0;
                    px = puzzle.hx+1;
                    py = puzzle.hy;
                    break;
        case left:  dx = puzzle.pw * puzzle.step / puzzle.steps;
                    dy = 0;
                    px = puzzle.hx-1;
                    py = puzzle.hy;
                    break;
    }

    for (r=0;r<puzzle.rows;r++)
    {
        for (c=0;c<puzzle.columns;c++)
        {
            if ((r == puzzle.hy) && (c == puzzle.hx))                                       /* HOLE */
            {
                /* nothing to do, 'cause the hole is slowly filled by the moving piece */
            }
            else
            {
                if ((r == py) && (c == px))                                                 /* MOVE */
                {
                    /* update the moving piece */
                    BltBitMap(puzzle.srcscr->RastPort.BitMap,
                               (puzzle.pieces[r][c].srcx)*puzzle.pw,
                               (puzzle.pieces[r][c].srcy)*puzzle.ph,
                              puzzle.destrp->BitMap,
                               pxl = c*puzzle.pw+dx,
                               pyt = r*puzzle.ph+dy,
                              puzzle.pw,
                              puzzle.ph,
                              0xc0,0xff,NULL);

                    if (puzzle.doborder)
                    {
                        pxr = pxl + puzzle.pw-1;
                        pyb = pyt + puzzle.ph-1;
                        DrawRectBorder(pxl,pyt,pxr,pyb);  /* draw the border of this piece */
                    }
                    /* fill the new hole (erase the old piece data there) */
                    if (dx || dy)
                    {
                        long    xtl,ytl,xbr,ybr;    /* new hole's rectangular coordinates */
                        switch (puzzle.from)
                        {
                            case left:  xtl = c * puzzle.pw;
                                        xbr = c * puzzle.pw + dx - 1;
                                        ytl = r * puzzle.ph;
                                        ybr = r * puzzle.ph + puzzle.ph - 1;
                                        break;
                            case right: xtl = c * puzzle.pw + puzzle.pw + dx;
                                        xbr = c * puzzle.pw + puzzle.pw - 1;
                                        ytl = r * puzzle.ph;
                                        ybr = r * puzzle.ph + puzzle.ph - 1;
                                        break;
                            case up:    ytl = r * puzzle.ph;
                                        ybr = r * puzzle.ph + dy - 1;
                                        xtl = c * puzzle.pw;
                                        xbr = c * puzzle.pw + puzzle.pw - 1;
                                        break;
                            case down:  ytl = r * puzzle.ph + puzzle.ph + dy;
                                        ybr = r * puzzle.ph + puzzle.ph - 1;
                                        xtl = c * puzzle.pw;
                                        xbr = c * puzzle.pw + puzzle.pw - 1;
                                        break;
                        }
                        SetAPen(puzzle.destrp,ColHole);
                        RectFill(puzzle.destrp,xtl,ytl,xbr,ybr);
                    }

                    if (++puzzle.step > puzzle.steps)   onemovecompleted = TRUE;
                }
                else                                                                        /* STILL */
                {
                    if (puzzle.doborder)
                    {
                        /* do a simple actualizing (first without border) */
                        BltBitMap(puzzle.srcscr->RastPort.BitMap,           /* source bitmap */
                                  (puzzle.pieces[r][c].srcx)*puzzle.pw+1,
                                  (puzzle.pieces[r][c].srcy)*puzzle.ph+1,   /* src coord */
                                  puzzle.destrp->BitMap,                    /* dest bitmap */
                                  (pxl=c*puzzle.pw)+1,
                                  (pyt=r*puzzle.ph)+1,                      /* dest coord */
                                  puzzle.pw-2,
                                  puzzle.ph-2,                              /* piece size without border */
                                  0xc0,0xff,NULL);                          /* minterm, planemask,tempbuf */
                        /* now add the border */
                        pxr = pxl + puzzle.pw -1;
                        pyb = pyt + puzzle.ph -1;
                        DrawRectBorder(pxl,pyt,pxr,pyb);
                    }
                    else
                    {
                        /* do a simple actualizing */
                        BltBitMap(puzzle.srcscr->RastPort.BitMap,           /* source bitmap */
                                  (puzzle.pieces[r][c].srcx)*puzzle.pw,
                                  (puzzle.pieces[r][c].srcy)*puzzle.ph,     /* src coord */
                                  puzzle.destrp->BitMap,                    /* dest bitmap */
                                  c*puzzle.pw,
                                  r*puzzle.ph,                              /* dest coord */
                                  puzzle.pw,
                                  puzzle.ph,                                /* piece size */
                                  0xc0,0xff,NULL);                          /* minterm, planemask,tempbuf */
                    }
                }
            }
        }
    }
    if (onemovecompleted)
    {   /* swap hole and last moving piece */
        puzzle.pieces[puzzle.hy][puzzle.hx].srcx =  puzzle.pieces[py][px].srcx;
        puzzle.pieces[puzzle.hy][puzzle.hx].srcy =  puzzle.pieces[py][px].srcy;
        switch (puzzle.from)
        {
            case down:  puzzle.hy++; break;
            case up:    puzzle.hy--; break;
            case left:  puzzle.hx--; break;
            case right: puzzle.hx++; break;
        }
        /* select new direction */
        getNewDir();
        puzzle.step = 0;
    }
}
/*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*
 * initSimplePuzzle        "Initialize only the parts of the puzzle needed for simple shuffling"
 *
 * Parameters:
 *
 * Result:      none
 *
 * Function:    Initializes all fields
 *
 * Note:    Ever call this function with valid screen pointers !!!
 *°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*/
void initSimplePuzzle (struct Screen *dest, long columns, long rows, long steps)
{
    long    r,c;

    puzzle.columns = columns;
    puzzle.rows = rows;
    puzzle.steps = steps;
    puzzle.pw = dest->Width / columns;
    puzzle.ph = dest->Height / rows;

    puzzle.srcscr = NULL;
    puzzle.destrp = &(dest->RastPort);

    puzzle.hx = RndBT(0,columns-1);
    puzzle.hy = RndBT(0,rows-1);
    puzzle.step = 0;
    puzzle.from = (direction)RndBT(0,3);
    getNewDir();  /* to do bordercorrections */

    if (puzzle.doborder)
    {
        /* Draw the pieces' border */
        for (r=0;r<rows;r++)
            for (c=0;c<columns;c++)
                DrawRectBorder( c*puzzle.pw, r*puzzle.ph, (c+1)*puzzle.pw-1, (r+1)*puzzle.ph-1 );
        /* Now draw a nice right border puzzle (which isn't shuffled, because it's too small) */
        if ((columns*puzzle.pw) < (dest->Width-2))
        {   /* enough room for a nice right border */
            for (r=0;r<rows;r++)
                DrawRectBorder(columns*puzzle.pw, r*puzzle.ph,
                               dest->Width-1,     (r+1)*puzzle.ph-1);
        }
        else if ((columns*puzzle.pw) < dest->Width)
        {   /* not enough room for a nice right border but a background strip */
            SetAPen(puzzle.destrp,ColBack);
            RectFill(puzzle.destrp,
                     columns*puzzle.pw, 0,
                     dest->Width-1,     dest->Height-1);
        }
        /* Now draw a nice bottom border puzzle (which isn't shuffled, because it's too small) */
        if ((rows*puzzle.ph) < (dest->Height-2))
        {   /* enough room for a nice bottom border */
            for (c=0;c<columns;c++)
                DrawRectBorder(c*puzzle.pw,       rows*puzzle.ph,
                               (c+1)*puzzle.pw-1, dest->Height-1);
            /* test if the bottom right edge can be drawn with a border */
            if ((columns*puzzle.pw) < (dest->Width-2))
            {   /* enough room for a nice bottom right border */
                DrawRectBorder(columns*puzzle.pw, rows*puzzle.ph,
                               dest->Width-1,     dest->Height-1);
            }
        }
        else if ((rows*puzzle.ph) < dest->Height)
        {   /* not enough room for a nice bottom border but a background strip */
            SetAPen(puzzle.destrp,ColBack);
            RectFill(puzzle.destrp,
                     0,             rows*puzzle.ph,
                     dest->Width-1, dest->Height-1);
        }
    }

    /* At last clear the hole piece */
    SetAPen(puzzle.destrp,ColHole);
    RectFill(puzzle.destrp, puzzle.hx*puzzle.pw, puzzle.hy*puzzle.ph,
                            (puzzle.hx+1)*puzzle.pw-1, (puzzle.hy+1)*puzzle.ph-1);
}
/*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*
 * doSimpleStep   "Do one step of movement just shuffling no updating"
 *
 * Parameters:  none
 *
 * Result:      none
 *
 * Function:    Redraws the whole puzzle with one step of movement done
 *°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*°*/
void doSimpleStep (void)
{
    long  px,py;    /* piece which is currently moving */
    long  dx,dy;             /* piece's moving offset */
    long  olddx,olddy;       /* piece's last moving offset */
    long  pxl,pxr,pyt,pyb;   /* dest coordinates */

    switch (puzzle.from)
    {
        case down:  dx = 0;
                    dy = - puzzle.ph * puzzle.step / puzzle.steps;
                    olddx = 0;
                    if (puzzle.step) olddy = - puzzle.ph * (puzzle.step-1) / puzzle.steps;
                    else             olddy = 0;
                    px = puzzle.hx;
                    py = puzzle.hy+1;
                    break;
        case up:    dx = 0;
                    dy = puzzle.ph * puzzle.step / puzzle.steps;
                    olddx = 0;
                    if (puzzle.step) olddy = puzzle.ph * (puzzle.step-1) / puzzle.steps;
                    else             olddy = 0;
                    px = puzzle.hx;
                    py = puzzle.hy-1;
                    break;
        case right: dx = - puzzle.pw * puzzle.step / puzzle.steps;
                    dy = 0;
                    if (puzzle.step) olddx = - puzzle.pw * (puzzle.step-1) / puzzle.steps;
                    else             olddx = 0;
                    olddy = 0;
                    px = puzzle.hx+1;
                    py = puzzle.hy;
                    break;
        case left:  dx = puzzle.pw * puzzle.step / puzzle.steps;
                    dy = 0;
                    if (puzzle.step) olddx = puzzle.pw * (puzzle.step-1) / puzzle.steps;
                    else             olddx = 0;
                    olddy = 0;
                    px = puzzle.hx-1;
                    py = puzzle.hy;
                    break;
    }
    /* update the moving piece */
    BltBitMap(puzzle.destrp->BitMap, px*puzzle.pw+olddx, py*puzzle.ph+olddy,
              puzzle.destrp->BitMap, pxl = px*puzzle.pw+dx, pyt = py*puzzle.ph+dy,
              puzzle.pw, puzzle.ph,
              0xc0,0xff,NULL);

    if (puzzle.doborder)
    {
        pxr = pxl + puzzle.pw-1;
        pyb = pyt + puzzle.ph-1;
        DrawRectBorder(pxl,pyt,pxr,pyb);  /* draw the border of this piece */
    }

    /* fill the new hole (erase the old piece data there) */
    if (dx || dy)
    {
        long    xtl,ytl,xbr,ybr;    /* new hole's rectangular coordinates */
        switch (puzzle.from)
        {
            case left:  xtl = px * puzzle.pw;
                        xbr = px * puzzle.pw + dx - 1;
                        ytl = py * puzzle.ph;
                        ybr = py * puzzle.ph + puzzle.ph - 1;
                        break;
            case right: xtl = px * puzzle.pw + puzzle.pw + dx;
                        xbr = px * puzzle.pw + puzzle.pw - 1;
                        ytl = py * puzzle.ph;
                        ybr = py * puzzle.ph + puzzle.ph - 1;
                        break;
            case up:    ytl = py * puzzle.ph;
                        ybr = py * puzzle.ph + dy - 1;
                        xtl = px * puzzle.pw;
                        xbr = px * puzzle.pw + puzzle.pw - 1;
                        break;
            case down:  ytl = py * puzzle.ph + puzzle.ph + dy;
                        ybr = py * puzzle.ph + puzzle.ph - 1;
                        xtl = px * puzzle.pw;
                        xbr = px * puzzle.pw + puzzle.pw - 1;
                        break;
        }
        SetAPen(puzzle.destrp,ColHole);
        RectFill(puzzle.destrp,xtl,ytl,xbr,ybr);
    }

    if (++puzzle.step > puzzle.steps)
    {   /* move hole */
        switch (puzzle.from)
        {
            case down:  puzzle.hy++; break;
            case up:    puzzle.hy--; break;
            case left:  puzzle.hx--; break;
            case right: puzzle.hx++; break;
        }
        /* select new direction */
        getNewDir();
        puzzle.step = 0;
    }
}
/************************************************************************************************/
/************************************************************************************************/
/************************************************************************************************/

LONG Blank( PrefObject *Prefs )
{
    long RetVal, ToFrontCount = 0;

    struct Screen *srcscr,*destscr;
    struct Window *win;

    long Rows,Columns,Steps,StepDelay;

    long i,delay;

    Columns   = Prefs[PREFS_HORIZ].po_Level;
    Rows      = Prefs[PREFS_VERT].po_Level;
    Steps     = Prefs[PREFS_DIVIS].po_Level;
    StepDelay = Prefs[PREFS_DELAY].po_Level;

    srand48(time(NULL));    /* Set seek value for Unix's lrand48() function */
                            /* lrand48() provides a number from 0..2**31    */
    
    if (OpenLibs())
    {
        if (Prefs[PREFS_UPDATE].po_Active == PREFS_UPDATE_YES)
        {
            /* get source screen pointer */
            if (Prefs[PREFS_SCREEN].po_Active==PREFS_SCREEN_PUBLIC)
            {
                srcscr = LockPubScreen(0L);
                UnlockPubScreen(0L,srcscr);
            }
            else
            if (Prefs[PREFS_SCREEN].po_Active==PREFS_SCREEN_FRONT)
            {
                ULONG lock = LockIBase(0);
                srcscr = IntuitionBase->FirstScreen;
                UnlockIBase(lock);
            }
            else srcscr=NULL;
        }
        else srcscr=NULL;
        if(destscr=cloneTopScreen(FALSE,Prefs[PREFS_SCREEN].po_Active))
        {
            if (Prefs[PREFS_HOLE].po_Active == PREFS_HOLE_BACKGROUND)
                 ColHole = ColBack;
            else ColHole = RndBT(0,(1L<<destscr->RastPort.BitMap->Depth)-1);    /* choose a colour randomly */
            if ((destscr->Width/Columns) < PIECE_MIN_WIDTH) Columns = destscr->Width/PIECE_MIN_WIDTH;
            if ((destscr->Height/Rows) < PIECE_MIN_HEIGHT) Rows = destscr->Height/PIECE_MIN_HEIGHT;
            if ((Columns>=2) && (Rows>=2))
            {
                /* adjust steps if necessary */
                if ((destscr->Width/Columns/2) <= Steps) Steps = destscr->Width/Columns / 2;
                if ((destscr->Height/Rows/2) <= Steps) Steps = destscr->Height/Rows / 2;

                if (srcscr || (Prefs[PREFS_UPDATE].po_Active==PREFS_UPDATE_NO))
                {
                    if(Prefs[PREFS_FADEPCT].po_Level)
                    {
                        ULONG *ColorTable, PctCount, BPG;

                        ColorTable = GetColorTable(destscr);
                        BPG = AvgBitsPerGun(getTopScreenMode());
                        PctCount = (1L<<BPG) * Prefs[PREFS_FADEPCT].po_Level/100;
                        for(i=0;i<PctCount;i++) FadeAndLoadTable(destscr,BPG,ColorTable,0);
                    }
                    puzzle.doborder = (Prefs[PREFS_BORDER].po_Active==PREFS_BORDER_YES)? TRUE:FALSE;
                    if (Prefs[PREFS_UPDATE].po_Active == PREFS_UPDATE_YES)
                         initPuzzle(srcscr,destscr,Columns,Rows,Steps); /* fetch gfx data from src scr */
                    else initSimplePuzzle(destscr,Columns,Rows,Steps); /* simple shuffling */
                    win = BlankMousePointer(destscr);
                    
                    delay = StepDelay;

                    while((RetVal=ContinueBlanking())==OK)
                    {
                        if(!(++ToFrontCount%64))    ScreenToFront(destscr);
                        
                        if (--delay)    Delay(1L);  /* delay for one tick */
                        else
                        {
                            if (puzzle.step==0)
                                if (Prefs[PREFS_HOLE].po_Active == PREFS_HOLE_RANDOM)
                                    ColHole = RndBT(0,(1L<<destscr->RastPort.BitMap->Depth)-1);
                            delay = StepDelay;
                            WaitTOF();
                            if (Prefs[PREFS_UPDATE].po_Active == PREFS_UPDATE_YES)  doStep();
                            else                                                    doSimpleStep();
                        }

                    } /* end of while(RetVal==OK) */

                    UnblankMousePointer(win);
                }
                else RetVal = FAILED; /* couldn't get srcscr address */
            }
            else RetVal = FAILED;   /* too less columns or rows */

            CloseScreen(destscr);
        }
        else RetVal = FAILED;
    }
    else RetVal = FAILED;
    CloseLibs();
    return RetVal;
}
