// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package trie

import (
	"fmt"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethdb"

	"github.com/gballet/go-verkle"
)

type verkleNodeIteratorState struct {
	Node  verkle.VerkleNode
	Index int
}

type verkleNodeIterator struct {
	trie    *VerkleTrie
	current verkle.VerkleNode
	lastErr error

	stack []verkleNodeIteratorState
}

func newVerkleNodeIterator(trie *VerkleTrie, start []byte) NodeIterator {
	if trie.Hash() == emptyState {
		return new(nodeIterator)
	}
	it := &verkleNodeIterator{trie: trie, current: trie.root}
	//it.err = it.seek(start)
	return it
}

// Next moves the iterator to the next node. If the parameter is false, any child
// nodes will be skipped.
func (it *verkleNodeIterator) Next(descend bool) bool {
	if it.lastErr == errIteratorEnd {
		it.lastErr = errIteratorEnd
		return false
	}

	if len(it.stack) == 0 {
		it.stack = append(it.stack, verkleNodeIteratorState{Node: it.trie.root, Index: 0})
		it.current = it.trie.root

		return true
	}

	switch node := it.current.(type) {
	case *verkle.InternalNode:
		context := &it.stack[len(it.stack)-1]

		// Look for the next non-empty child
		children := node.Children()
		for ; context.Index < len(children); context.Index++ {
			if _, ok := children[context.Index].(verkle.Empty); !ok {
				it.stack = append(it.stack, verkleNodeIteratorState{Node: children[context.Index], Index: 0})
				it.current = children[context.Index]
				return it.Next(descend)
			}
		}

		// Reached the end of this node, go back to the parent, if
		// this isn't root.
		if len(it.stack) == 1 {
			it.lastErr = errIteratorEnd
			return false
		}
		it.stack = it.stack[:len(it.stack)-1]
		it.current = it.stack[len(it.stack)-1].Node
		it.stack[len(it.stack)-1].Index++
		return it.Next(descend)
	case *verkle.LeafNode:
		// Look for the next non-empty value
		for i := it.stack[len(it.stack)-1].Index + 1; i < 256; i++ {
			if node.Value(i) != nil {
				it.stack[len(it.stack)-1].Index = i
				return true
			}
		}
		// go back to parent to get the next leaf
		it.stack = it.stack[:len(it.stack)-1]
		it.current = it.stack[len(it.stack)-1].Node
		it.stack[len(it.stack)-1].Index++
		return it.Next(descend)
	case *verkle.HashedNode:
		// resolve the node
		data, err := it.trie.db.diskdb.Get(nodeToDBKey(node))
		if err != nil {
			panic(err)
		}
		it.current, err = verkle.ParseNode(data, len(it.stack)-1)
		if err != nil {
			panic(err)
		}

		// update the stack and parent with the resolved node
		it.stack[len(it.stack)-1].Node = it.current
		parent := &it.stack[len(it.stack)-2]
		parent.Node.(*verkle.InternalNode).SetChild(parent.Index, it.current)
		return true
	default:
		fmt.Println(node)
		panic("invalid node type")
	}
}

// Error returns the error status of the iterator.
func (it *verkleNodeIterator) Error() error {
	if it.lastErr == errIteratorEnd {
		return nil
	}
	return it.lastErr
}

// Hash returns the hash of the current node.
func (it *verkleNodeIterator) Hash() common.Hash {
	return it.current.ComputeCommitment().Bytes()
}

// Parent returns the hash of the parent of the current node. The hash may be the one
// grandparent if the immediate parent is an internal node with no hash.
func (it *verkleNodeIterator) Parent() common.Hash {
	return it.stack[len(it.stack)-1].Node.ComputeCommitment().Bytes()
}

// Path returns the hex-encoded path to the current node.
// Callers must not retain references to the return value after calling Next.
// For leaf nodes, the last element of the path is the 'terminator symbol' 0x10.
func (it *verkleNodeIterator) Path() []byte {

	panic("not completely implemented")
}

// Leaf returns true iff the current node is a leaf node.
func (it *verkleNodeIterator) Leaf() bool {
	_, ok := it.current.(*verkle.LeafNode)
	return ok
}

// LeafKey returns the key of the leaf. The method panics if the iterator is not
// positioned at a leaf. Callers must not retain references to the value after
// calling Next.
func (it *verkleNodeIterator) LeafKey() []byte {
	leaf, ok := it.current.(*verkle.LeafNode)
	if !ok {
		panic("Leaf() called on an verkle node iterator not at a leaf location")
	}

	return leaf.Key(it.stack[len(it.stack)-1].Index)
}

// LeafBlob returns the content of the leaf. The method panics if the iterator
// is not positioned at a leaf. Callers must not retain references to the value
// after calling Next.
func (it *verkleNodeIterator) LeafBlob() []byte {
	leaf, ok := it.current.(*verkle.LeafNode)
	if !ok {
		panic("LeafBlob() called on an verkle node iterator not at a leaf location")
	}

	return leaf.Value(it.stack[len(it.stack)-1].Index)
}

// LeafProof returns the Merkle proof of the leaf. The method panics if the
// iterator is not positioned at a leaf. Callers must not retain references
// to the value after calling Next.
func (it *verkleNodeIterator) LeafProof() [][]byte {
	_, ok := it.current.(*verkle.LeafNode)
	if !ok {
		panic("LeafProof() called on an verkle node iterator not at a leaf location")
	}

	//return it.trie.Prove(leaf.Key())
	panic("not completely implemented")
}

// AddResolver sets an intermediate database to use for looking up trie nodes
// before reaching into the real persistent layer.
//
// This is not required for normal operation, rather is an optimization for
// cases where trie nodes can be recovered from some external mechanism without
// reading from disk. In those cases, this resolver allows short circuiting
// accesses and returning them from memory.
//
// Before adding a similar mechanism to any other place in Geth, consider
// making trie.Database an interface and wrapping at that level. It's a huge
// refactor, but it could be worth it if another occurrence arises.
func (it *verkleNodeIterator) AddResolver(ethdb.KeyValueStore) {
	panic("not completely implemented")
}

type dummy struct{}

func (it dummy) Next(descend bool) bool {
	return false
}

func (it dummy) Error() error {
	return nil
}

func (it dummy) Hash() common.Hash {
	panic("should not be called")
}

func (it dummy) Leaf() bool {
	return false
}

func (it dummy) LeafKey() []byte {
	return nil
}

func (it dummy) LeafProof() [][]byte {
	return nil
}

func (it dummy) LeafBlob() []byte {
	return nil
}

func (it dummy) Parent() common.Hash {
	return common.Hash{}
}

func (it dummy) Path() []byte {
	return nil
}

func (it dummy) AddResolver(ethdb.KeyValueStore) {
	panic("not completely implemented")
}
