/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package model

import (
	"context"
	stdErrors "errors"
	"fmt"

	"github.com/pkg/errors"
	"github.com/rs/zerolog"

	. "github.com/apache/plc4x/plc4go/spi/codegen/fields"
	. "github.com/apache/plc4x/plc4go/spi/codegen/io"
	"github.com/apache/plc4x/plc4go/spi/utils"
)

// Code generated by code-generation. DO NOT EDIT.

// BACnetReadAccessPropertyReadResult is the corresponding interface of BACnetReadAccessPropertyReadResult
type BACnetReadAccessPropertyReadResult interface {
	fmt.Stringer
	utils.LengthAware
	utils.Serializable
	utils.Copyable
	// GetPeekedTagHeader returns PeekedTagHeader (property field)
	GetPeekedTagHeader() BACnetTagHeader
	// GetPropertyValue returns PropertyValue (property field)
	GetPropertyValue() BACnetConstructedData
	// GetPropertyAccessError returns PropertyAccessError (property field)
	GetPropertyAccessError() ErrorEnclosed
	// GetPeekedTagNumber returns PeekedTagNumber (virtual field)
	GetPeekedTagNumber() uint8
	// IsBACnetReadAccessPropertyReadResult is a marker method to prevent unintentional type checks (interfaces of same signature)
	IsBACnetReadAccessPropertyReadResult()
	// CreateBuilder creates a BACnetReadAccessPropertyReadResultBuilder
	CreateBACnetReadAccessPropertyReadResultBuilder() BACnetReadAccessPropertyReadResultBuilder
}

// _BACnetReadAccessPropertyReadResult is the data-structure of this message
type _BACnetReadAccessPropertyReadResult struct {
	PeekedTagHeader     BACnetTagHeader
	PropertyValue       BACnetConstructedData
	PropertyAccessError ErrorEnclosed
}

var _ BACnetReadAccessPropertyReadResult = (*_BACnetReadAccessPropertyReadResult)(nil)

// NewBACnetReadAccessPropertyReadResult factory function for _BACnetReadAccessPropertyReadResult
func NewBACnetReadAccessPropertyReadResult(peekedTagHeader BACnetTagHeader, propertyValue BACnetConstructedData, propertyAccessError ErrorEnclosed) *_BACnetReadAccessPropertyReadResult {
	if peekedTagHeader == nil {
		panic("peekedTagHeader of type BACnetTagHeader for BACnetReadAccessPropertyReadResult must not be nil")
	}
	return &_BACnetReadAccessPropertyReadResult{PeekedTagHeader: peekedTagHeader, PropertyValue: propertyValue, PropertyAccessError: propertyAccessError}
}

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
/////////////////////// Builder
///////////////////////

// BACnetReadAccessPropertyReadResultBuilder is a builder for BACnetReadAccessPropertyReadResult
type BACnetReadAccessPropertyReadResultBuilder interface {
	utils.Copyable
	// WithMandatoryFields adds all mandatory fields (convenience for using multiple builder calls)
	WithMandatoryFields(peekedTagHeader BACnetTagHeader) BACnetReadAccessPropertyReadResultBuilder
	// WithPeekedTagHeader adds PeekedTagHeader (property field)
	WithPeekedTagHeader(BACnetTagHeader) BACnetReadAccessPropertyReadResultBuilder
	// WithPeekedTagHeaderBuilder adds PeekedTagHeader (property field) which is build by the builder
	WithPeekedTagHeaderBuilder(func(BACnetTagHeaderBuilder) BACnetTagHeaderBuilder) BACnetReadAccessPropertyReadResultBuilder
	// WithPropertyValue adds PropertyValue (property field)
	WithOptionalPropertyValue(BACnetConstructedData) BACnetReadAccessPropertyReadResultBuilder
	// WithOptionalPropertyValueBuilder adds PropertyValue (property field) which is build by the builder
	WithOptionalPropertyValueBuilder(func(BACnetConstructedDataBuilder) BACnetConstructedDataBuilder) BACnetReadAccessPropertyReadResultBuilder
	// WithPropertyAccessError adds PropertyAccessError (property field)
	WithOptionalPropertyAccessError(ErrorEnclosed) BACnetReadAccessPropertyReadResultBuilder
	// WithOptionalPropertyAccessErrorBuilder adds PropertyAccessError (property field) which is build by the builder
	WithOptionalPropertyAccessErrorBuilder(func(ErrorEnclosedBuilder) ErrorEnclosedBuilder) BACnetReadAccessPropertyReadResultBuilder
	// Build builds the BACnetReadAccessPropertyReadResult or returns an error if something is wrong
	Build() (BACnetReadAccessPropertyReadResult, error)
	// MustBuild does the same as Build but panics on error
	MustBuild() BACnetReadAccessPropertyReadResult
}

