/*  
    sdlshadowdisplay.c

    Copyright (C) 2010-2021 Amf

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version. 

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>

#include "chroma.h"
#include "menu.h"
#include "level.h"
#include "display.h"
#include "graphics.h"
#include "colours.h"
#include "sdlfont.h"
#include "sdlscreen.h"
#include "util.h"

extern SDL_Surface *screen_surface;

extern int screen_width;
extern int screen_height;
extern int screen_fullscreen;

extern int options_sdl_width;
extern int options_sdl_height;
extern int options_sdl_fullscreen;
extern int options_sdl_size_x;
extern int options_sdl_size_y;
extern int options_sdl_delay;
extern int options_sdl_player_delay;
extern int options_sdl_replay_delay;
extern int options_sdl_undo_delay;
extern int options_sdl_mouse;
extern int options_graphic_level;
extern int options_debug;
extern char options_graphics[];
extern char options_colours[];

extern char *piece_name[];

extern int move_x[];
extern int move_y[];

extern int display_offset_x;
extern int display_offset_y;
extern int display_offset_pixels_x;
extern int display_offset_pixels_y;
extern int display_start_x;
extern int display_start_y;
extern int display_end_x;
extern int display_end_y;
extern int display_pieces_x;
extern int display_pieces_y;
extern int display_focus_x;
extern int display_focus_y;
extern int display_bar_pixels;
extern int display_border_x;
extern int display_border_y;

extern float display_animation;
extern int display_animation_x;
extern int display_animation_y;

extern struct graphics* pdisplaygraphics;

void display_clip(struct level* plevel, int clip);
int display_bevelsquare(struct level* plevel, int x, int y);

void displayshadowed_level(struct level* plevel);
void displayshadowed_movers(struct level* plevel, int redraw);

static inline void displayshadowed_piece(struct level* plevel, int p, int x, int y, int d)
{
    SDL_Surface *pimage;

    SDL_Rect srect;
    SDL_Rect drect;
    int px, py;
    int alpha = 0;
    int bimage[4];
    int b;
    int bsizex, bsizey;
    int boffset = 0;
    int i;
    int xend;

#ifdef XOR_COMPATIBILITY
    if(p == PIECE_WALL && plevel->switched)
        return;
#endif

    px = x * pdisplaygraphics->size_x + display_offset_pixels_x; 
    py = y * pdisplaygraphics->size_y + display_offset_pixels_y; 

    if(d != MOVE_NONE)
    { 
        px += move_x[d] * display_animation_x;
        py += move_y[d] * display_animation_y;
    }

    if(isexplosion(p))
        alpha = 255 * (1 - display_animation);
    if(isnewexplosion(p))
    {
        alpha = 255 * display_animation;
        p += PIECE_EXPLOSION_FIRST - PIECE_EXPLOSION_NEW_FIRST;
    }

    pimage = pdisplaygraphics->image[p][IMAGE_PIECE];

    if(isexplosion(p))
        SDL_SetAlpha(pimage, SDL_SRCALPHA, alpha);

    srect.x = 0;
    srect.y = 0;
    srect.w = pdisplaygraphics->size_x;
    srect.h = pdisplaygraphics->size_y;

    drect.x = px;
    drect.y = py;
    drect.w = pdisplaygraphics->size_x;
    drect.h = pdisplaygraphics->size_y;

    if(pimage->w > pdisplaygraphics->size_x)
    {
        if(p == PIECE_PLAYER_ONE || p == PIECE_PLAYER_TWO)
        {
            if(plevel->player != (p & 1) && plevel->player != 2)
                srect.x = pdisplaygraphics->size_x;
        }
        /* Is the piece tiled? */
        if(pdisplaygraphics->image_flags[p] & GRAPHICS_TILE)
        {
            xend = pimage->w / pdisplaygraphics->size_x;
            if(pdisplaygraphics->image_flags[p] & GRAPHICS_BEVEL)
                xend -= 4;

            b = x % xend;
            if(b < 0)
                b += xend;
            srect.x += pdisplaygraphics->size_x * b;

            b = y % (pimage->h / pdisplaygraphics->size_y);
            if(b < 0)
                b += pimage->h / pdisplaygraphics->size_y;
            srect.y += pdisplaygraphics->size_y * b;
        }
    }

    /* Plot piece */
    SDL_BlitSurface(pimage, &srect, screen_surface, &drect);

    /* Plot bevelling */
    if(pdisplaygraphics->image_flags[p] & GRAPHICS_BEVEL)
    {
        xend = pimage->w / pdisplaygraphics->size_x;
        xend -=4;

        b = level_data(plevel, x, y) & BEVEL_ALL;
        if(b != 0)
        {
            bsizex = pdisplaygraphics->size_x / 2;
            bsizey = pdisplaygraphics->size_y / 2;
            boffset = (xend - 1) * pdisplaygraphics->size_x;

            for(i = 0; i < 4; i ++)
                bimage[i] = 0;

            if(b & BEVEL_L)
            {
                if(b & BEVEL_U)
                    bimage[0] = 3 * pdisplaygraphics->size_x;
                else
                    bimage[0] = 1 * pdisplaygraphics->size_x;

                if(b & BEVEL_D)
                    bimage[2] = 3 * pdisplaygraphics->size_x;
                else
                    bimage[2] = 1 * pdisplaygraphics->size_x;
            }
            else
            {
                if(b & BEVEL_U)
                    bimage[0] = 2 * pdisplaygraphics->size_x;
                if(b & BEVEL_D)
                    bimage[2] = 2 * pdisplaygraphics->size_x;
            }

            if(b & BEVEL_R)
            {
                if(b & BEVEL_U)
                    bimage[1] = 3 * pdisplaygraphics->size_x;
                else
                    bimage[1] = 1 * pdisplaygraphics->size_x;

                if(b & BEVEL_D)
                    bimage[3] = 3 * pdisplaygraphics->size_x;
                else
                    bimage[3] = 1 * pdisplaygraphics->size_x;
            }
            else
            {
                if(b & BEVEL_U)
                    bimage[1] = 2 * pdisplaygraphics->size_x;
                if(b & BEVEL_D)
                    bimage[3] = 2 * pdisplaygraphics->size_x;
            }

            if(b & BEVEL_TL)
                bimage[0] = 4 * pdisplaygraphics->size_x;
            if(b & BEVEL_TR)
                bimage[1] = 4 * pdisplaygraphics->size_x;
            if(b & BEVEL_BL)
                bimage[2] = 4 * pdisplaygraphics->size_x;
            if(b & BEVEL_BR)
                bimage[3] = 4 * pdisplaygraphics->size_x;

            for(i = 0; i < 4; i ++)
            {
                if(bimage[i] != 0)
                {
                    srect.x = boffset + bimage[i] + ((i & 1) ? bsizex : 0);
                    srect.y = (i & 2) ? bsizey : 0;
                    srect.w = bsizex;
                    srect.h = bsizey;

                    drect.x = px + ((i & 1) ? bsizex : 0);
                    drect.y = py + ((i & 2) ? bsizey : 0);
                    drect.w = bsizex;
                    drect.h = bsizey;

                    SDL_BlitSurface(pimage, &srect, screen_surface, &drect);
                }
            }
        }
    }
}

