// Copyright (c) 2017-2026 Lethean (https://lt.hn)
//
// Licensed under the European Union Public Licence (EUPL) version 1.2.
// SPDX-License-Identifier: EUPL-1.2

package wire

import (
	"encoding/binary"
	"io"
)

// Encoder writes consensus-critical binary data to an io.Writer.
// It uses a sticky error pattern: after the first write error, all
// subsequent writes become no-ops. Call Err() after a complete
// encoding sequence to check for failures.
type Encoder struct {
	w   io.Writer
	err error
	buf [10]byte // scratch for LE integers and varints
}

// NewEncoder creates a new Encoder writing to w.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w: w}
}

// Err returns the first error encountered during encoding.
func (e *Encoder) Err() error { return e.err }

// WriteUint8 writes a single byte.
func (e *Encoder) WriteUint8(v uint8) {
	if e.err != nil {
		return
	}
	e.buf[0] = v
	_, e.err = e.w.Write(e.buf[:1])
}

// WriteUint64LE writes a uint64 in little-endian byte order.
func (e *Encoder) WriteUint64LE(v uint64) {
	if e.err != nil {
		return
	}
	binary.LittleEndian.PutUint64(e.buf[:8], v)
	_, e.err = e.w.Write(e.buf[:8])
}

// WriteVarint writes a uint64 as a CryptoNote varint (LEB128).
func (e *Encoder) WriteVarint(v uint64) {
	if e.err != nil {
		return
	}
	b := EncodeVarint(v)
	_, e.err = e.w.Write(b)
}

// WriteBytes writes raw bytes with no length prefix.
func (e *Encoder) WriteBytes(b []byte) {
	if e.err != nil {
		return
	}
	if len(b) == 0 {
		return
	}
	_, e.err = e.w.Write(b)
}

// WriteBlob32 writes a 32-byte fixed-size blob (hash, public key, key image).
func (e *Encoder) WriteBlob32(b *[32]byte) {
	if e.err != nil {
		return
	}
	_, e.err = e.w.Write(b[:])
}

// WriteBlob64 writes a 64-byte fixed-size blob (signature).
func (e *Encoder) WriteBlob64(b *[64]byte) {
	if e.err != nil {
		return
	}
	_, e.err = e.w.Write(b[:])
}

// WriteVariantTag writes a single-byte variant discriminator.
func (e *Encoder) WriteVariantTag(tag uint8) {
	e.WriteUint8(tag)
}
