/*
 * ion/ioncore/float-placement.c
 *
 * Copyright (c) Tuomo Valkonen 1999-2007.
 *
 * See the included file LICENSE for details.
 */

#include <string.h>

#include "common.h"
#include "rootwin.h"
#include "xwindow.h"
#include "group.h"
#include "float-placement.h"


WFloatPlacement ioncore_placement_method=PLACEMENT_LRUD;


static void random_placement(WRectangle box, WRectangle *g)
{
    box.w-=g->w;
    box.h-=g->h;
    g->x=box.x+(box.w<=0 ? 0 : rand()%box.w);
    g->y=box.y+(box.h<=0 ? 0 : rand()%box.h);
}


static void ggeom(WRegion *reg, WRectangle *geom)
{
    *geom=REGION_GEOM(reg);
}


static bool st_filt(WStacking *st, void *lvl)
{
    uint level=*(uint*)lvl;

    return (st->reg!=NULL &&
            REGION_IS_MAPPED(st->reg) &&
            st->level==level);
}


#define FOR_ALL_STACKING_NODES(VAR, WS, LVL, TMP)          \
    for(stacking_iter_init(&(TMP), group_get_stacking(ws), \
                          st_filt, &LVL),                  \
        VAR=stacking_iter_nodes(&(TMP));                   \
        VAR!=NULL;                                         \
        VAR=stacking_iter_nodes(&(TMP)))


#define IGNORE_ST(ST, WS) ((ST)->reg==NULL || (ST)==(WS)->bottom)


static WRegion* is_occupied(WGroup *ws, uint level, const WRectangle *r)
{
    WStackingIterTmp tmp;
    WStacking *st;
    WRectangle p;

    FOR_ALL_STACKING_NODES(st, ws, level, tmp){
        ggeom(st->reg, &p);

        if(r->x>=p.x+p.w)
            continue;
        if(r->y>=p.y+p.h)
            continue;
        if(r->x+r->w<=p.x)
            continue;
        if(r->y+r->h<=p.y)
            continue;
        return st->reg;
    }

    return NULL;
}


static int next_least_x(WGroup *ws, uint level, int x)
{
    WRectangle p;
    int retx=REGION_GEOM(ws).x+REGION_GEOM(ws).w;
    WStackingIterTmp tmp;
    WStacking *st;

    FOR_ALL_STACKING_NODES(st, ws, level, tmp){
        ggeom(st->reg, &p);

        if(p.x+p.w>x && p.x+p.w<retx)
            retx=p.x+p.w;
    }

    return retx+1;
}



static int next_lowest_y(WGroup *ws, uint level, int y)
{
    WRectangle p;
    int rety=REGION_GEOM(ws).y+REGION_GEOM(ws).h;
    WStackingIterTmp tmp;
    WStacking *st;

    FOR_ALL_STACKING_NODES(st, ws, level, tmp){
        ggeom(st->reg, &p);

        if(p.y+p.h>y && p.y+p.h<rety)
            rety=p.y+p.h;
    }

    return rety+1;
}


static bool tiling_placement(WGroup *ws, uint level, WRectangle *g)
{
    WRegion *p;
    WRectangle r, r2;
    int maxx, maxy;

    r=REGION_GEOM(ws);
    r.w=g->w;
    r.h=g->h;

    maxx=REGION_GEOM(ws).x+REGION_GEOM(ws).w;
    maxy=REGION_GEOM(ws).y+REGION_GEOM(ws).h;

    if(ioncore_placement_method==PLACEMENT_UDLR){
        while(r.x<maxx){
            p=is_occupied(ws, level, &r);
            while(p!=NULL && r.y+r.h<maxy){
                ggeom(p, &r2);
                r.y=r2.y+r2.h+1;
                p=is_occupied(ws, level, &r);
            }
            if(r.y+r.h<maxy && r.x+r.w<maxx){
                g->x=r.x;
                g->y=r.y;
                return TRUE;
            }else{
                r.x=next_least_x(ws, level, r.x);
                r.y=0;
            }
        }
    }else{
        while(r.y<maxy){
            p=is_occupied(ws, level, &r);
            while(p!=NULL && r.x+r.w<maxx){
                ggeom(p, &r2);
                r.x=r2.x+r2.w+1;
                p=is_occupied(ws, level, &r);
            }
            if(r.y+r.h<maxy && r.x+r.w<maxx){
                g->x=r.x;
                g->y=r.y;
                return TRUE;
            }else{
                r.y=next_lowest_y(ws, level, r.y);
                r.x=0;
            }
        }
    }

    return FALSE;

}

static bool pointer_placement(WGroup *ws, WRectangle *g)
{
    WRootWin *rootwin=region_rootwin_of((WRegion*)ws);
    int px=0, py=0;

    if(xwindow_pointer_pos(WROOTWIN_ROOT(rootwin), &px, &py)){
        const WRectangle r=REGION_GEOM(ws);
        g->x=px-(g->w/2);
        g->y=py-(g->h/2);
        rectangle_clamp_or_center(g, &r);
        return TRUE;
    }
    return FALSE;
}


void group_calc_placement(WGroup *ws, uint level, WRectangle *geom)
{
    if(ioncore_placement_method==PLACEMENT_POINTER){
        if(pointer_placement(ws, geom))
            return;
    }else if(ioncore_placement_method!=PLACEMENT_RANDOM){
        if(tiling_placement(ws, level, geom))
            return;
    }
    random_placement(REGION_GEOM(ws), geom);
}

