//go:build !tinywasm

package gtree

import (
	"errors"
	"io"
	"iter"
)

// OutputFromRoot outputs tree to w.
// This function requires node generated by NewRoot function.
func OutputFromRoot(w io.Writer, root *Node, options ...Option) error {
	if err := validateTreeRoot(root); err != nil {
		return err
	}

	cfg := newConfig(options)
	return initializeTree(cfg).outputProgrammably(w, root, cfg)
}

// MkdirFromRoot makes directories.
// This function requires node generated by NewRoot function.
func MkdirFromRoot(root *Node, options ...Option) error {
	if err := validateTreeRoot(root); err != nil {
		return err
	}

	cfg := newConfig(options)
	return initializeTree(cfg).mkdirProgrammably(root, cfg)
}

// VerifyFromRoot verifies directory.
// This function requires node generated by NewRoot function.
func VerifyFromRoot(root *Node, options ...Option) error {
	if err := validateTreeRoot(root); err != nil {
		return err
	}

	cfg := newConfig(options)
	return initializeTree(cfg).verifyProgrammably(root, cfg)
}

// WalkFromRoot executes user-defined function while traversing tree structure recursively.
// This function requires node generated by NewRoot function.
func WalkFromRoot(root *Node, callback func(*WalkerNode) error, options ...Option) error {
	if err := validateTreeRoot(root); err != nil {
		return err
	}

	cfg := newConfig(options)
	return initializeTree(cfg).walkProgrammably(root, callback, cfg)
}

// WalkIterFromRoot returns each node resulting from recursively traversing tree structure.
// This function requires node generated by NewRoot function.
func WalkIterFromRoot(root *Node, options ...Option) iter.Seq2[*WalkerNode, error] {
	return func(yield func(*WalkerNode, error) bool) {
		if err := validateTreeRoot(root); err != nil {
			yield(nil, err)
			return
		}

		cfg := newConfig(options)
		cfg.massive = false // treePipeline で walkIterProgrammably は未実装で、treeSimple のを使わせてもらうため
		walkIter := initializeTree(cfg).walkIterProgrammably(root, cfg)
		next, stop := iter.Pull2(walkIter)
		defer stop()

		for {
			wn, err, ok := next()
			if !ok {
				return
			}
			if err != nil {
				yield(nil, err)
				return
			}
			if !yield(wn, nil) {
				return
			}
		}
	}
}

// NewRoot creates a starting node for building tree.
func NewRoot(text string, options ...NodeOption) *Node {
	return newNode(text, rootHierarchyNum, options...)
}

// Add adds a node and returns an instance of it.
// If a node with the same text already exists in the same hierarchy of the tree, that node will be returned.
// However, if the WithDuplicationAllowed function is specified for the NewRoot function, it will create and return a new node instance even if a node with the same text exists at the same hierarchy.
func (parent *Node) Add(text string) *Node {
	if !parent.allowDuplicates {
		if child := parent.findChildByText(text); child != nil {
			return child
		}
	}

	nodeOptions := []NodeOption{}
	if parent.allowDuplicates {
		nodeOptions = append(nodeOptions, WithDuplicationAllowed())
	}

	current := newNode(text, parent.hierarchy+1, nodeOptions...)
	current.setParent(parent)
	parent.addChild(current)
	return current
}

var (
	// ErrNilNode is returned if the argument *gtree.Node of OutputFromRoot / MkdirFromRoot / VerifyFromRoot / WalkFromRoot / WalkIterFromRoot function is nill.
	ErrNilNode = errors.New("nil node")
	// ErrNotRoot is returned if the argument *gtree.Node of OutputFromRoot / MkdirFromRoot / VerifyFromRoot / WalkFromRoot / WalkIterFromRoot function is not root of the tree.
	ErrNotRoot = errors.New("not root node")
)

func validateTreeRoot(root *Node) error {
	if root == nil {
		return ErrNilNode
	}
	if !root.isRoot() {
		return ErrNotRoot
	}
	return nil
}

// OutputProgrammably outputs tree to w.
// This function requires node generated by NewRoot function.
//
// Deprecated: Call OutputFromRoot.
//
//go:fix inline
func OutputProgrammably(w io.Writer, root *Node, options ...Option) error {
	return OutputFromRoot(w, root, options...)
}

// MkdirProgrammably makes directories.
// This function requires node generated by NewRoot function.
//
// Deprecated: Call MkdirFromRoot.
//
//go:fix inline
func MkdirProgrammably(root *Node, options ...Option) error {
	return MkdirFromRoot(root, options...)
}

// VerifyProgrammably verifies directory.
// This function requires node generated by NewRoot function.
//
// Deprecated: Call VerifyFromRoot.
//
//go:fix inline
func VerifyProgrammably(root *Node, options ...Option) error {
	return VerifyFromRoot(root, options...)
}

// WalkProgrammably executes user-defined function while traversing tree structure recursively.
// This function requires node generated by NewRoot function.
//
// Deprecated: Call WalkFromRoot.
//
//go:fix inline
func WalkProgrammably(root *Node, callback func(*WalkerNode) error, options ...Option) error {
	return WalkFromRoot(root, callback, options...)
}

// WalkIterProgrammably returns each node resulting from recursively traversing tree structure.
// This function requires node generated by NewRoot function.
//
// Deprecated: Call WalkIterFromRoot.
//
//go:fix inline
func WalkIterProgrammably(root *Node, options ...Option) iter.Seq2[*WalkerNode, error] {
	return WalkIterFromRoot(root, options...)
}
