// KEKS -- Go KEKS codec implementation
// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
//
// This program 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, version 3 of the License.
//
// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

package keks

import (
	"math/big"

	"go.cypherpunks.su/keks/types"
	"go.cypherpunks.su/tai64n/v4"
)

// Iterate over parsed elements of Decoder. You call iter.Next() function to
// proceed to the next decoded element. Then you look at iter.T to determine
// the type of the element. Then you call necessary method to get the
// native Go's type. If the element is either list or map, then
// iter.Len() will tell you how many elements it contains and how many
// times you have to call Next to get them.
type Iterator struct {
	ctx *Decoder

	T     types.Type
	Depth int

	i        int
	bigints  int
	blobs    int
	bools    int
	ints     int
	lens     int
	raws     int
	strs     int
	tai64nas int
	tai64ns  int
	tai64s   int
	uints    int
	hexlets  int
	floats   int
}

func (ctx *Decoder) Iter() *Iterator {
	return &Iterator{ctx: ctx, i: -1, T: types.Invalid}
}

// Proceed to the next parsed element. Returns false if end is reached.
func (iter *Iterator) Next() bool {
	switch iter.T {
	case types.Bool:
		iter.bools++
	case types.Hexlet:
		iter.hexlets++
	case types.UInt:
		iter.uints++
	case types.Int:
		iter.ints++
	case types.BigInt:
		iter.bigints++
	case types.List, types.Map:
		iter.lens++
	case types.Blob:
		iter.blobs++
	case types.TAI64:
		iter.tai64s++
	case types.TAI64N:
		iter.tai64ns++
	case types.TAI64NA:
		iter.tai64nas++
	case types.Float:
		iter.floats++
	case types.Bin, types.Str, types.Magic:
		iter.strs++
	case types.Raw:
		iter.raws++
		iter.strs++
	}
	iter.i++
	if iter.i >= len(iter.ctx.types) {
		iter.T = types.Invalid
		return false
	}
	iter.T = iter.ctx.types[iter.i]
	iter.Depth = int(iter.ctx.depths[iter.i])
	return true
}

// Get item's absolute offset.
// Available only if was decoded with opts.SaveOffsets=true.
func (iter *Iterator) Offset() int64 {
	return iter.ctx.offsets[iter.i]
}

func (iter *Iterator) Bool() bool {
	return iter.ctx.bools[iter.bools]
}

func (iter *Iterator) Hexlet() *Hexlet {
	return iter.ctx.hexlets[iter.hexlets]
}

func (iter *Iterator) UInt() uint64 {
	return iter.ctx.uints[iter.uints]
}

func (iter *Iterator) Int() int64 {
	return iter.ctx.ints[iter.ints]
}

func (iter *Iterator) BigInt() *big.Int {
	return iter.ctx.bigints[iter.bigints]
}

func (iter *Iterator) Float() *Float {
	return iter.ctx.floats[iter.floats]
}

func (iter *Iterator) Blob() BlobChunked {
	return BlobChunked{
		ChunkLen: iter.ctx.blobChunkLens[iter.blobs],
		Chunks:   iter.ctx.blobChunkses[iter.blobs],
	}
}

func (iter *Iterator) TAI64() *tai64n.TAI64 {
	return &iter.ctx.tai64s[iter.tai64s]
}

func (iter *Iterator) TAI64N() *tai64n.TAI64N {
	return &iter.ctx.tai64ns[iter.tai64ns]
}

func (iter *Iterator) TAI64NA() *tai64n.TAI64NA {
	return &iter.ctx.tai64nas[iter.tai64nas]
}

func (iter *Iterator) Magic() Magic {
	return Magic(iter.ctx.strs[iter.strs])
}

func (iter *Iterator) Bin() []byte {
	return []byte(iter.ctx.strs[iter.strs])
}

func (iter *Iterator) Str() string {
	return iter.ctx.strs[iter.strs]
}

func (iter *Iterator) Raw() Raw {
	return Raw(append(
		[]byte{byte(iter.ctx.rawTypes[iter.raws])},
		iter.ctx.strs[iter.strs]...,
	))
}

func (iter *Iterator) Len() int {
	return iter.ctx.lens[iter.lens]
}