// NewBACnetReadAccessPropertyReadResultBuilder() creates a BACnetReadAccessPropertyReadResultBuilder
func NewBACnetReadAccessPropertyReadResultBuilder() BACnetReadAccessPropertyReadResultBuilder {
	return &_BACnetReadAccessPropertyReadResultBuilder{_BACnetReadAccessPropertyReadResult: new(_BACnetReadAccessPropertyReadResult)}
}

type _BACnetReadAccessPropertyReadResultBuilder struct {
	*_BACnetReadAccessPropertyReadResult

	collectedErr []error
}

var _ (BACnetReadAccessPropertyReadResultBuilder) = (*_BACnetReadAccessPropertyReadResultBuilder)(nil)

func (b *_BACnetReadAccessPropertyReadResultBuilder) WithMandatoryFields(peekedTagHeader BACnetTagHeader) BACnetReadAccessPropertyReadResultBuilder {
	return b.WithPeekedTagHeader(peekedTagHeader)
}

func (b *_BACnetReadAccessPropertyReadResultBuilder) WithPeekedTagHeader(peekedTagHeader BACnetTagHeader) BACnetReadAccessPropertyReadResultBuilder {
	b.PeekedTagHeader = peekedTagHeader
	return b
}

func (b *_BACnetReadAccessPropertyReadResultBuilder) WithPeekedTagHeaderBuilder(builderSupplier func(BACnetTagHeaderBuilder) BACnetTagHeaderBuilder) BACnetReadAccessPropertyReadResultBuilder {
	builder := builderSupplier(b.PeekedTagHeader.CreateBACnetTagHeaderBuilder())
	var err error
	b.PeekedTagHeader, err = builder.Build()
	if err != nil {
		b.collectedErr = append(b.collectedErr, errors.Wrap(err, "BACnetTagHeaderBuilder failed"))
	}
	return b
}

func (b *_BACnetReadAccessPropertyReadResultBuilder) WithOptionalPropertyValue(propertyValue BACnetConstructedData) BACnetReadAccessPropertyReadResultBuilder {
	b.PropertyValue = propertyValue
	return b
}

func (b *_BACnetReadAccessPropertyReadResultBuilder) WithOptionalPropertyValueBuilder(builderSupplier func(BACnetConstructedDataBuilder) BACnetConstructedDataBuilder) BACnetReadAccessPropertyReadResultBuilder {
	builder := builderSupplier(b.PropertyValue.CreateBACnetConstructedDataBuilder())
	var err error
	b.PropertyValue, err = builder.Build()
	if err != nil {
		b.collectedErr = append(b.collectedErr, errors.Wrap(err, "BACnetConstructedDataBuilder failed"))
	}
	return b
}

func (b *_BACnetReadAccessPropertyReadResultBuilder) WithOptionalPropertyAccessError(propertyAccessError ErrorEnclosed) BACnetReadAccessPropertyReadResultBuilder {
	b.PropertyAccessError = propertyAccessError
	return b
}

func (b *_BACnetReadAccessPropertyReadResultBuilder) WithOptionalPropertyAccessErrorBuilder(builderSupplier func(ErrorEnclosedBuilder) ErrorEnclosedBuilder) BACnetReadAccessPropertyReadResultBuilder {
	builder := builderSupplier(b.PropertyAccessError.CreateErrorEnclosedBuilder())
	var err error
	b.PropertyAccessError, err = builder.Build()
	if err != nil {
		b.collectedErr = append(b.collectedErr, errors.Wrap(err, "ErrorEnclosedBuilder failed"))
	}
	return b
}

