package gamemap

import (
	"errors"
	"fmt"
	"strings"

	"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"
	"lab.zaar.be/thefish/alchemyst-go/util/appctx"
)

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: x, Y: 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{X: room.X + i, Y: 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" {
				appctx.Logger().Warn().Msg("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 = room.Center.X + where.X
	room.Center.Y = room.Center.Y + where.Y
	//update connector?
	for i, coords := range room.Connectors {
		coords.X = coords.X + where.X
		coords.Y = coords.Y + where.Y
		room.Connectors[i] = coords
	}

	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{X: w / 2, Y: h /2 },
		Geometry: make([]func()*Tile, w*h),
	}
	newRoom.Blit(fillage, &newRoom)
	//add connectors
	newRoom.Connectors = append(
		newRoom.Connectors,
		types.Coords{X: rng.Range(1, w - 2), Y: 1},
		types.Coords{X: rng.Range(1, w - 2), Y: h -2},
		types.Coords{X: 1,  Y: rng.Range(1, h - 2)},
		types.Coords{X: w - 2,  Y: rng.Range(1, h - 2)},
	)
	return newRoom
}

func (r *Room) String() string {
	return strings.Join([]string{
		"room: ",
		"\t" + fmt.Sprintf(" rect: X: %d, Y: %d, maxX: %d, maxY: %d", r.Rect.X, r.Rect.Y, r.Rect.W + r.X - 1, r.Rect.H + r.Y - 1),
        "\t" + fmt.Sprintf(" center: %d, %d", r.Center.X, r.Center.Y),
		"\t" + fmt.Sprintf(" Connectors: %v", r.Connectors),
	},"\n") + "\n"
}
