package glob

import (
	"errors"
	"fmt"
	"unicode/utf8"
)

type node interface {
	children() []node
	append(node)
}

type nodeImpl struct {
	desc []node
}

func (n *nodeImpl) append(c node) {
	n.desc = append(n.desc, c)
}
func (n *nodeImpl) children() []node {
	return n.desc
}

type nodeList struct {
	nodeImpl
	not   bool
	chars string
}
type nodeRange struct {
	nodeImpl
	not    bool
	lo, hi rune
}
type nodeText struct {
	nodeImpl
	text string
}

type nodePattern struct{ nodeImpl }
type nodeAny struct{ nodeImpl }
type nodeSuper struct{ nodeImpl }
type nodeSingle struct{ nodeImpl }
type nodeAnyOf struct{ nodeImpl }

type tree struct {
	root    node
	current node
	path    []node
}

func (t *tree) enter(c node) {
	if t.root == nil {
		t.root = c
		t.current = c
		return
	}

	t.current.append(c)
	t.path = append(t.path, c)
	t.current = c
}

func (t *tree) leave() {
	if len(t.path)-1 <= 0 {
		t.current = t.root
		t.path = nil
		return
	}

	t.path = t.path[:len(t.path)-1]
	t.current = t.path[len(t.path)-1]
}

type parseFn func(*tree, *lexer) (parseFn, error)

func parse(lexer *lexer) (*nodePattern, error) {
	var parser parseFn

	root := &nodePattern{}
	tree := &tree{}
	tree.enter(root)

	for parser = parserMain; ; {
		next, err := parser(tree, lexer)
		if err != nil {
			return nil, err
		}

		if next == nil {
			break
		}

		parser = next
	}

	return root, nil
}

func parserMain(tree *tree, lexer *lexer) (parseFn, error) {
	for stop := false; !stop; {
		item := lexer.nextItem()

		switch item.t {
		case item_eof:
			stop = true
			continue

		case item_error:
			return nil, errors.New(item.s)

		case item_text:
			tree.current.append(&nodeText{text: item.s})
			return parserMain, nil

		case item_any:
			tree.current.append(&nodeAny{})
			return parserMain, nil

		case item_super:
			tree.current.append(&nodeSuper{})
			return parserMain, nil

		case item_single:
			tree.current.append(&nodeSingle{})
			return parserMain, nil

		case item_range_open:
			return parserRange, nil

		case item_terms_open:
			tree.enter(&nodeAnyOf{})
			tree.enter(&nodePattern{})
			return parserMain, nil

		case item_separator:
			tree.leave()
			tree.enter(&nodePattern{})
			return parserMain, nil

		case item_terms_close:
			tree.leave()
			tree.leave()
			return parserMain, nil

		default:
			return nil, fmt.Errorf("unexpected token: %s", item)
		}
	}

	return nil, nil
}

func parserRange(tree *tree, lexer *lexer) (parseFn, error) {
	var (
		not   bool
		lo    rune
		hi    rune
		chars string
	)

	for {
		item := lexer.nextItem()

		switch item.t {
		case item_eof:
			return nil, errors.New("unexpected end")

		case item_error:
			return nil, errors.New(item.s)

		case item_not:
			not = true

		case item_range_lo:
			r, w := utf8.DecodeRuneInString(item.s)
			if len(item.s) > w {
				return nil, fmt.Errorf("unexpected length of lo character")
			}

			lo = r

		case item_range_between:
			//

		case item_range_hi:
			r, w := utf8.DecodeRuneInString(item.s)
			if len(item.s) > w {
				return nil, fmt.Errorf("unexpected length of lo character")
			}

			hi = r

			if hi < lo {
				return nil, fmt.Errorf("hi character '%s' should be greater than lo '%s'", string(hi), string(lo))
			}

		case item_text:
			chars = item.s

		case item_range_close:
			isRange := lo != 0 && hi != 0
			isChars := chars != ""

			if isChars == isRange {
				return nil, fmt.Errorf("could not parse range")
			}

			if isRange {
				tree.current.append(&nodeRange{
					lo:  lo,
					hi:  hi,
					not: not,
				})
			} else {
				tree.current.append(&nodeList{
					chars: chars,
					not:   not,
				})
			}

			return parserMain, nil
		}
	}
}