func (b *_BACnetReadAccessPropertyReadResultBuilder) Build() (BACnetReadAccessPropertyReadResult, error) {
	if b.PeekedTagHeader == nil {
		b.collectedErr = append(b.collectedErr, errors.New("mandatory field 'peekedTagHeader' not set"))
	}
	if err := stdErrors.Join(b.collectedErr...); err != nil {
		return nil, errors.Wrap(err, "error occurred during build")
	}
	return b._BACnetReadAccessPropertyReadResult.deepCopy(), nil
}

func (b *_BACnetReadAccessPropertyReadResultBuilder) MustBuild() BACnetReadAccessPropertyReadResult {
	build, err := b.Build()
	if err != nil {
		panic(err)
	}
	return build
}

func (b *_BACnetReadAccessPropertyReadResultBuilder) DeepCopy() any {
	_copy := b.CreateBACnetReadAccessPropertyReadResultBuilder().(*_BACnetReadAccessPropertyReadResultBuilder)
	if b.collectedErr != nil {
		copy(_copy.collectedErr, b.collectedErr)
	}
	return _copy
}

// CreateBACnetReadAccessPropertyReadResultBuilder creates a BACnetReadAccessPropertyReadResultBuilder
func (b *_BACnetReadAccessPropertyReadResult) CreateBACnetReadAccessPropertyReadResultBuilder() BACnetReadAccessPropertyReadResultBuilder {
	if b == nil {
		return NewBACnetReadAccessPropertyReadResultBuilder()
	}
	return &_BACnetReadAccessPropertyReadResultBuilder{_BACnetReadAccessPropertyReadResult: b.deepCopy()}
}

///////////////////////
///////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
/////////////////////// Accessors for property fields.
///////////////////////

func (m *_BACnetReadAccessPropertyReadResult) GetPeekedTagHeader() BACnetTagHeader {
	return m.PeekedTagHeader
}

func (m *_BACnetReadAccessPropertyReadResult) GetPropertyValue() BACnetConstructedData {
	return m.PropertyValue
}

func (m *_BACnetReadAccessPropertyReadResult) GetPropertyAccessError() ErrorEnclosed {
	return m.PropertyAccessError
}

///////////////////////
///////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
/////////////////////// Accessors for virtual fields.
///////////////////////

func (m *_BACnetReadAccessPropertyReadResult) GetPeekedTagNumber() uint8 {
	ctx := context.Background()
	_ = ctx
	propertyValue := m.GetPropertyValue()
	_ = propertyValue
	propertyAccessError := m.GetPropertyAccessError()
	_ = propertyAccessError
	return uint8(m.GetPeekedTagHeader().GetActualTagNumber())
}

///////////////////////
///////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////

// Deprecated: use the interface for direct cast
func CastBACnetReadAccessPropertyReadResult(structType any) BACnetReadAccessPropertyReadResult {
	if casted, ok := structType.(BACnetReadAccessPropertyReadResult); ok {
		return casted
	}
	if casted, ok := structType.(*BACnetReadAccessPropertyReadResult); ok {
		return *casted
	}
	return nil
}

func (m *_BACnetReadAccessPropertyReadResult) GetTypeName() string {
	return "BACnetReadAccessPropertyReadResult"
}

func (m *_BACnetReadAccessPropertyReadResult) GetLengthInBits(ctx context.Context) uint16 {
	lengthInBits := uint16(0)

	// A virtual field doesn't have any in- or output.

	// Optional Field (propertyValue)
	if m.PropertyValue != nil {
		lengthInBits += m.PropertyValue.GetLengthInBits(ctx)
	}

	// Optional Field (propertyAccessError)
	if m.PropertyAccessError != nil {
		lengthInBits += m.PropertyAccessError.GetLengthInBits(ctx)
	}

	return lengthInBits
}

func (m *_BACnetReadAccessPropertyReadResult) GetLengthInBytes(ctx context.Context) uint16 {
	return m.GetLengthInBits(ctx) / 8
}

func BACnetReadAccessPropertyReadResultParse(ctx context.Context, theBytes []byte, objectTypeArgument BACnetObjectType, propertyIdentifierArgument BACnetPropertyIdentifier, arrayIndexArgument BACnetTagPayloadUnsignedInteger) (BACnetReadAccessPropertyReadResult, error) {
	return BACnetReadAccessPropertyReadResultParseWithBuffer(ctx, utils.NewReadBufferByteBased(theBytes), objectTypeArgument, propertyIdentifierArgument, arrayIndexArgument)
}