static inline void displayshadowed_pieceshadow(struct level* plevel, int p, int x, int y, int d)
{
    SDL_Surface *pimage;

    SDL_Rect srect;
    SDL_Rect drect;
    int px, py;
    int alpha = 0;

    if(isexplosion(p))
        alpha = 255 * (1 - display_animation);
    if(isnewexplosion(p))
    {
        alpha = 255 * display_animation;
        p += PIECE_EXPLOSION_FIRST - PIECE_EXPLOSION_NEW_FIRST;
    }

    pimage = pdisplaygraphics->image[p][IMAGE_SHADOW];
    if(pimage == NULL)
        return;

    if(isexplosion(p))
        SDL_SetAlpha(pimage, SDL_SRCALPHA, alpha);

    px = x * pdisplaygraphics->size_x + display_offset_pixels_x; 
    py = y * pdisplaygraphics->size_y + display_offset_pixels_y; 
    if(d != MOVE_NONE)
    { 
        px += move_x[d] * display_animation_x;
        py += move_y[d] * display_animation_y;
    }

    srect.x = 0;
    srect.y = 0;
    srect.w = pdisplaygraphics->shadow_width[p][9];
    srect.h = pdisplaygraphics->shadow_height[p][9];

    drect.x = px + pdisplaygraphics->shadow_offset_x[p][9];
    drect.y = py + pdisplaygraphics->shadow_offset_y[p][9];
    drect.w = srect.w ;
    drect.h = srect.h;

    if(pimage->w > pdisplaygraphics->shadow_width[p][9])
    {
        if(p == PIECE_PLAYER_ONE || p == PIECE_PLAYER_TWO)
        {
            if(plevel->player != (p & 1) && plevel->player != 2)
                srect.x = pdisplaygraphics->shadow_width[p][9];
        }
    }

    /* Plot piece */
    SDL_BlitSurface(pimage, &srect, screen_surface, &drect);
}

