// package asciiscan is used to parse plaintext files into structured data
package asciiscan

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"strconv"
	"strings"
)

var hashComment = []byte("#")

type Scanner struct {
	bufio  *bufio.Scanner
	closer io.Closer
}

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

	return &Scanner{
		bufio:  bufio.NewScanner(f),
		closer: f,
	}, nil
}

func (s *Scanner) Bufio() *bufio.Scanner {
	return s.bufio
}

func (s *Scanner) Close() error {
	return s.closer.Close()
}

func (s *Scanner) ConsumeString() (string, error) {
	for s.bufio.Scan() {
		line := s.bufio.Bytes()

		if len(line) == 0 || line[0] == hashComment[0] { // Most .dat files use # for comments
			continue
		}

		comment := bytes.Index(line, hashComment)
		if comment > 0 {
			line = line[0:comment]
		}

		return string(bytes.TrimRight(line, "\r\n\t ")), nil
	}

	err := s.bufio.Err()
	if err == nil {
		return "", io.EOF
	}

	return "", err
}

// It's common for properties to be specified as "foo : bar". Parse them out.
func ConsumeProperty(s string) (string, string) {
	if !IsProperty(s) {
		return "", ""
	}

	parts := strings.SplitN(s, ":", 2)
	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
}

// Peek ahead in the input stream to see if the next line might be a property
// (contain a colon character).
func IsProperty(s string) bool {
	return strings.Contains(s, ":")
}

func (s *Scanner) ConsumeInt() (int, error) {
	str, err := s.ConsumeString()
	if err != nil {
		return 0, err
	}

	return strconv.Atoi(str)
}

func (s *Scanner) ConsumeIntPtr(to *int) error {
	val, err := s.ConsumeInt()
	if err != nil {
		return err
	}

	*to = val
	return nil
}

func (s *Scanner) ConsumeIntPtrs(ptrs ...*int) error {
	for _, ptr := range ptrs {
		if err := s.ConsumeIntPtr(ptr); err != nil {
			return err
		}
	}

	return nil
}
