package mapgens

import (
	"context"
	"lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
	"lab.zaar.be/thefish/alchemyst-go/engine/types"
	"lab.zaar.be/thefish/alchemyst-go/util"
	"lab.zaar.be/thefish/alchemyst-go/util/appctx"
)

//fixme move to config
var minRoomSize = 8
var maxRoomSize = 22
var maxrooms = 100

var fges = map[int]types.RectFill{
	1: types.RectFill{
		Top:         gamemap.NewWall,
		Bottom:      gamemap.NewWall,
		Left:        gamemap.NewWall,
		Right:       gamemap.NewWall,
		BottomLeft:  gamemap.NewWall,
		BottomRight: gamemap.NewWall,
		TopLeft:     gamemap.NewWall,
		TopRight:    gamemap.NewWall,
		Body:        gamemap.NewFloor,
	},

	2: types.RectFill{
		Top:         gamemap.NewWaterTile,
		Bottom:      gamemap.NewWaterTile,
		Left:        gamemap.NewWaterTile,
		Right:       gamemap.NewWaterTile,
		BottomLeft:  gamemap.NewWaterTile,
		BottomRight: gamemap.NewWaterTile,
		TopLeft:     gamemap.NewWaterTile,
		TopRight:    gamemap.NewWaterTile,
		Body:        gamemap.NewDeepWaterTile,
	},
}

func GetRandomRoomList(ctx context.Context, rng *util.RNG, l *gamemap.Level, maxRooms, minRoomSize, maxRoomSize int, ) []gamemap.Room{
	rooms := make([]gamemap.Room, 0)
	pfLoader := gamemap.NewPrefabLoader(ctx)
	pfRooms := pfLoader.PrefabRoomsList()

	var fillage types.RectFill
	prefabUsed := false

	for i := 0; i < maxRooms; i++ {
		failed := false
		fillage = fges[rng.GetWeightedEntity(map[int]int{1: 10, 2: 1})]



		var newRoom = gamemap.Room{}
		if !prefabUsed || rng.Range(0, 5) > 3 {
			//if prefabUsed {
			//prefab
			prefabUsed = true

			r := pfRooms[rng.Range(0, len(pfRooms))] //copy to local scope
			newRoom = gamemap.Room{
				Rect: r.Rect,
				Center: r.Center,
				Geometry: r.Geometry,
				Items: r.Items,
				Mobs: r.Mobs,
				Connectors: make([]types.Coords,0),
			}
			for _, coord := range r.Connectors {
				newRoom.Connectors = append(newRoom.Connectors, coord)
			}
		} else {
			newRoom = gamemap.NewRandomRectRoom(
				rng,
				rng.Range(minRoomSize, maxRoomSize),
				rng.Range(minRoomSize, maxRoomSize),
				fillage,
			)
		}
		where := types.Coords{
			rng.Range(1, l.W-2-newRoom.W),
			rng.Range(1, l.H-2-newRoom.H),
		}

		newRoom.MoveToCoords(where)

		for _, otherRoom := range rooms {
			if otherRoom.Intersects(newRoom.Rect) {
				failed = true
				break
			}
		}
		if failed {
			continue
		}
		if len(newRoom.Connectors) > 0 {
			rooms = append(rooms, newRoom)
		}
	}
	return rooms
}

func BlitToLevel (ctx context.Context, l *gamemap.Level, rooms[]gamemap.Room) {
	for _, room := range rooms {
		err := room.BlitToLevel(ctx, l)
		if err != nil {
			appctx.Logger(ctx).Err(err)
		}
	}
}

//delaunay helper funcs
func MedianStraight(rng *util.RNG, l *gamemap.Level, rooms []gamemap.Room, centers []types.Coords, edge types.Edge) {
	//find connected rooms
	var  fromRoom, toRoom gamemap.Room
	for _, room := range rooms {
		if room.Center == edge.From {
			fromRoom = room
			continue
		}
		if room.Center == edge.To {
			toRoom = room
			continue
		}
		if len(fromRoom.Connectors) > 0 && len(toRoom.Connectors) > 0 {
			break
		}
	}

	midpoint := edge.Midpoint()
	fromConnector := FindNearestConnector(midpoint, fromRoom)
	toConnector := FindNearestConnector(midpoint, toRoom)
	ConnectStraight(rng, l, fromConnector, toConnector, midpoint)
}

func FindNearestConnector(midpoint types.Coords, room gamemap.Room) types.Coords {
	var nearest types.Coords
	for _, con := range room.Connectors {
		if nearest.X == 0 {
			nearest = con
			continue
		}
		if midpoint.DistanceTo(con) < midpoint.DistanceTo(nearest) {
			nearest = con
		}
	}
	return nearest
}

func ConnectStraight(rng *util.RNG, l *gamemap.Level, from, to, midpoint types.Coords) {
	toss := rng.Range(1, 2)
	if toss > 1 {
		DigHTunnel(l, from.X, midpoint.X, from.Y)
		DigVTunnel(l, from.Y, to.Y, midpoint.X)
		DigHTunnel(l, midpoint.X, to.X, to.Y)
	} else {
		DigVTunnel(l, from.Y, midpoint.Y, from.X)
		DigHTunnel(l, from.X, to.X, midpoint.Y)
		DigVTunnel(l, midpoint.Y, to.Y, to.X)
	}
}

func DigHTunnel(l *gamemap.Level, x1, x2, y int) {
	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.MakePassByXY(i, y, gamemap.NewFloor())
			//l.Tiles[i][y] = gamemap.NewFloor()
		}
	}
}

func DigVTunnel(l *gamemap.Level, y1, y2, x int) {
	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.MakePassByXY(x, i, gamemap.NewFloor())
		}
	}
}

//default helper

func ConnectRoomCenters(l *gamemap.Level, room, otherRoom gamemap.Room, toss int) {
	if toss == 0 {
		DigHTunnel(l, room.Center.X, otherRoom.Center.X, room.Center.Y)
		DigVTunnel(l, room.Center.Y, otherRoom.Center.Y, otherRoom.Center.X)
	} else {
		DigVTunnel(l, room.Center.Y, otherRoom.Center.Y, room.Center.Y)
		DigHTunnel(l, room.Center.X, otherRoom.Center.X, otherRoom.Center.Y)
	}
}