package sets

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strconv"
	"strings"

	"code.ur.gs/lupine/ordoor/internal/util/asciiscan"
)

type MapSet struct {
	Description string

	SurfaceCount int
	LeftCount    int
	RightCount   int
	CenterCount  int

	Palette []string

	// TODO: is there any more structure than this? Should I preserve empty lines?
	SurfacePalette []string
	LeftPalette    []string
	RightPalette   []string
	CenterPalette  []string
}

func (m *MapSet) Count() int {
	return m.SurfaceCount + m.LeftCount + m.RightCount + m.CenterCount
}

func LoadSets(dir string) (map[string]*MapSet, error) {
	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		return nil, err
	}

	out := make(map[string]*MapSet, len(fis))

	for _, fi := range fis {
		filename := filepath.Join(dir, fi.Name())
		basename := filepath.Base(filename)
		extname := filepath.Ext(filename)

		// Don't try to load non-.set files
		if !strings.EqualFold(extname, ".set") {
			continue
		}

		obj, err := LoadSet(filename)
		if err != nil {
			return nil, fmt.Errorf("%s: %v", filename, err)
		}

		out[basename] = obj
	}

	return out, nil
}

func LoadSet(filename string) (*MapSet, error) {
	out := &MapSet{}

	s, err := asciiscan.New(filename)
	if err != nil {
		return nil, err
	}

	defer s.Close()

	out.Description, err = s.ConsumeString()
	if err != nil {
		return nil, err
	}

	if err := consumeDefs(s, out); err != nil {
		return nil, err
	}

	fmt.Println(filename)
	out.Palette = make([]string, out.Count())
	offset := 0

	out.SurfacePalette = out.Palette[offset : offset+out.SurfaceCount : offset+out.SurfaceCount]
	offset += out.SurfaceCount

	out.LeftPalette = out.Palette[offset : offset+out.LeftCount : offset+out.LeftCount]
	offset += out.LeftCount

	out.RightPalette = out.Palette[offset : offset+out.RightCount : offset+out.RightCount]
	offset += out.RightCount

	out.CenterPalette = out.Palette[offset : offset+out.CenterCount : offset+out.CenterCount]
	offset += out.CenterCount

	if err := consumePalette(s, out.Palette); err != nil {
		return nil, fmt.Errorf("Failed to read palette: %v", err)
	}

	return out, nil
}

func consumeDefs(scanner *asciiscan.Scanner, in *MapSet) error {
	expectDefs, err := scanner.ConsumeString()
	if err != nil {
		return err
	}

	if expectDefs != "Defs" {
		return fmt.Errorf("Couldn't find Defs section")
	}

	defs, err := scanner.ConsumeString()
	if err != nil {
		return err
	}

	parts := strings.SplitN(defs, " ", -1)
	if len(parts) != 4 {
		return fmt.Errorf("Defs section did not have 4 components")
	}

	ints := make([]int, 4)

	for i, part := range parts {
		n, err := strconv.ParseInt(part, 10, 8)
		if err != nil {
			return err
		}

		ints[i] = int(n) // safe as we specify 8 bits to ParseInt
	}

	in.SurfaceCount = ints[0]
	in.LeftCount = ints[1]
	in.RightCount = ints[2]
	in.CenterCount = ints[3]

	return nil
}

func consumePalette(scanner *asciiscan.Scanner, into []string) error {
	for i := 0; i < len(into); i++ {
		str, err := scanner.ConsumeString()
		if err != nil {
			return err // EOF is bad: the number of entries is explicit
		}

		into[i] = str
	}

	return nil
}