func BACnetReadAccessPropertyReadResultParseWithBufferProducer(objectTypeArgument BACnetObjectType, propertyIdentifierArgument BACnetPropertyIdentifier, arrayIndexArgument BACnetTagPayloadUnsignedInteger) func(ctx context.Context, readBuffer utils.ReadBuffer) (BACnetReadAccessPropertyReadResult, error) {
	return func(ctx context.Context, readBuffer utils.ReadBuffer) (BACnetReadAccessPropertyReadResult, error) {
		return BACnetReadAccessPropertyReadResultParseWithBuffer(ctx, readBuffer, objectTypeArgument, propertyIdentifierArgument, arrayIndexArgument)
	}
}

func BACnetReadAccessPropertyReadResultParseWithBuffer(ctx context.Context, readBuffer utils.ReadBuffer, objectTypeArgument BACnetObjectType, propertyIdentifierArgument BACnetPropertyIdentifier, arrayIndexArgument BACnetTagPayloadUnsignedInteger) (BACnetReadAccessPropertyReadResult, error) {
	v, err := (new(_BACnetReadAccessPropertyReadResult)).parse(ctx, readBuffer, objectTypeArgument, propertyIdentifierArgument, arrayIndexArgument)
	if err != nil {
		return nil, err
	}
	return v, nil
}

func (m *_BACnetReadAccessPropertyReadResult) parse(ctx context.Context, readBuffer utils.ReadBuffer, objectTypeArgument BACnetObjectType, propertyIdentifierArgument BACnetPropertyIdentifier, arrayIndexArgument BACnetTagPayloadUnsignedInteger) (__bACnetReadAccessPropertyReadResult BACnetReadAccessPropertyReadResult, err error) {
	positionAware := readBuffer
	_ = positionAware
	if pullErr := readBuffer.PullContext("BACnetReadAccessPropertyReadResult"); pullErr != nil {
		return nil, errors.Wrap(pullErr, "Error pulling for BACnetReadAccessPropertyReadResult")
	}
	currentPos := positionAware.GetPos()
	_ = currentPos

	peekedTagHeader, err := ReadPeekField[BACnetTagHeader](ctx, "peekedTagHeader", ReadComplex[BACnetTagHeader](BACnetTagHeaderParseWithBuffer, readBuffer), 0)
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'peekedTagHeader' field"))
	}
	m.PeekedTagHeader = peekedTagHeader

	peekedTagNumber, err := ReadVirtualField[uint8](ctx, "peekedTagNumber", (*uint8)(nil), peekedTagHeader.GetActualTagNumber())
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'peekedTagNumber' field"))
	}
	_ = peekedTagNumber

	var propertyValue BACnetConstructedData
	_propertyValue, err := ReadOptionalField[BACnetConstructedData](ctx, "propertyValue", ReadComplex[BACnetConstructedData](BACnetConstructedDataParseWithBufferProducer[BACnetConstructedData]((uint8)(uint8(4)), (BACnetObjectType)(objectTypeArgument), (BACnetPropertyIdentifier)(propertyIdentifierArgument), (BACnetTagPayloadUnsignedInteger)(arrayIndexArgument)), readBuffer), bool((peekedTagNumber) == (4)))
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'propertyValue' field"))
	}
	if _propertyValue != nil {
		propertyValue = *_propertyValue
		m.PropertyValue = propertyValue
	}

	// Validation
	if !(bool((bool(bool((peekedTagNumber) == (4))) && bool(bool((propertyValue) != (nil))))) || bool(bool((peekedTagNumber) != (4)))) {
		return nil, errors.WithStack(utils.ParseValidationError{Message: "failure parsing field 4"})
	}

	var propertyAccessError ErrorEnclosed
	_propertyAccessError, err := ReadOptionalField[ErrorEnclosed](ctx, "propertyAccessError", ReadComplex[ErrorEnclosed](ErrorEnclosedParseWithBufferProducer((uint8)(uint8(5))), readBuffer), bool((peekedTagNumber) == (5)))
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'propertyAccessError' field"))
	}
	if _propertyAccessError != nil {
		propertyAccessError = *_propertyAccessError
		m.PropertyAccessError = propertyAccessError
	}

	// Validation
	if !(bool((bool(bool((peekedTagNumber) == (5))) && bool(bool((propertyAccessError) != (nil))))) || bool(bool((peekedTagNumber) != (5)))) {
		return nil, errors.WithStack(utils.ParseValidationError{Message: "failure parsing field 5"})
	}

	// Validation
	if !(bool(bool((peekedTagNumber) == (4))) || bool(bool((peekedTagNumber) == (5)))) {
		return nil, errors.WithStack(utils.ParseAssertError{Message: "should be either 4 or 5"})
	}

	if closeErr := readBuffer.CloseContext("BACnetReadAccessPropertyReadResult"); closeErr != nil {
		return nil, errors.Wrap(closeErr, "Error closing for BACnetReadAccessPropertyReadResult")
	}

	return m, nil
}