static inline void displayshadowed_piecebase(struct level* plevel, int x, int y)
{
    int p;
    SDL_Surface *pimage;
    struct shadow *pshadow;
    struct shadow *pshadowstart;
    struct shadow *pshadowtmp;
    struct shadow *pshadowlast;
    int px, py;
    int z;
    int ok;
    int b, bp;
    int xend;

    SDL_Rect srect;
    SDL_Rect drect;
    SDL_Rect bsrect, bdrect;
    int alpha;

    p = level_piece(plevel, x, y);

    if(level_moving(plevel, x, y) != MOVE_NONE)
    {
        if(display_animation >= 1)
            p = PIECE_SPACE;
        else
            p = level_previous(plevel, x, y);
    }

#ifdef XOR_COMPATIBILITY
    if(plevel->switched && (p == PIECE_WALL || p == PIECE_SPACE))
        p = PIECE_DARKNESS;
#endif

    /* If the piece isn't transparent, nothing needs to be plotted */
    if(p != PIECE_SPACE
#ifdef XOR_COMPATIBILITY
            &&  p != PIECE_DARKNESS
#endif
            && !(pdisplaygraphics->image[p][IMAGE_PIECE]->flags & SDL_SRCALPHA))
        return;

#ifdef XOR_COMPATIBILITY
    if(plevel->switched)
        bp = PIECE_DARKNESS;
    else
#endif
        bp = PIECE_SPACE;

    pimage = pdisplaygraphics->image[bp][IMAGE_PIECE];

    px = x * pdisplaygraphics->size_x + display_offset_pixels_x; 
    py = y * pdisplaygraphics->size_y + display_offset_pixels_y; 

    srect.x = 0;
    srect.y = 0;
    srect.w = pdisplaygraphics->size_x;
    srect.h = pdisplaygraphics->size_y;

    drect.x = px;
    drect.y = py;
    drect.w = pdisplaygraphics->size_x;
    drect.h = pdisplaygraphics->size_y;

    /* Is the base tiled? */
    if(pdisplaygraphics->image_flags[bp] & GRAPHICS_TILE)
    {
        xend = pimage->w / pdisplaygraphics->size_x;
        if(pdisplaygraphics->image_flags[bp] & GRAPHICS_BEVEL)
            xend -= 4;
        srect.x += pdisplaygraphics->size_x * (x % xend);
        srect.y += pdisplaygraphics->size_y * (y % (pimage->h / pdisplaygraphics->size_y));
    }

    /* Plot the base */
    SDL_BlitSurface(pimage, &srect, screen_surface, &drect);

    /* Do we need to order the shadows prior to plotting? */
    if(pdisplaygraphics->flags & GRAPHICS_ZORDER)
    {
        pshadowstart = NULL;
        pshadowlast = NULL;

        pshadow = pdisplaygraphics->shadows;
        while(pshadow != NULL)
        {
            /* Determine which piece to consider the shadow of */
            p = level_piece(plevel, x - pshadow->x, y - pshadow->y);
            if(level_moving(plevel, x - pshadow->x, y - pshadow->y) != MOVE_NONE)
            {
                if(display_animation >= 1)
                    p = PIECE_SPACE;
                else
                {
                    if(level_previousmoving(plevel, x - pshadow->x, y - pshadow->y) == MOVE_NONE)
                        p = level_previous(plevel, x - pshadow->x, y - pshadow->y);
                    else
                        p = PIECE_SPACE;
                }
            }
#ifdef XOR_COMPATIBILITY
            if(p == PIECE_WALL && plevel->switched)
                p = PIECE_DARKNESS;
#endif
            /* Does it have a shadow? */
            if(pdisplaygraphics->image[p][IMAGE_SHADOW] != NULL && (pdisplaygraphics->shadow_flags[p] & pshadow->flag))
            {
                z = pdisplaygraphics->shadow_z[p];
                pshadow->z = pdisplaygraphics->shadow_z[p];
                pshadow->p = p;

                /* Put it in the ordered list */
                pshadowtmp = pshadowstart;
                while(pshadowtmp != NULL)
                {
                    if(pshadowtmp->z > z)
                        break;

                    pshadowtmp = pshadowtmp->nextordered;
                }
                if(pshadowstart == NULL)
                    pshadowstart = pshadow;

                /* It goes on the end of the list */
                if(pshadowtmp == NULL)
                {
                    pshadow->nextordered = NULL;
                    pshadow->previousordered = pshadowlast;
                    if(pshadowlast != NULL)
                        pshadowlast->nextordered = pshadow;
                    pshadowlast = pshadow;
                }
                /* It goes before pshadowtmp */
                else
                {
                    pshadow->nextordered = pshadowtmp;
                    pshadow->previousordered = pshadowtmp->previousordered;
                    if(pshadowtmp->previousordered != NULL)
                        pshadowtmp->previousordered->nextordered = pshadow;
                    else
                        pshadowstart = pshadow;
                    pshadowtmp->previousordered = pshadow;
                }
            }
            pshadow = pshadow->next;
        }
    }

    /* Plot shadows in order */
    if(pdisplaygraphics->flags & GRAPHICS_ZORDER)
        pshadow = pshadowstart;
    else
        pshadow = pdisplaygraphics->shadows;

    while(pshadow != NULL)
    {
        /* Determine which piece to consider the shadow of */
        p = level_piece(plevel, x - pshadow->x, y - pshadow->y);
        if(level_moving(plevel, x - pshadow->x, y - pshadow->y) != MOVE_NONE)
        {
            if(display_animation >= 1)
                p = PIECE_SPACE;
            else
            {
                if(level_previousmoving(plevel, x - pshadow->x, y - pshadow->y) == MOVE_NONE)
                    p = level_previous(plevel, x - pshadow->x, y - pshadow->y);
                else
                    p = PIECE_SPACE;
            }
        }
        /* Does it have a shadow? */
        if(pdisplaygraphics->image[p][IMAGE_SHADOW] != NULL && (pdisplaygraphics->shadow_flags[p] & pshadow->flag))
        {
            pimage = pdisplaygraphics->image[p][IMAGE_SHADOW];

            if(isexplosion(p))
            {
                alpha = 255 * (1 - display_animation);
                SDL_SetAlpha(pdisplaygraphics->image[p][IMAGE_SHADOW], SDL_SRCALPHA, alpha);
            }
            if(isnewexplosion(p))
            {
                p += PIECE_EXPLOSION_FIRST - PIECE_EXPLOSION_NEW_FIRST;
                alpha = 255 * display_animation;
                SDL_SetAlpha(pdisplaygraphics->image[p][IMAGE_SHADOW], SDL_SRCALPHA, alpha);
            }

            srect.x = pdisplaygraphics->shadow_start_x[p][pshadow->shadow];
            srect.y = pdisplaygraphics->shadow_start_y[p][pshadow->shadow];
            srect.w = pdisplaygraphics->shadow_width[p][pshadow->shadow];
            srect.h = pdisplaygraphics->shadow_height[p][pshadow->shadow];

            drect.x = px + pdisplaygraphics->shadow_offset_x[p][pshadow->shadow];
            drect.y = py + pdisplaygraphics->shadow_offset_y[p][pshadow->shadow];
            drect.w = pdisplaygraphics->shadow_width[p][pshadow->shadow];
            drect.h = pdisplaygraphics->shadow_height[p][pshadow->shadow];

            /* Are there multiple images? */
            if(pimage->w > pdisplaygraphics->shadow_width[p][9])
            {   
                /* Choose swapped player if necessary */
                if(p == PIECE_PLAYER_ONE || p == PIECE_PLAYER_TWO)
                {   
                    if(plevel->player != (p & 1) && plevel->player != 2)
                        srect.x += pdisplaygraphics->shadow_width[p][9];
                } 
            }

            /* Plot shadow */
            SDL_BlitSurface(pimage, &srect, screen_surface, &drect);

            /* Bevel shadow */
            if(pdisplaygraphics->image_flags[p] & GRAPHICS_BEVEL_SHADOW)
            {
                    b = level_data(plevel, x - pshadow->x, y - pshadow->y) & BEVEL_ALL;
                    /* Top left quadrant */
                    if(b & (BEVEL_L | BEVEL_U | BEVEL_TL))
                    {
                        bsrect.x = 0;
                        bsrect.y = 0;
                        bsrect.w = pdisplaygraphics->shadow_width[p][9] / 2;
                        bsrect.h = pdisplaygraphics->shadow_height[p][9] / 2;
                        bdrect.x = drect.x;
                        bdrect.y = drect.y;

                        ok = 1;
                        if(bsrect.x < srect.x)
                        {
                            if(bsrect.w > (srect.x - bsrect.x))
                            {
                                bsrect.w -= (srect.x - bsrect.x);
                                bdrect.x -= bsrect.x;
                                bsrect.x = srect.x;
                            }
                            else
                                ok = 0;
                        }
                        if(bsrect.y < srect.y)
                        {
                            if(bsrect.h > (srect.y - bsrect.y))
                            {
                                bsrect.h -= (srect.y - bsrect.y);
                                bdrect.y -= bsrect.y;
                                bsrect.y = srect.y;
                            }
                            else
                                ok = 0;
                        }
                        if(bdrect.x + bsrect.w > drect.x + drect.w)
                        {
                            if(bsrect.w > ((bdrect.x + bsrect.w) - (drect.x + drect.w)))
                                bsrect.w -= ((bdrect.x + bsrect.w) - (drect.x + drect.w));
                            else
                                ok = 0;
                        }
                        if(bdrect.y + bsrect.h > drect.y + drect.h)
                        {
                            if(bsrect.h > ((bdrect.y + bsrect.h) - (drect.y + drect.h)))
                                bsrect.h -= ((bdrect.y + bsrect.h) - (drect.y + drect.h));
                            else
                                ok = 0;
                        }
                        if(b & BEVEL_TL)
                            bsrect.x += pdisplaygraphics->shadow_width[p][9] * 4;
                        else
                        {
                            if(b & BEVEL_U)
                                bsrect.x += pdisplaygraphics->shadow_width[p][9] * 2;
                            if(b & BEVEL_L)
                                bsrect.x += pdisplaygraphics->shadow_width[p][9];
                        }

                        if(ok)
                            SDL_BlitSurface(pimage, &bsrect, screen_surface, &bdrect);
                    }

                    /* Top right quadrant */
                    if(b & (BEVEL_R | BEVEL_U | BEVEL_TR))
                    {
                        bsrect.x = pdisplaygraphics->shadow_width[p][9] / 2;
                        bsrect.y = 0;
                        bsrect.w = pdisplaygraphics->shadow_width[p][9] / 2;
                        bsrect.h = pdisplaygraphics->shadow_height[p][9] / 2;
                        bdrect.x = drect.x + pdisplaygraphics->size_x / 2;
                        bdrect.y = drect.y;

                        ok = 1;
                        if(bsrect.x < srect.x)
                        {
                            if(bsrect.w > (srect.x - bsrect.x))
                            {
                                bsrect.w -= (srect.x - bsrect.x);
                                bdrect.x -= bsrect.x;
                                bsrect.x = srect.x;
                            }
                            else
                                ok = 0;
                        }
                        if(bsrect.y < srect.y)
                        {
                            if(bsrect.h > (srect.y - bsrect.y))
                            {
                                bsrect.h -= (srect.y - bsrect.y);
                                bdrect.y -= bsrect.y;
                                bsrect.y = srect.y;
                            }
                            else
                                ok = 0;
                        }
                        if(bdrect.x + bsrect.w > drect.x + drect.w)
                        {
                            if(bsrect.w > ((bdrect.x + bsrect.w) - (drect.x + drect.w)))
                                bsrect.w -= ((bdrect.x + bsrect.w) - (drect.x + drect.w));
                            else
                                ok = 0;
                        }
                        if(bdrect.y + bsrect.h > drect.y + drect.h)
                        {
                            if(bsrect.h > ((bdrect.y + bsrect.h) - (drect.y + drect.h)))
                                bsrect.h -= ((bdrect.y + bsrect.h) - (drect.y + drect.h));
                            else
                                ok = 0;
                        }
                        if(b & BEVEL_TR)
                            bsrect.x += pdisplaygraphics->shadow_width[p][9] * 4;
                        else
                        {
                            if(b & BEVEL_U)
                                bsrect.x += pdisplaygraphics->shadow_width[p][9] * 2;
                            if(b & BEVEL_R)
                                bsrect.x += pdisplaygraphics->shadow_width[p][9];
                        }
                        if(ok)
                            SDL_BlitSurface(pimage, &bsrect, screen_surface, &bdrect);
                    }

                    /* Bottom left quadrant */
                    if(b & (BEVEL_L | BEVEL_D | BEVEL_BL))
                    {
                        bsrect.x = 0;
                        bsrect.y = pdisplaygraphics->shadow_height[p][9] / 2;;
                        bsrect.w = pdisplaygraphics->shadow_width[p][9] / 2;
                        bsrect.h = pdisplaygraphics->shadow_height[p][9] / 2;
                        bdrect.x = drect.x;
                        bdrect.y = drect.y + pdisplaygraphics->size_y / 2;

                        ok = 1;
                        if(bsrect.x < srect.x)
                        {
                            if(bsrect.w > (srect.x - bsrect.x))
                            {
                                bsrect.w -= (srect.x - bsrect.x);
                                bdrect.x -= bsrect.x;
                                bsrect.x = srect.x;
                            }
                            else
                                ok = 0;
                        }
                        if(bsrect.y < srect.y)
                        {
                            if(bsrect.h > (srect.y - bsrect.y))
                            {
                                bsrect.h -= (srect.y - bsrect.y);
                                bdrect.y -= bsrect.y;
                                bsrect.y = srect.y;
                            }
                            else
                                ok = 0;
                        }
                        if(bdrect.x + bsrect.w > drect.x + drect.w)
                        {
                            if(bsrect.w > ((bdrect.x + bsrect.w) - (drect.x + drect.w)))
                                bsrect.w -= ((bdrect.x + bsrect.w) - (drect.x + drect.w));
                            else
                                ok = 0;
                        }
                        if(bdrect.y + bsrect.h > drect.y + drect.h)
                        {
                            if(bsrect.h > ((bdrect.y + bsrect.h) - (drect.y + drect.h)))
                                bsrect.h -= ((bdrect.y + bsrect.h) - (drect.y + drect.h));
                            else
                                ok = 0;
                        }
                        if(b & BEVEL_BL)
                            bsrect.x += pdisplaygraphics->shadow_width[p][9] * 4;
                        else
                        {
                            if(b & BEVEL_D)
                                bsrect.x += pdisplaygraphics->shadow_width[p][9] * 2;
                            if(b & BEVEL_L)
                                bsrect.x += pdisplaygraphics->shadow_width[p][9];
                        }
                        if(ok)
                            SDL_BlitSurface(pimage, &bsrect, screen_surface, &bdrect);
                    }

                    /* Bottom right quadrant */
                    if(b & (BEVEL_R | BEVEL_D | BEVEL_BR))
                    {
                        bsrect.x = pdisplaygraphics->shadow_width[p][9] / 2;
                        bsrect.y = pdisplaygraphics->shadow_height[p][9] / 2;
                        bsrect.w = pdisplaygraphics->shadow_width[p][9] / 2;
                        bsrect.h = pdisplaygraphics->shadow_height[p][9] / 2;
                        bdrect.x = drect.x + pdisplaygraphics->size_x / 2;
                        bdrect.y = drect.y + pdisplaygraphics->size_y / 2;

                        ok = 1;
                        if(bsrect.x < srect.x)
                        {
                            if(bsrect.w > (srect.x - bsrect.x))
                            {
                                bsrect.w -= (srect.x - bsrect.x);
                                bdrect.x -= bsrect.x;
                                bsrect.x = srect.x;
                            }
                            else
                                ok = 0;
                        }
                        if(bsrect.y < srect.y)
                        {
                            if(bsrect.h > (srect.y - bsrect.y))
                            {
                                bsrect.h -= (srect.y - bsrect.y);
                                bdrect.y -= bsrect.y;
                                bsrect.y = srect.y;
                            }
                            else
                                ok = 0;
                        }
                        if(bdrect.x + bsrect.w > drect.x + drect.w)
                        {
                            if(bsrect.w > ((bdrect.x + bsrect.w) - (drect.x + drect.w)))
                                bsrect.w -= ((bdrect.x + bsrect.w) - (drect.x + drect.w));
                            else
                                ok = 0;
                        }
                        if(bdrect.y + bsrect.h > drect.y + drect.h)
                        {
                            if(bsrect.h > ((bdrect.y + bsrect.h) - (drect.y + drect.h)))
                                bsrect.h -= ((bdrect.y + bsrect.h) - (drect.y + drect.h));
                            else
                                ok = 0;
                        }
                        if(b & BEVEL_BR)
                            bsrect.x += pdisplaygraphics->shadow_width[p][9] * 4;
                        else
                        {
                            if(b & BEVEL_D)
                                bsrect.x += pdisplaygraphics->shadow_width[p][9] * 2;
                            if(b & BEVEL_R)
                                bsrect.x += pdisplaygraphics->shadow_width[p][9];
                        }
                        if(ok)
                            SDL_BlitSurface(pimage, &bsrect, screen_surface, &bdrect);
                }
            }

        }

        if(pdisplaygraphics->flags & GRAPHICS_ZORDER)
            pshadow = pshadow->nextordered;
        else
            pshadow = pshadow->next;
    }
}

