/*
 * Licensed under BSD license.  See LICENCE.TXT  
 *
 * Produced by:	Jeff Lait
 *
 *      	7 Day Band Development
 *
 * NAME:        builder.h ( 7 Day Band, C++ )
 *
 * COMMENTS:
 *	Verbish class to build ascii maps
 */

#ifndef __builder_h__
#define __builder_h__

#include "glbdef.h"
#include "vec2.h"
#include "grid.h"
#include <assert.h>

class RCT
{
public:
    int		x, y, w, h;	// [x, x+w) x [y, y+h)

    bool	contains(int px, int py) const { return contains(IVEC2(px, py)); }
    bool	contains(IVEC2 p) const
    {
	return p.x() >= x && p.y() >= y && p.x() < (x+w) && p.y() < (y+h);
    }
    bool	overlap(const RCT &o) const
    {
	if (MAX(x, o.x) > MIN(x+w, o.x+o.w))
	    return false;
	if (MAX(y, o.y) > MIN(y+h, o.y+o.h))
	    return false;
	return true;
    }

    void	doUnion(const RCT &o)
    {
	// Unioning with empty does nothing.
	if (o.w == 0 || o.h == 0)
	    return;
	// If we are empty, union should replace (our x,y should not matter)
	if (w == 0 || h == 0)
	{
	    *this = o;
	    return;
	}
	int minx = MIN(x, o.x);
	int maxx = MAX(x+w, o.x+o.w);
	int miny = MIN(y, o.y);
	int maxy = MAX(y+h, o.y+o.h);

	x = minx;
	y = miny;
	w = maxx - minx;
	h = maxy - miny;
    }

};


struct WALLCUT;

class BUILDER
{
public:
    BUILDER(u8 *data, GRIDI *metadata, int width, int height, int stride, int depth);
    void		setGoalTiles(int goal) { myGoalTiles = goal; }

    void		build(ROOMTYPE_NAMES type);

    void		buildRogue();
    void		buildCave();
    void		buildBigRoom();
    void		buildMaze();
    void		buildMage();
    void		buildPalace();
    void		buildCaveRooms();
    void		buildFace(ROOMTYPE_NAMES type);
    void		buildWilderness(const TERRAIN_NAMES *terrain);
    void		buildOvermap();

    bool		connectRooms(const RCT &a, const RCT &b);

    int			width() const { return myW; }
    int			height() const { return myH; }

    bool		isValid(int x, int y) const { return (x >= 0 && x < myW && y >= 0 && y < myH); }
    bool		isValid(IVEC2 p) const { return isValid(p.x(), p.y()); }
    u8			get(int x, int y) const
			{ J_ASSERT(isValid(x, y)); return myData[x+y*myStride]; }
    u8			get(IVEC2 p) const
			{ return get(p.x(), p.y()); }
    u8			wrapget(int x, int y) const
			{ wrap(x, y); return myData[x+y*myStride]; }
    u8			wrapget(IVEC2 p) const
			{ wrap(p); return get(p.x(), p.y()); }
    void		set(int x, int y, u8 val)
			{ J_ASSERT(isValid(x, y)); myData[x+y*myStride] = val; }
    void		set(IVEC2 p, u8 val)
			{ set(p.x(), p.y(), val); }
    void		clear(u8 val)
			{ clear(0, 0, myW, myH, val); }
    void		clear(int x, int y, int w, int h, u8 val);

    // OP = [](IVEC2 loc, bool border)
    // Fills given rectangle, border is set to true if it is on the border.
    // Will clip to valid bounds.
    template <typename OP>
    void		drawBorderedRect(RCT r, const OP &op);

    void		replace(u8 orig, u8 newval);
    void		replace_chance(u8 orig, u8 newval, int chance);

    RCT			rand_rect(int minw, int minh, int maxw, int maxh) const;

    bool		findRandom(u8 val, int &x, int &y) const;
			
    void		savemap(const char *fname) const;

    void		cutWall(const WALLCUT &cut); // randomdoor
    void		cutWall(const WALLCUT &cut, bool usedoor);

    // Run forest growth logic.
    void		growBushes();
    void		growForests();

    // ADds mobs, stairs, etc.
    bool		dressLevel();
    bool		dressLevel(int nmob, int nitem);

    void		wrap(int &x, int &y) const
    { x = x % width();  if (x < 0) x += width();
      y = y % height(); if (y < 0) y += height(); }
    void		wrap(IVEC2 &p) const
    { wrap(p.x(), p.y()); }

    // OP = [](IVEC2 loc) -> bool, false to stop excavating early.
    // op is not called on start location.
    template <typename OP>
    void		drunkExcavate(IVEC2 startpos, int dist, const OP &op);
    // op is not called on start or end location
    template <typename OP>
    void		drunkExcavateBetween(IVEC2 startpos, IVEC2 endpos, int drunkness, const OP &op);

    // filter = [](IVEC2 loc) -> bool, finds largest rect where bool is
    // true starting at start pos.  May be empty if startpos invalid.
    // No implicit bounds checking.
    template <typename FILTER>
    RCT			findLargestRoom(IVEC2 startpos, const FILTER &filter) const;

    // Returns true if all squares pass the filter 
    template <typename FILTER>
    bool		testRect(RCT test, const FILTER &filter) const;


protected:
    u8		*myData;
    GRIDI	*myMetaData;
    int		 myW, myH, myStride;
    int	 	 myDepth;
    int		 myGoalTiles;
};

#endif
