package mapgens

import (
	"lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
	"lab.zaar.be/thefish/alchemyst-go/engine/types"
	"lab.zaar.be/thefish/alchemyst-go/util"
)
//fixme move to config
var minRoomSize = 3
var maxRoomSize = 22
var maxrooms = 30

//fixme make closure to stack them
func DefaultGen(l *gamemap.Level) *gamemap.Level {

	rng := util.NewRNG()

	//fill with walls
	for i := 0; i < l.W; i ++ {
		for j := 0; j < l.H; j++ {
			l.SetTileByXY(i, j, gamemap.NewWall())
		}
	}

	rooms := make([]*gamemap.Room, 0)

	for i := 0; i < maxrooms; i++ {
		newRoom := &gamemap.Room{
			Rect: types.NewRect(
				rng.Range(l.X, l.W),
				rng.Range(l.Y, l.H),
				rng.Range(minRoomSize, maxRoomSize),
				rng.Range(minRoomSize, maxRoomSize),
			)}
		newRoom.Center = types.Coords{newRoom.X + newRoom.W / 2, newRoom.Y + newRoom.H / 2}

		failed := false

		if !l.InBounds(types.Coords{newRoom.X, newRoom.Y}) {
			failed = true
		}

		if !failed && !l.InBounds(types.Coords{newRoom.X + newRoom.W, newRoom.Y + newRoom.H}) {
			failed = true
		}

		if !failed {
			for _, otherRoom := range rooms {
				if otherRoom.Intersects(newRoom.Rect) {
					failed = true
					break
				}
			}
		}

		if !failed {
			rooms = append(rooms, newRoom)
		}
	}

	//fillage := types.RectFill{
	//	Top: func() *gamemap.Tile {return gamemap.NewWall()},
	//	Bottom: func() *gamemap.Tile {return gamemap.NewWall()},
	//	Left: func() *gamemap.Tile {return gamemap.NewWall()},
	//	Right: func() *gamemap.Tile {return gamemap.NewWall()},
	//	BottomLeft: func() *gamemap.Tile {return gamemap.NewWall()},
	//	BottomRight: func() *gamemap.Tile {return gamemap.NewWall()},
	//	TopLeft: func() *gamemap.Tile {return gamemap.NewWall()},
	//	TopRight: func() *gamemap.Tile {return gamemap.NewWall()},
	//	Body: func() *gamemap.Tile {return gamemap.NewFloor()},
	//}

	fillage := types.RectFill{
		Top: func() *gamemap.Tile {return gamemap.NewWaterTile()},
		Bottom: func() *gamemap.Tile {return gamemap.NewWaterTile()},
		Left: func() *gamemap.Tile {return gamemap.NewWaterTile()},
		Right: func() *gamemap.Tile {return gamemap.NewWaterTile()},
		BottomLeft: func() *gamemap.Tile {return gamemap.NewWaterTile()},
		BottomRight: func() *gamemap.Tile {return gamemap.NewWaterTile()},
		TopLeft: func() *gamemap.Tile {return gamemap.NewWaterTile()},
		TopRight: func() *gamemap.Tile {return gamemap.NewWaterTile()},
		Body: func() *gamemap.Tile {return gamemap.NewDeepWaterTile()},
	}

	for idx, room := range rooms {
		room.Blit(fillage, l)
		if idx > 0 {
			connectRooms(l, room, rooms[idx-1], fillage, rng.Range(0,1))
		}
	}

	return l
}

func connectRooms (l *gamemap.Level, room, otherRoom *gamemap.Room, fillage types.RectFill, toss int) {
	if toss == 0 {
		digHTunnel(l, room.Center.X,otherRoom.Center.X,room.Center.Y, fillage)
		digVTunnel(l, room.Center.Y,otherRoom.Center.Y,otherRoom.Center.X, fillage)
	} else {
		digVTunnel(l, room.Center.Y, otherRoom.Center.Y, room.Center.Y, fillage)
		digHTunnel(l, room.Center.X, otherRoom.Center.X, otherRoom.Center.Y, fillage)
	}
}

func digHTunnel(l *gamemap.Level, x1,x2,y int, fillage types.RectFill) {
	var start, finish int
	if x1 < x2 {
		start = x1
		finish = x2
	} else {
		start = x2
		finish = x1
	}
	for i := start; i <= finish; i++ {
		if l.InBounds(types.Coords{i, y}) {
			l.SetTileByXY(i, y, fillage.Body.(func() *gamemap.Tile)())
			//l.Tiles[i][y] = gamemap.NewFloor()
		}
	}
}

func digVTunnel(l *gamemap.Level, y1,y2,x int, fillage types.RectFill) {
	var start, finish int
	if y1 < y2 {
		start = y1
		finish = y2
	} else {
		start = y2
		finish = y1
	}
	for i := start; i <= finish; i++ {
		if l.InBounds(types.Coords{x, i}) {
			l.SetTileByXY(x, i, fillage.Body.(func() *gamemap.Tile)())
		}
	}
}