package main

import (
	"flag"
	"fmt"
	"log"
	"path/filepath"
	"strings"

	"code.ur.gs/lupine/ordoor/internal/data"
	"code.ur.gs/lupine/ordoor/internal/fonts"
	"code.ur.gs/lupine/ordoor/internal/idx"
	"code.ur.gs/lupine/ordoor/internal/maps"
	"code.ur.gs/lupine/ordoor/internal/menus"
	"code.ur.gs/lupine/ordoor/internal/sets"
)

var (
	gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation")
	skipObj  = flag.Bool("skip-obj", true, "Skip loading .obj files")
)

func main() {
	flag.Parse()

	loadData()

	if !*skipObj {
		loadObj()
	}

	loadMapsFrom("Maps")
	loadMapsFrom("MultiMaps")
	loadSets()
	loadMenus()
	loadFonts()
	loadIdx()
}

func loadData() {
	dataPath := filepath.Join(*gamePath, "Data")
	accountingPath := filepath.Join(dataPath, "Accounting.dat")
	aniObDefPath := filepath.Join(dataPath, "AniObDef.dat")
	genericDataPath := filepath.Join(dataPath, "GenericData.dat")
	hasActionPath := filepath.Join(dataPath, "HasAction.dat")
	i18nPath := filepath.Join(dataPath, data.I18nFile)

	log.Printf("Loading %s...", accountingPath)
	accounting, err := data.LoadAccounting(accountingPath)
	if err != nil {
		log.Fatalf("Failed to parse %s: %s", accountingPath, err)
	}

	log.Printf("%s: %+v", accountingPath, accounting)

	log.Printf("Loading %s...", aniObDefPath)
	animated, err := data.LoadAnimatedObjectDefinitions(aniObDefPath)
	if err != nil {
		log.Fatalf("Failed to parse %s: %s", genericDataPath, err)
	}

	log.Printf("%s: %+v", aniObDefPath, animated)

	log.Printf("Loading %s...", genericDataPath)
	genericData, err := data.LoadGeneric(genericDataPath)
	if err != nil {
		log.Fatalf("Failed to parse %s: %s", genericDataPath, err)
	}

	log.Printf("%s: %+v", genericDataPath, genericData)

	log.Printf("Loading %s...", i18nPath)
	i18n, err := data.LoadI18n(i18nPath)
	if err != nil {
		log.Fatalf("Failed to parse %s: %s", i18nPath, err)
	}

	log.Printf("%s: len=%v", i18nPath, i18n.Len())

	ha, err := data.LoadHasAction(hasActionPath)
	if err != nil {
		log.Fatalf("Failed to parse %s: %v", hasActionPath, err)
	}
	ha.Print()
}

func loadObj() {
	objDataPath := filepath.Join(*gamePath, "Obj")

	// TODO: Obj/cpiece.rec isn't loaded by this. Do we need it? How do we know?
	log.Printf("Loading %s...", objDataPath)
	objects, err := data.LoadObjects(objDataPath)
	if err != nil {
		log.Fatalf("Failed to parse %s: %s", objDataPath, err)
	}

	inspect := "c_webs"
	inspect_obj := inspect + ".obj"

	log.Printf("Objects in %s:", objDataPath)
	for key, obj := range objects {
		if key != inspect_obj {
			continue
		}
		log.Printf(
			"\t%s\t%d sprites\t%d bytes",
			key, obj.ObjectHeader.NumSprites, obj.ObjectHeader.DataSize,
		)
	}

	log.Printf("%s: %#v", inspect, objects[inspect_obj])

	for i, sprite := range objects[inspect_obj].Sprites {
		numPixels := sprite.Width * sprite.Height
		log.Printf("sprite %d: w=%d h=%d sz=%d w*h=%d bits/pixel=%v", i, sprite.Width, sprite.Height, sprite.PixelSize, numPixels, float64(sprite.PixelSize*8)/float64(numPixels))
	}
}

func loadMapsFrom(part string) {
	mapsPath := filepath.Join(*gamePath, part)
	log.Printf("Loading maps from %s", mapsPath)

	gameMaps, err := maps.LoadGameMaps(mapsPath)
	if err != nil {
		log.Fatalf("Failed to parse %s/*.{MAP,txt} as game maps: %v", mapsPath, err)
	}

	log.Printf("Maps in %s:", mapsPath)
	for key, gameMap := range gameMaps {
		hdr := gameMap.Header
		fmt.Printf(
			"    * `%s`: IsCampaignMap=%v W=%v:%v L=%v:%v SetName=%s\n",
			key,
			hdr.IsCampaignMap,
			hdr.MinWidth, hdr.MaxWidth,
			hdr.MinLength, hdr.MaxLength,
			string(hdr.SetName[:]),
		)
	}
}

func loadSets() {
	setsPath := filepath.Join(*gamePath, "Sets")
	log.Printf("Loading sets from %s", setsPath)

	mapSets, err := sets.LoadSets(setsPath)
	if err != nil {
		log.Fatalf("Failed to parse %s/*.set as map sets: %v", setsPath, err)
	}

	for key, mapSet := range mapSets {
		fmt.Printf("	* `%s`: surface expected=%d actual=%d\n", key, mapSet.SurfaceCount, len(mapSet.SurfacePalette))
		fmt.Printf("	* `%s`: left expected=%d actual=%d\n", key, mapSet.LeftCount, len(mapSet.LeftPalette))
		fmt.Printf("	* `%s`: right expected=%d actual=%d\n", key, mapSet.RightCount, len(mapSet.RightPalette))
		fmt.Printf("	* `%s`: center expected=%d actual=%d\n", key, mapSet.CenterCount, len(mapSet.CenterPalette))
	}
}

func loadMenus() {
	menusPath := filepath.Join(*gamePath, "Menu")

	menus, err := menus.LoadMenus(menusPath)
	if err != nil {
		log.Fatalf("Failed to parse %s/*.mnu as menus: %v", menusPath, err)
	}

	for _, menu := range menus {
		fmt.Printf("    * `%s`: objects=%v fonts=%v\n", menu.Name, menu.ObjectFiles, menu.FontNames)

		for _, group := range menu.Groups {
			// TODO: display group
			for _, record := range group.Records {
				displayRecord(record, 2)
			}
		}
	}
}

func displayRecord(record *menus.Record, depth int) {
	content := fmt.Sprintf("id=%v type=%v sprite=%v", record.ID, record.Type, record.SpriteId)

	if !record.Active {
		content = "(" + content + ")"
	}

	fmt.Printf("%s* %s\n", strings.Repeat("    ", depth), content)
}

func loadFonts() {
	fontsPath := filepath.Join(*gamePath, "Fonts")

	fonts, err := fonts.LoadFonts(fontsPath)
	if err != nil {
		log.Fatalf("Failed to parse %s/*.fnt as fonts: %v", fontsPath, err)
	}

	for _, font := range fonts {
		fmt.Printf("    * `%s`: obj=%v entries=%v\n", font.Name, font.ObjectFile, font.Entries())
	}
}

func loadIdx() {
	idxPath := filepath.Join(*gamePath, "Idx", "WarHammer.idx")
	idx, err := idx.Load(idxPath)
	if err != nil {
		log.Fatalf("Failed to parse %s as idx: %v", idxPath, err)
	}

	for i, group := range idx.Groups {
		log.Printf("Group %2d: %4d records, start sprite is %6d", i, len(group.Records), group.Spec.SpriteIdx)
	}
}
