typedef struct SDLx_LayerManager
{
    AV          *layers;
    SDL_Surface *saveshot;
    SDL_Surface *dest;
    int          saved;
} SDLx_LayerManager;

typedef struct SDLx_Layer
{
    SDLx_LayerManager *manager;
    int                index;
    int                attached;
    int                touched;
    SDL_Surface       *surface;
    SDL_Rect          *clip;
    SDL_Rect          *pos;
    SDL_Rect          *attached_pos;
    SDL_Rect          *attached_rel;
    HV                *data;
} SDLx_Layer;

SDLx_Layer *bag_to_layer( SV *bag )
{
    SDLx_Layer *layer = NULL;

    if( sv_isobject(bag) && (SvTYPE(SvRV(bag)) == SVt_PVMG) )
    {
       void **pointers = (void **)(SvIV((SV *)SvRV( bag ))); 
       layer           = (SDLx_Layer *)(pointers[0]);
    }
    
    return layer;
}

SDL_Surface *bag_to_surface( SV *bag )
{
    SDL_Surface *surface = NULL;

    if( sv_isobject(bag) && (SvTYPE(SvRV(bag)) == SVt_PVMG) )
    {
       void **pointers = (void **)(SvIV((SV *)SvRV( bag ))); 
       surface         = (SDL_Surface *)(pointers[0]);
    }
    
    return surface;
}

int intersection( SDLx_Layer *layer1, SDLx_Layer *layer2 )
{
    if(
        // upper left point inside layer
        (   layer1->pos->x <= layer2->pos->x
            && layer2->pos->x < layer1->pos->x + layer1->clip->w
            && layer1->pos->y <= layer2->pos->y
            && layer2->pos->y < layer1->pos->y + layer1->clip->h
        )

        // upper right point inside layer
        || (   layer1->pos->x < layer2->pos->x + layer2->clip->w
            && layer2->pos->x + layer2->clip->w <= layer1->pos->x + layer1->clip->w
            && layer1->pos->y <= layer2->pos->y
            && layer2->pos->y < layer1->pos->y + layer1->clip->h )

        // lower left point inside layer
        || (   layer1->pos->x <= layer2->pos->x
            && layer2->pos->x < layer1->pos->x + layer1->clip->w
            && layer1->pos->y < layer2->pos->y + layer2->clip->h
            && layer2->pos->y + layer2->clip->h <= layer1->pos->y + layer1->clip->h )

        // lower right point inside layer
        || (   layer1->pos->x < layer2->pos->x + layer2->clip->w
            && layer2->pos->x + layer2->clip->w <= layer1->pos->x + layer1->clip->w
            && layer1->pos->y < layer2->pos->y + layer2->clip->h
            && layer2->pos->y + layer2->clip->h <= layer1->pos->y + layer1->clip->h )
    ){
        return 1;
    }
    
    return 0;
}

AV *layers_behind( SDLx_Layer *layer)
{
    AV *matches = newAV();
    int i;
    int count = 0;

    for( i = layer->index - 1; i >= 0; i-- )
    {
        SV *bag            = *av_fetch(layer->manager->layers, i, 0);
        SDLx_Layer *layer2 = bag_to_layer(bag);
        if(intersection( layer, layer2 ) || intersection( layer2, layer ))
        {
            // TODO checking transparency
            SvREFCNT_inc(bag);
            av_store( matches, count, bag );
            count++;
        }
    }
    
    if(count)
    {
        AV *behind = layers_behind(bag_to_layer(*av_fetch(matches, av_len(matches), 0)));
        if(av_len(behind) >= 0)
        {
            for( i = 0; i <= av_len(behind); i++ )
            {
                av_store( matches, count, *av_fetch(behind, i, 0));
                count++;
            }
        }
    }

    return matches;
}

AV *layers_ahead( SDLx_Layer *layer )
{
    AV *matches = newAV();
    int i;
    int count = 0;

    for( i = layer->index + 1; i <= av_len(layer->manager->layers); i++ )
    {
        SV *bag            = *av_fetch(layer->manager->layers, i, 0);
        SDLx_Layer *layer2 = bag_to_layer(bag);
        if(intersection( layer, layer2 ) || intersection( layer2, layer ))
        {
            // TODO checking transparency
            SvREFCNT_inc(bag);
            av_store( matches, count, bag );
            count++;
        }
    }
    
    if(count)
    {
        AV *ahead = layers_ahead(bag_to_layer(*av_fetch(matches, av_len(matches), 0)));
        if(av_len(ahead) >= 0)
        {
            for( i = 0; i <= av_len(ahead); i++ )
            {
                av_store( matches, count, *av_fetch(ahead, i, 0));
                count++;
            }
        }
    }
    
    return matches;
}