package gamemap

import (
	"errors"
	"fmt"
	"lab.zaar.be/thefish/alchemyst-go/engine/items"
	"lab.zaar.be/thefish/alchemyst-go/engine/mob"
	"lab.zaar.be/thefish/alchemyst-go/engine/types"
	"lab.zaar.be/thefish/alchemyst-go/util"
)

var invalidBlit = errors.New("trying to blit on existing good tile")

type Room struct {
	*types.Rect
	Center     types.Coords
	Geometry   []func() *Tile
	Mobs       []mob.Mob
	Items      []items.Carried
	Connectors []types.Coords
}

func (r *Room) Put (x, y int, tileFunc interface{}) {
	tf := tileFunc.(func() *Tile)
	if tf == nil {
		return //fixme error
	}
	if r.InBounds(types.Coords{x, y}) {
		r.Geometry[x+y*r.W] = tf
	}
}

func (room *Room) BlitToLevel(l *Level) error {
	//copy tiles like this:
	//https://stackoverflow.com/questions/21011023/copy-pointer-values-a-b-in-golang

	for j := 0; j < room.H; j++ {

		for i := 0; i < room.W; i++ {
			mapCoords := types.Coords{room.X + i, room.Y + j}
			underlyingTile := l.GetTile(mapCoords)

			tileFunc := room.Geometry[i+j*room.W]

			if tileFunc == nil {
				continue
			}
			//check underlying tile
			if underlyingTile == nil ||
				underlyingTile.Name != "Wall" {
				fmt.Println("Invalid blit!")
				return invalidBlit
			}
			l.Put(mapCoords.X, mapCoords.Y, tileFunc)
		}
	}

	return nil
}

func (room *Room) MoveToCoords(where types.Coords) *Room {
	//update room coords?
	room.X = where.X
	room.Y = where.Y
	//update centers!
	room.Center.X += where.X
	room.Center.Y += where.Y
	//update connector?
	for i, _ := range room.Connectors {
		room.Connectors[i].X += where.X
		room.Connectors[i].Y += where.Y
	}
	return room
}

func NewRandomRectRoom(rng *util.RNG, w, h int, fillage types.RectFill) *Room {
	newRoom := &Room{
		Rect: types.NewRect(
			0,
			0,
			w,
			h,
		),
		Center: types.Coords{w / 2, h /2 },
		Geometry: make([]func()*Tile, w*h),
	}
	newRoom.Blit(fillage, newRoom)
	//add connectors
	newRoom.Connectors = append(
		newRoom.Connectors,
		types.Coords{rng.Range(1, w - 2), 0},
		types.Coords{rng.Range(1, w - 2), h -1},
		types.Coords{0,  rng.Range(1, h - 2)},
		types.Coords{w - 1,  rng.Range(1, h - 2)},
	)
	return newRoom
}