static inline void displayshadowed_redrawpiece(int p, int x, int y, int d)
{
    int dx, dy;

    dx = x * pdisplaygraphics->size_x + display_offset_pixels_x;
    dy = y * pdisplaygraphics->size_y + display_offset_pixels_y;

    if(d != MOVE_NONE && p != PIECE_SPACE && !isexplosion(p))
    {
        if(d == MOVE_LEFT) { dx = dx - display_animation_x; }
        if(d == MOVE_RIGHT) { dx = dx + display_animation_x; }
        if(d == MOVE_UP) { dy = dy - display_animation_y; }
        if(d == MOVE_DOWN) { dy = dy + display_animation_y; }
    }

    screen_redraw(dx, dy, pdisplaygraphics->size_x, pdisplaygraphics->size_y);
}

void displayshadowed_level(struct level* plevel)
{
    int x, y;
    int p;

    display_clip(plevel, 1);

    /* Plot base */
    for(y = display_start_y; y < display_end_y; y ++)
    {
        for(x = display_start_x; x < display_end_x; x ++)
        {
            displayshadowed_piecebase(plevel, x, y);

            p = level_piece(plevel, x, y);

            /* Plot the piece itself */
            if(p != PIECE_SPACE)
                 displayshadowed_piece(plevel, p, x, y, MOVE_NONE);
        }
    }

    if(plevel->mover_first != NULL)
        displayshadowed_movers(plevel, 0);

    display_clip(plevel, 0);

    screen_redraw(0, 0, screen_width, screen_height);
}