func (m *_BACnetReadAccessPropertyReadResult) Serialize() ([]byte, error) {
	wb := utils.NewWriteBufferByteBased(utils.WithInitialSizeForByteBasedBuffer(int(m.GetLengthInBytes(context.Background()))))
	if err := m.SerializeWithWriteBuffer(context.Background(), wb); err != nil {
		return nil, err
	}
	return wb.GetBytes(), nil
}

func (m *_BACnetReadAccessPropertyReadResult) SerializeWithWriteBuffer(ctx context.Context, writeBuffer utils.WriteBuffer) error {
	positionAware := writeBuffer
	_ = positionAware
	log := zerolog.Ctx(ctx)
	_ = log
	if pushErr := writeBuffer.PushContext("BACnetReadAccessPropertyReadResult"); pushErr != nil {
		return errors.Wrap(pushErr, "Error pushing for BACnetReadAccessPropertyReadResult")
	}
	// Virtual field
	peekedTagNumber := m.GetPeekedTagNumber()
	_ = peekedTagNumber
	if _peekedTagNumberErr := writeBuffer.WriteVirtual(ctx, "peekedTagNumber", m.GetPeekedTagNumber()); _peekedTagNumberErr != nil {
		return errors.Wrap(_peekedTagNumberErr, "Error serializing 'peekedTagNumber' field")
	}

	if err := WriteOptionalField[BACnetConstructedData](ctx, "propertyValue", GetRef(m.GetPropertyValue()), WriteComplex[BACnetConstructedData](writeBuffer), true); err != nil {
		return errors.Wrap(err, "Error serializing 'propertyValue' field")
	}

	if err := WriteOptionalField[ErrorEnclosed](ctx, "propertyAccessError", GetRef(m.GetPropertyAccessError()), WriteComplex[ErrorEnclosed](writeBuffer), true); err != nil {
		return errors.Wrap(err, "Error serializing 'propertyAccessError' field")
	}

	if popErr := writeBuffer.PopContext("BACnetReadAccessPropertyReadResult"); popErr != nil {
		return errors.Wrap(popErr, "Error popping for BACnetReadAccessPropertyReadResult")
	}
	return nil
}

func (m *_BACnetReadAccessPropertyReadResult) IsBACnetReadAccessPropertyReadResult() {}

func (m *_BACnetReadAccessPropertyReadResult) DeepCopy() any {
	return m.deepCopy()
}

func (m *_BACnetReadAccessPropertyReadResult) deepCopy() *_BACnetReadAccessPropertyReadResult {
	if m == nil {
		return nil
	}
	_BACnetReadAccessPropertyReadResultCopy := &_BACnetReadAccessPropertyReadResult{
		utils.DeepCopy[BACnetTagHeader](m.PeekedTagHeader),
		utils.DeepCopy[BACnetConstructedData](m.PropertyValue),
		utils.DeepCopy[ErrorEnclosed](m.PropertyAccessError),
	}
	return _BACnetReadAccessPropertyReadResultCopy
}

func (m *_BACnetReadAccessPropertyReadResult) String() string {
	if m == nil {
		return "<nil>"
	}
	wb := utils.NewWriteBufferBoxBased(
		utils.WithWriteBufferBoxBasedMergeSingleBoxes(),
		utils.WithWriteBufferBoxBasedOmitEmptyBoxes(),
		utils.WithWriteBufferBoxBasedPrintPosLengthFooter(),
	)
	if err := wb.WriteSerializable(context.Background(), m); err != nil {
		return err.Error()
	}
	return wb.GetBox().String()
}
