package assetstore

import (
	"fmt"
	"image"
	"log"

	"code.ur.gs/lupine/ordoor/internal/maps"
)

type Map struct {
	assets *AssetStore
	set    *Set
	Rect   image.Rectangle

	raw *maps.GameMap
}

// Map loads a game map with the given name (e.g. "Chapter01")
func (a *AssetStore) Map(name string) (*Map, error) {
	name = canonical(name)

	if m, ok := a.maps[name]; ok {
		return m, nil
	}
	log.Printf("Loading map %v", name)

	mapFile, err := a.lookup(name, "map", "Maps", "MultiMaps")
	if err != nil {
		return nil, err
	}

	txtFile, err := a.lookup(name, "txt", "Maps", "MultiMaps")
	if err != nil {
		return nil, err
	}

	raw, err := maps.LoadGameMapByFiles(mapFile, txtFile)
	if err != nil {
		return nil, err
	}

	// The set for a map is small and frequently referenced, so load it here
	set, err := a.Set(raw.MapSetName())
	if err != nil {
		return nil, err
	}

	m := &Map{
		Rect: raw.Rect(),
		assets: a,
		raw:    raw,
		set:    set,
	}

	a.maps[canonical(name)] = m

	return m, nil
}

func (m *Map) LoadSprites() error {
	// Eager load the sprites we use
	for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
		for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
			for z := 0; z < maps.MaxHeight; z++ {
				if _, err := m.SpritesForCell(x, y, z); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

// FIXME: get rid of this
func (m *Map) Cell(x, y, z int) maps.Cell {
	return m.raw.Cells.At(x, y, z)
}

// SpritesForCell returns the sprites needed to correctly render this cell.
// They should be rendered from first to last to get the correct ordering
func (m *Map) SpritesForCell(x, y, z int) ([]*Sprite, error) {
	cell := m.raw.At(x, y, z)
	sprites := make([]*Sprite, 0, 4)

	for _, ref := range []maps.ObjRef{cell.Surface, cell.Right, cell.Left, cell.Center} {
		if !ref.IsActive() {
			continue
		}

		obj, err := m.set.Object(ref.Index())
		if err != nil {
			return nil, fmt.Errorf("Failed to get object for %#+v: %v", ref, err)
		}

		sprite, err := obj.Sprite(ref.Sprite())
		if err != nil {
			return nil, err
		}

		sprites = append(sprites, sprite)
	}

	return sprites, nil
}
