package main

import (
	"errors"
	"io"
	"strings"
)

type levelPack struct {
	name   string
	order  []string
	levels map[string]level
}

func (p levelPack) find(level string) int {
	for i, l := range p.order {
		if l == level {
			return i
		}
	}
	return -1
}

func (p levelPack) FindNext(level string) (string, bool) {
	idx := p.find(level)
	if idx == -1 || idx == len(p.order)-1 {
		return "", false
	}
	return p.order[idx+1], true
}

func (p levelPack) Len() int { return len(p.order) }

type parseLevelPackContext struct {
	name   string
	levels []string
}

func parseLevelPackAsset(r io.Reader, openLevelFn func(id string) (io.ReadCloser, error)) (levelPack, error) {
	ctx := &parseLevelPackContext{}
	err := parseLines(r, ctx.Parse)
	if err != nil {
		return levelPack{}, err
	}

	pack := levelPack{name: ctx.name, levels: map[string]level{}}
	for _, id := range ctx.levels {
		rc, err := openLevelFn(id)
		if err != nil {
			return levelPack{}, err
		}
		defer rc.Close()
		level, err := parseLevelAsset(rc)
		if err != nil {
			return levelPack{}, err
		}
		pack.order = append(pack.order, id)
		pack.levels[id] = level
	}

	return pack, nil
}

func (c *parseLevelPackContext) Parse(p *lineParser) parseLineFn {
	if p.skipSpaceEOF() {
		return p.emitErr(errors.New("empty level pack"))
	}
	return c.parse
}

func (c *parseLevelPackContext) parse(p *lineParser) parseLineFn {
	const levelsTag = "levels:"
	const nameTag = "name:"

	if p.skipSpaceEOF() {
		return nil
	}

	line := p.next()
	switch {
	case strings.HasPrefix(line, nameTag):
		c.name = strings.TrimSpace(line[len(nameTag):])
		return c.parse
	case strings.HasPrefix(line, levelsTag):
		return skipSpaceBeforeContent(c.parseLevels)
	}
	return p.emitErr(errors.New("tag not allowed"))
}

func (c *parseLevelPackContext) parseLevels(p *lineParser) parseLineFn {
	const levelTag = "level:"
	const levelsEndTag = ":levels"

	line := p.next()
	switch line {
	case levelTag:
		return skipSpaceBeforeContent(c.parseLevel)
	case levelsEndTag:
		return c.parse
	}
	return p.emitErr(errors.New("tag not allowed"))
}

func (c *parseLevelPackContext) parseLevel(p *lineParser) parseLineFn {
	const idTag = "id:"

	line := p.next()
	switch {
	case strings.HasPrefix(line, idTag):
		c.levels = append(c.levels, strings.TrimSpace(line[len(idTag):]))
		return skipSpaceBeforeContent(c.parseLevelEnd)
	}
	return p.emitErr(errors.New("must have an id tag"))
}

func (c *parseLevelPackContext) parseLevelEnd(p *lineParser) parseLineFn {
	const levelEndTag = ":level"

	line := p.next()
	switch {
	case strings.HasPrefix(line, levelEndTag):
		return skipSpaceBeforeContent(c.parseLevels)
	}
	return p.emitErr(errors.New("tag not allowed"))
}
