/*
 * SPDX-FileCopyrightText: © 2017-2025 Istari Digital, Inc.
 * SPDX-License-Identifier: Apache-2.0
 */

package trie

import (
	
	
	

	
	
)

type node struct {
	children map[byte]*node
	ignore   *node
	ids      []uint64
}

func ( *node) () bool {
	return len(.children) == 0 && len(.ids) == 0 && .ignore == nil
}

func newNode() *node {
	return &node{
		children: make(map[byte]*node),
		ids:      []uint64{},
	}
}

// Trie datastructure.
type Trie struct {
	root *node
}

// NewTrie returns Trie.
func () *Trie {
	return &Trie{
		root: newNode(),
	}
}

// parseIgnoreBytes would parse the ignore string, and convert it into a list of bools, where
// bool[idx] = true implies that key[idx] can be ignored during comparison.
func parseIgnoreBytes( string) ([]bool, error) {
	var  []bool
	if  == "" {
		return , nil
	}

	for ,  := range strings.Split(strings.TrimSpace(), ",") {
		 := strings.Split(strings.TrimSpace(), "-")
		if len() == 0 || len() > 2 {
			return , fmt.Errorf("Invalid range: %s", )
		}
		,  := -1, -1 //nolint:ineffassign
		if len() == 2 {
			,  := strconv.Atoi(strings.TrimSpace([1]))
			if  != nil {
				return , 
			}
			 = 
		}
		{
			// Always consider r[0]
			,  := strconv.Atoi(strings.TrimSpace([0]))
			if  != nil {
				return , 
			}
			 = 
		}
		if  == -1 {
			return , fmt.Errorf("Invalid range: %s", )
		}
		for  >= len() {
			 = append(, false)
		}
		for  >= len() { // end could be -1, so do have the start loop above.
			 = append(, false)
		}
		if  == -1 {
			[] = true
		} else {
			for  := ;  <= ; ++ {
				[] = true
			}
		}
	}
	return , nil
}

// Add adds the id in the trie for the given prefix path.
func ( *Trie) ( []byte,  uint64) {
	 := pb.Match{
		Prefix: ,
	}
	y.Check(.AddMatch(, ))
}

// AddMatch allows you to send in a prefix match, with "holes" in the prefix. The holes are
// specified via IgnoreBytes in a comma-separated list of indices starting from 0. A dash can be
// used to denote a range. Valid example is "3, 5-8, 10, 12-15". Length of IgnoreBytes does not need
// to match the length of the Prefix passed.
//
// Consider a prefix = "aaaa". If the IgnoreBytes is set to "0, 2", then along with key "aaaa...",
// a key "baba..." would also match.
func ( *Trie) ( pb.Match,  uint64) error {
	return .fix(, , set)
}

const (
	set = iota
	del
)

func ( *Trie) ( pb.Match,  uint64,  int) error {
	 := .root

	,  := parseIgnoreBytes(.IgnoreBytes)
	if  != nil {
		return fmt.Errorf("while parsing ignore bytes: %s: %w", .IgnoreBytes, )
	}
	for len() < len(.Prefix) {
		 = append(, false)
	}
	for ,  := range .Prefix {
		var  *node
		if [] {
			 = .ignore
			if  == nil {
				if  == del {
					// No valid node found for delete operation. Return immediately.
					return nil
				}
				 = newNode()
				.ignore = 
			}
		} else {
			 = .children[]
			if  == nil {
				if  == del {
					// No valid node found for delete operation. Return immediately.
					return nil
				}
				 = newNode()
				.children[] = 
			}
		}
		 = 
	}

	// We only need to add the id to the last node of the given prefix.
	if  == set {
		.ids = append(.ids, )

	} else if  == del {
		 := .ids[:0]
		for ,  := range .ids {
			if  !=  {
				 = append(, )
			}
		}
		.ids = 
	} else {
		y.AssertTrue(false)
	}
	return nil
}

func ( *Trie) ( []byte) map[uint64]struct{} {
	return .get(.root, )
}

// Get returns prefix matched ids for the given key.
func ( *Trie) ( *node,  []byte) map[uint64]struct{} {
	y.AssertTrue( != nil)

	 := make(map[uint64]struct{})
	// If any node in the path of the key has ids, pick them up.
	// This would also match nil prefixes.
	for ,  := range .ids {
		[] = struct{}{}
	}
	if len() == 0 {
		return 
	}

	// If we found an ignore node, traverse that path.
	if .ignore != nil {
		 := .(.ignore, [1:])
		for  := range  {
			[] = struct{}{}
		}
	}

	if  := .children[[0]];  != nil {
		 := .(, [1:])
		for  := range  {
			[] = struct{}{}
		}
	}
	return 
}

func removeEmpty( *node) bool {
	// Go depth first.
	if .ignore != nil {
		if  := (.ignore);  {
			.ignore = nil
		}
	}

	for ,  := range .children {
		if  := ();  {
			delete(.children, )
		}
	}

	return .isEmpty()
}

// Delete will delete the id if the id exist in the given index path.
func ( *Trie) ( []byte,  uint64) error {
	return .DeleteMatch(pb.Match{Prefix: }, )
}

func ( *Trie) ( pb.Match,  uint64) error {
	if  := .fix(, , del);  != nil {
		return 
	}
	// Would recursively delete empty nodes.
	// Do not remove the t.root even if its empty.
	removeEmpty(.root)
	return nil
}

func numNodes( *node) int {
	if  == nil {
		return 0
	}

	 := (.ignore)
	for ,  := range .children {
		 += ()
	}
	return  + 1
}