static inline int displayshadowed_count(struct level* plevel, int x, int y, int delta)
{
    unsigned int d;

    d = level_data(plevel, x, y);
    level_setdata(plevel, x, y, d + (delta * SHADOW_BASE));

    return d & (0x7f * SHADOW_BASE);
}


void displayshadowed_movers(struct level* plevel, int redraw)
{
    struct shadow* pshadow;
    struct mover* pmover;
    int x, y, p, pm;
    int i, j;
    char buffer[16];
    int d;
    int bevel;
    int bevelold;
    SDL_Surface *psurface;
    SDL_Rect srect, drect;

    p = PIECE_SPACE;

    display_animation_x = - pdisplaygraphics->size_x + (int)((float) pdisplaygraphics->size_x * display_animation);
    display_animation_y = - pdisplaygraphics->size_y + (int)((float) pdisplaygraphics->size_y * display_animation);

    display_clip(plevel, 1);

    /* Stage one: plot base for pieces that need rebevelling */
    if(display_animation == 0 || display_animation == 1)
    {
        /* When undoing, we have to create the wall prior to rebevelling, as it
           wouldn't otherwise exist until after the end of the animation. */
        pmover = plevel->mover_first;
        while(pmover != NULL)
        {
            if(pmover->piece == PIECE_WALL)
                level_setpiece(plevel, pmover->x, pmover->y, pmover->piece);
            pmover = pmover->next;
        }

        pmover = plevel->mover_first;
        while(pmover != NULL)
        {
            x = pmover->x;
            y = pmover->y;
            if(pmover->piece == PIECE_WALL ||
                (isexplosion(pmover->piece) && display_animation == 1))
            {
                pshadow = pdisplaygraphics->shadows;
                while(pshadow != NULL)
                {
                    bevel = display_bevelsquare(plevel, x + pshadow->x, y + pshadow->y);
                    bevelold = (level_data(plevel, x + pshadow->x, y + pshadow->y) & BEVEL_ALL);
                    /* Because this happens only once per move cycle, we
                       are lazy and don't bother to count whether this base
                       has already been plotted */
                    if(bevel != bevelold);
                        displayshadowed_piecebase(plevel, pmover->x + pshadow->x, pmover->y + pshadow->y);
                    pshadow = pshadow->next;
                }
            }
            pmover = pmover->next;
        }
    }

    /* Stage two: plot shadows for stationary squares affected by movers */
    pmover = plevel->mover_first;
    while(pmover != NULL)
    {
        if(pmover->piece != PIECE_GONE)
        {
            /* This is overkill, but it's easier just to plot everything that
               could be affected than to calculate what is actually affected.
               */
            pshadow = pdisplaygraphics->shadows;
            while(pshadow != NULL)
            {
                if(displayshadowed_count(plevel, pmover->x + pshadow->x, pmover->y + pshadow->y, 1) == 0)
                    displayshadowed_piecebase(plevel, pmover->x + pshadow->x, pmover->y + pshadow->y);
                pshadow = pshadow->next;
            }
            if(displayshadowed_count(plevel, pmover->x, pmover->y, 1) == 0)
                displayshadowed_piecebase(plevel, pmover->x, pmover->y);
        }
        
        pmover = pmover->next;
    }
    /* then reset the counts */
    pmover = plevel->mover_first;
    while(pmover != NULL)
    {
        if(pmover->piece != PIECE_GONE)
        {
            pshadow = pdisplaygraphics->shadows;
            while(pshadow != NULL)
            {
                displayshadowed_count(plevel, pmover->x + pshadow->x, pmover->y + pshadow->y, -1);
                pshadow = pshadow->next;
            }
            displayshadowed_count(plevel, pmover->x, pmover->y, -1);
        }
        
        pmover = pmover->next;
    }

    /* Stage three: plot shadows for movers */
    pmover = plevel->mover_first;
    while(pmover != NULL)
    {
        d = pmover->direction;
        x = pmover->x;
        y = pmover->y;

        if(isexplosion(pmover->piece))
        {
            /* If the previous piece, that is, the piece destroyed in the
               explosion, is stationary, we don't need to plot a shadow for it,
               as that is handled in stage one. If it is moving, however, it
               needs a moving shadow, which we do have to plot here. */
            p = level_previous(plevel, x, y);
            pm = level_previousmoving(plevel, x, y);
            if(p != PIECE_SPACE && pm != MOVE_NONE)
                displayshadowed_pieceshadow(plevel, p, x, y, pm);

            /* Plot shadow for the detonator */
            p = level_detonator(plevel, x, y);
            pm = level_detonatormoving(plevel, x, y);
            if(p != PIECE_SPACE)
                displayshadowed_pieceshadow(plevel, p, x, y, pm);

        }
        /* Spaces and walls were handled in stage one */
        else if(pmover->piece != PIECE_SPACE && pmover->piece != PIECE_WALL && pmover->piece != PIECE_GONE)
        {
            /* We don't need to plot the shadow for the previous piece
               as that is handled in stage one */

            /* Plot shadow for mover */
#ifdef XOR_COMPATIBILITY
            if(pmover->piece == PIECE_TELEPORT)
                d = MOVE_NONE;
#endif
            displayshadowed_pieceshadow(plevel, pmover->piece, pmover->x, pmover->y, d);
        }
        pmover = pmover->next;
    }

    /* Stage four: plot shadows for explosions */
    pmover = plevel->mover_first;
    while(pmover != NULL)
    {
        x = pmover->x;
        y = pmover->y;
        /* Plot growing explosion */
        if(isexplosion(pmover->piece))
            displayshadowed_pieceshadow(plevel, pmover->piece + PIECE_EXPLOSION_NEW_FIRST - PIECE_EXPLOSION_FIRST, x, y, MOVE_NONE);

        /* Plot dying explosion */
        p = level_previous(plevel, x, y);
        if(isexplosion(p) && display_animation < 1)
            displayshadowed_pieceshadow(plevel, p, x, y, MOVE_NONE);

        pmover = pmover->next;
    }

    /* Stage five: plot pieces for stationary squares affected by movers.
       We need to be careful not to plot the same piece twice, so we keep
       count and only plot on the first occurrence. */
    pmover = plevel->mover_first;
    while(pmover != NULL)
    {
        pshadow = pdisplaygraphics->shadows;
        while(pshadow != NULL)
        {
            if(displayshadowed_count(plevel, pmover->x + pshadow->x, pmover->y + pshadow->y, 1) == 0)
            {
                p = level_piece(plevel, pmover->x + pshadow->x, pmover->y + pshadow->y);
                pm = level_moving(plevel, pmover->x + pshadow->x, pmover->y + pshadow->y);
                if(p != PIECE_SPACE && p != PIECE_GONE && pm == MOVE_NONE)
                    displayshadowed_piece(plevel, p, pmover->x + pshadow->x, pmover->y + pshadow->y, MOVE_NONE);
            }
            pshadow = pshadow->next;
        }
        pmover = pmover->next;
    }
    /* then reset the counts */
    pmover = plevel->mover_first;
    while(pmover != NULL)
    {
       pshadow = pdisplaygraphics->shadows;
        while(pshadow != NULL)
        {  
            displayshadowed_count(plevel, pmover->x + pshadow->x, pmover->y + pshadow->y, -1);
            pshadow = pshadow->next;
        }
        pmover = pmover->next;
    }

    /* Stage six: plot pieces for movers */
    pmover = plevel->mover_first;
    while(pmover != NULL)
    {
        d = pmover->direction;
        x = pmover->x;
        y = pmover->y;

        if(isexplosion(pmover->piece))
        {
            /* Plot any piece destroyed by the explosion, or the bomb itself */
            p = level_previous(plevel, x, y);
            pm = level_previousmoving(plevel, x, y);
            if(p != PIECE_SPACE)
                displayshadowed_piece(plevel, p, x, y, pm);

            /* Plot the detonator */
            p = level_detonator(plevel, x, y);
            pm = level_detonatormoving(plevel, x, y);
            if(p != PIECE_SPACE)
                displayshadowed_piece(plevel, p, x, y, pm);

        }
        /* Spaces were handled in stage one */
        else if(pmover->piece != PIECE_SPACE && pmover->piece != PIECE_GONE)
        {
            if(display_animation < 1)
            {   
                /* Pieces being collected, earth being eaten */
                p = level_previous(plevel, x, y);
                pm = level_previousmoving(plevel, x, y);
                if((p != PIECE_SPACE && !isexplosion(p) && pm == MOVE_NONE)
#ifdef XOR_COMPATIBILITY
                        || pmover->piece == PIECE_TELEPORT
#endif
                  )
                    displayshadowed_piece(plevel, p, x, y, pm);
            }

            /* Plot the piece itself */
#ifdef XOR_COMPATIBILITY
            if(pmover->piece == PIECE_TELEPORT)
                d = MOVE_NONE;
#endif
            displayshadowed_piece(plevel, pmover->piece, pmover->x, pmover->y, d);
        }
        pmover = pmover->next;
    }

    /* Stage seven: plot pieces that need rebevelling */
    if(display_animation == 0 || display_animation == 1)
    {
        pmover = plevel->mover_first;
        while(pmover != NULL)
        {
            x = pmover->x;
            y = pmover->y;
            if(pmover->piece == PIECE_WALL ||
                (isexplosion(pmover->piece) && display_animation == 1))
            {
                for(i = -1; i < 2; i ++)
                {
                    for(j = - 1; j < 2; j ++)
                    {
                        bevel = display_bevelsquare(plevel, x + i, y + j);
                        bevelold = (level_data(plevel, x + i, y + j) & BEVEL_ALL);
                        if(bevel != bevelold);
                        {
                            /* Here we are not lazy, to avoid issues with
                               transparent graphics being plotted twice */
                            if(displayshadowed_count(plevel, pmover->x + i, pmover->y + j, 1) == 0)
                            {
                                level_setdata(plevel, x + i, y + j, bevel | (level_data(plevel, x + i, y + j) & ~BEVEL_ALL));
                                p = level_piece(plevel, x + i, y + j);
                                if(p == PIECE_WALL)
                                    displayshadowed_piece(plevel, p, pmover->x + i, pmover->y + j, MOVE_NONE);
                                level_setdata(plevel, x + i, y + j, bevelold | (level_data(plevel, x + i, y + j) & ~BEVEL_ALL));
                            }
                        }
                    }
                }
            }
            pmover = pmover->next;
        }
        /* and reset counts */
        pmover = plevel->mover_first;
        while(pmover != NULL)
        {
            x = pmover->x;
            y = pmover->y;
            if(pmover->piece == PIECE_WALL ||
                (isexplosion(pmover->piece) && display_animation == 1))
            {
                for(i = -1; i < 2; i ++)
                {
                    for(j = - 1; j < 2; j ++)
                    {
                        bevel = display_bevelsquare(plevel, x + i, y + j);
                        bevelold = (level_data(plevel, x + i, y + j) & BEVEL_ALL);
                        if(bevel != bevelold);
                            displayshadowed_count(plevel, pmover->x + i, pmover->y + j, -1);
                    }
                }
            }
            pmover = pmover->next;
        }
    }

    /* Stage eight: plot pieces for explosions */
    pmover = plevel->mover_first;
    while(pmover != NULL)
    {
        x = pmover->x;
        y = pmover->y;
        /* Plot growing explosion */
        if(isexplosion(pmover->piece))
            displayshadowed_piece(plevel, pmover->piece + PIECE_EXPLOSION_NEW_FIRST - PIECE_EXPLOSION_FIRST, x, y, MOVE_NONE);

        /* Plot dying explosion */
        p = level_previous(plevel, x, y);
        if(isexplosion(p) && display_animation < 1)
            displayshadowed_piece(plevel, p, x, y, MOVE_NONE);

        pmover = pmover->next;
    }

    /* Stage nine: plot order of movers if debugging (but not in editor) */
    if(options_debug & DEBUG_ORDER && display_animation < 1 && plevel->player != 2)
    {   
        pmover = plevel->mover_first;
        i = 0;
        while(pmover != NULL)
        {   
            if(pmover->piece != PIECE_SPACE && pmover->piece != PIECE_GONE)
            {   
                pm = pmover->direction;
                if(isexplosion(pmover->piece) || isnewexplosion(pmover->piece))
                    pm = MOVE_NONE;

                x = pmover->x * pdisplaygraphics->size_x + display_offset_pixels_x + ((-1 + display_animation) * move_x[pm] * pdisplaygraphics->size_x);
                y = pmover->y * pdisplaygraphics->size_y + display_offset_pixels_y + ((-1 + display_animation) * move_y[pm] * pdisplaygraphics->size_y);

                sprintf(buffer, "%X", i++);
                switch(pmover->direction)
                {
                    case MOVE_UP:
                        strcat(buffer, ARROW_UP);
                        break;
                    case MOVE_DOWN:
                        strcat(buffer, ARROW_DOWN);
                        break;
                    case MOVE_LEFT:
                        strcat(buffer, ARROW_LEFT);
                        break;
                    case MOVE_RIGHT:
                        strcat(buffer, ARROW_RIGHT);
                        break;
                    default:
                        break;
                }
                psurface = font_render(buffer, COLOUR_WHITE | COLOUR_BOLD);
                srect.w = psurface->w > pdisplaygraphics->size_x ? pdisplaygraphics->size_x : psurface->w;
                srect.h = psurface->h > pdisplaygraphics->size_y ? pdisplaygraphics->size_y : psurface->h;
                srect.x = psurface->w - srect.w;
                srect.y = 0;
                drect.x = x + pdisplaygraphics->size_x - srect.w;
                drect.y = y;
                SDL_BlitSurface(psurface, &srect, screen_surface, &drect);
                SDL_FreeSurface(psurface);
            }
            pmover = pmover->next;
        }
    }

    display_clip(plevel, 0);

    if(redraw == 0)
        return;

    display_clip(plevel, 1);

    /* Stage ten: redraw modified squares */
    pmover = plevel->mover_first;
    while(pmover != NULL)
    {
        if(pmover->piece != PIECE_GONE)
        {
            pshadow = pdisplaygraphics->shadows;
            while(pshadow != NULL)
            {
                if(displayshadowed_count(plevel, pmover->x + pshadow->x, pmover->y + pshadow->y, 1) == 0)
                    displayshadowed_redrawpiece(level_piece(plevel, pmover->x + pshadow->x, pmover->y + pshadow->y), pmover->x + pshadow->x, pmover->y + pshadow->y, MOVE_NONE);
                pshadow = pshadow->next;
            }
            if(displayshadowed_count(plevel, pmover->x, pmover->y, 1) == 0)
                displayshadowed_redrawpiece(level_piece(plevel, pmover->x, pmover->y), pmover->x, pmover->y, MOVE_NONE);
        }
        pmover = pmover->next;
    }
    /* then reset the counts */
    pmover = plevel->mover_first;
    while(pmover != NULL)
    {
        if(pmover->piece != PIECE_GONE)
        {
            pshadow = pdisplaygraphics->shadows;
            while(pshadow != NULL)
            {
                displayshadowed_count(plevel, pmover->x + pshadow->x, pmover->y + pshadow->y, -1);
                pshadow = pshadow->next;
            }
            displayshadowed_count(plevel, pmover->x, pmover->y, -1);
        }
        pmover = pmover->next;
    }

    /* Stage eleven: redraw pieces that need rebevelling */
    if(display_animation == 0 || display_animation == 1)
    {
        pmover = plevel->mover_first;
        while(pmover != NULL)
        {
            x = pmover->x;
            y = pmover->y;
            if(pmover->piece == PIECE_WALL ||
                (isexplosion(pmover->piece) && display_animation == 1))
            {
                for(i = -1; i < 2; i ++)
                {
                    for(j = - 1; j < 2; j ++)
                    {
                        bevel = display_bevelsquare(plevel, x + i, y + j);
                        bevelold = (level_data(plevel, x + i, y + j) & BEVEL_ALL);
                        if(bevel != bevelold);
                        {
                            /* Again, we are lazy and do not keep count of
                               whether this square has already been redrawn. */
                            level_setdata(plevel, x + i, y + j, bevel | (level_data(plevel, x + i, y + j) & ~BEVEL_ALL));
                            displayshadowed_redrawpiece(level_piece(plevel, pmover->x + i, pmover->y + j), pmover->x + i, pmover->y + j, MOVE_NONE);
                        }
                    }
                }
            }
            pmover = pmover->next;
        }
    }

    display_clip(plevel, 0);
}
