package muesli

import (
	_ "bufio"
	_ "bytes"
	_ "errors"
	_ "fmt"
	_ "io"
	_ "os"
	_ "reflect"
	_ "runtime"
	_ "strings"
	_ "unicode"
	// "gitlab.com/beoran/woe/graphviz"
	// _ "gitlab.com/beoran/woe/monolog"
)

type AstKind int

const (
	AstKindProgram = AstKind(iota)
	AstKindStatements
	AstKindStatement
	AstKindSet
	AstKindGet
	AstKindCommand
	AstKindArguments
	AstKindBlock
	AstKindList
	AstKindCapture
	AstKindWordValue
	AstKindWord
	AstKindType
	AstKindValue
	AstKindEox
	AstKindError
)

type Parser struct {
	Lexer Lexer
	Ast
	next    *Token
	current *Token
}

func (parser *Parser) Peek() *Token {
	if parser.next == nil {
		token := parser.Lexer.Lex()
		parser.next = &token
	}
	return parser.next
}

func (parser *Parser) Next() *Token {
	next := parser.Peek()
	parser.current = next
	parser.next = nil
	parser.Peek()
	return parser.current
}

func (parser *Parser) Require(wanted TokenKind, astkind AstKind, parent *Ast) *Ast {
	token := parser.Next()
	if token.TokenKind == wanted {
		return parent.NewChild(astkind, token)
	}
	return parent.NewChild(AstKindError, token)
}

func (parser *Parser) ParseMany(
	astkind AstKind, parsefunc func(*Parser) *Ast) *Ast {
	ast := NewAst(astkind, nil, nil)
	for sub := parsefunc(parser); sub != nil && sub.AstKind != AstKindError; sub = parsefunc(parser) {
		ast.AppendChild(sub)
	}
	return ast
}

func (parser *Parser) NewAstError(message string) *Ast {
	sv := StringValue(message)
	pos := parser.current.Position
	tok := NewToken(TokenKindError, sv, pos)
	return NewAst(AstKindError, nil, &tok)
}

func (parser *Parser) ParseAny(astkind AstKind, parsefuncs ...(func(*Parser) *Ast)) *Ast {
	ast := NewAst(astkind, nil, nil)
	for _, parsefunc := range parsefuncs {
		sub := parsefunc(parser)
		if sub != nil {
			ast.AppendChild(sub)
			return ast
		}
	}
	err := parser.NewAstError("Unexpected token")
	ast.AppendChild(err)
	return ast
}

func (parser *Parser) ParseSet() *Ast {
	return nil
}

func (parser *Parser) ParseGet() *Ast {
	return nil
}

func (parser *Parser) ParseCommand() *Ast {
	return nil
}

func (parser *Parser) ParseStatement() *Ast {
	return parser.ParseAny(AstKindStatement,
		(*Parser).ParseSet, (*Parser).ParseGet, (*Parser).ParseCommand)
}

func (parser *Parser) ParseStatements() *Ast {
	return parser.ParseMany(AstKindStatements, (*Parser).ParseStatement)
}

func (parser *Parser) ParseProgram() *Ast {
	ast := NewAst(AstKindProgram, nil, nil)
	stats := parser.ParseStatements()
	ast.AppendChild(stats)
	return ast
}

func (parser *Parser) Parse() *Ast {
	ast := parser.ParseProgram()
	return ast
}