// package idx parses the Idx/WarHammer.idx file. It groups the sprites in
// Anim/WarHammer.ani into playable animations.
package idx

import (
	"encoding/binary"
	"fmt"
	//"log"
	"os"
	//"strings"
)

const (
	NumGroups = 512 // Experimentally determined
)

type Idx struct {
	Filename string
	Groups   []Group
}

type Group struct {
	Spec    Spec
	Records []Record
	Details []Detail // Records and details are correlated by index
}

// type Spec links a set of animations to a starting sprite in WarHammer.ani
type Spec struct {
	Offset    uint32 // Where the Records for this Spec are to be found
	Count     uint32 // Number of Records for this Spec
	SpriteIdx uint32 // Index of the first sprite in
}

type Record struct {
	// A guess, but each group of 8 records with increasing compass points share
	// this value.
	ActionID uint16

	Compass byte // It's odd to only have one byte. Maybe Unknown1 belongs to this too?

	Unknown1  byte   // ??? Only see values 0x33 and 0x00 for librarian.
	Offset    uint32 // Where the Detail for this Record is to be found.
	NumFrames uint32 // A guess, but seems to fit. Number of frames for this action + compass.
}

type Detail struct {
	FirstSprite uint16 // Relative offset from the group's SpriteIdx
	LastSprite  uint16 // Relative offset from the group's SpriteIdx
	Unknown1    uint16 // Could also be LastSprite? Something else? AtRestSprite?
	Unknown2    uint16 // Number of resting sprites, if we're AtRestSprite?

	Padding [12]byte // Set to zero in the cases I've looked at so far.

	// Remainder []byte // FIXME: no idea what this is yet, but we seem to have NumFrames*6 of them
}

func Load(filename string) (*Idx, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	defer f.Close()

	out := &Idx{
		Filename: filename,
		Groups:   make([]Group, NumGroups),
	}

	var specs [NumGroups]Spec

	if err := binary.Read(f, binary.LittleEndian, &specs); err != nil {
		return nil, fmt.Errorf("reading specs: %v", err)
	}

	for i, spec := range specs {
		group := &out.Groups[i]
		group.Spec = spec
		group.Records = make([]Record, spec.Count)
		group.Details = make([]Detail, spec.Count)

		if _, err := f.Seek(int64(spec.Offset), 0); err != nil {
			return nil, fmt.Errorf("spec %v: seeking: %v", i, err)
		}

		// We can read all records at once
		if err := binary.Read(f, binary.LittleEndian, &group.Records); err != nil {
			return nil, fmt.Errorf("spec %v: reading records: %v", i, err)
		}

		// But we need to step through the records to learn where to read details
		for j, rec := range group.Records {
			// group.Details[j].Remainder = make([]byte, rec.NumFrames*6)

			if _, err := f.Seek(int64(rec.Offset), 0); err != nil {
				return nil, fmt.Errorf("spec %v, record %v: seeking to detail: %v", i, j, err)
			}

			if err := binary.Read(f, binary.LittleEndian, &group.Details[j]); err != nil {
				return nil, fmt.Errorf("spec %v, record %v: reading detail: %v", i, j, err)
			}
		}

		out.Groups[i] = *group
	}

	return out, nil
}
