/*
 * 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.

// CALReplyLong is the corresponding interface of CALReplyLong
type CALReplyLong interface {
	fmt.Stringer
	utils.LengthAware
	utils.Serializable
	utils.Copyable
	CALReply
	// GetTerminatingByte returns TerminatingByte (property field)
	GetTerminatingByte() uint32
	// GetUnitAddress returns UnitAddress (property field)
	GetUnitAddress() UnitAddress
	// GetBridgeAddress returns BridgeAddress (property field)
	GetBridgeAddress() BridgeAddress
	// GetSerialInterfaceAddress returns SerialInterfaceAddress (property field)
	GetSerialInterfaceAddress() SerialInterfaceAddress
	// GetReservedByte returns ReservedByte (property field)
	GetReservedByte() *byte
	// GetReplyNetwork returns ReplyNetwork (property field)
	GetReplyNetwork() ReplyNetwork
	// GetIsUnitAddress returns IsUnitAddress (virtual field)
	// TODO: this should be subSub type but mspec doesn't support that yet directly
	GetIsUnitAddress() bool
	// IsCALReplyLong is a marker method to prevent unintentional type checks (interfaces of same signature)
	IsCALReplyLong()
	// CreateBuilder creates a CALReplyLongBuilder
	CreateCALReplyLongBuilder() CALReplyLongBuilder
}

// _CALReplyLong is the data-structure of this message
type _CALReplyLong struct {
	CALReplyContract
	TerminatingByte        uint32
	UnitAddress            UnitAddress
	BridgeAddress          BridgeAddress
	SerialInterfaceAddress SerialInterfaceAddress
	ReservedByte           *byte
	ReplyNetwork           ReplyNetwork
	// Reserved Fields
	reservedField0 *byte
}

var _ CALReplyLong = (*_CALReplyLong)(nil)
var _ CALReplyRequirements = (*_CALReplyLong)(nil)

// NewCALReplyLong factory function for _CALReplyLong
func NewCALReplyLong(calType byte, calData CALData, terminatingByte uint32, unitAddress UnitAddress, bridgeAddress BridgeAddress, serialInterfaceAddress SerialInterfaceAddress, reservedByte *byte, replyNetwork ReplyNetwork) *_CALReplyLong {
	if serialInterfaceAddress == nil {
		panic("serialInterfaceAddress of type SerialInterfaceAddress for CALReplyLong must not be nil")
	}
	_result := &_CALReplyLong{
		CALReplyContract:       NewCALReply(calType, calData),
		TerminatingByte:        terminatingByte,
		UnitAddress:            unitAddress,
		BridgeAddress:          bridgeAddress,
		SerialInterfaceAddress: serialInterfaceAddress,
		ReservedByte:           reservedByte,
		ReplyNetwork:           replyNetwork,
	}
	_result.CALReplyContract.(*_CALReply)._SubType = _result
	return _result
}

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

// CALReplyLongBuilder is a builder for CALReplyLong
type CALReplyLongBuilder interface {
	utils.Copyable
	// WithMandatoryFields adds all mandatory fields (convenience for using multiple builder calls)
	WithMandatoryFields(terminatingByte uint32, serialInterfaceAddress SerialInterfaceAddress) CALReplyLongBuilder
	// WithTerminatingByte adds TerminatingByte (property field)
	WithTerminatingByte(uint32) CALReplyLongBuilder
	// WithUnitAddress adds UnitAddress (property field)
	WithOptionalUnitAddress(UnitAddress) CALReplyLongBuilder
	// WithOptionalUnitAddressBuilder adds UnitAddress (property field) which is build by the builder
	WithOptionalUnitAddressBuilder(func(UnitAddressBuilder) UnitAddressBuilder) CALReplyLongBuilder
	// WithBridgeAddress adds BridgeAddress (property field)
	WithOptionalBridgeAddress(BridgeAddress) CALReplyLongBuilder
	// WithOptionalBridgeAddressBuilder adds BridgeAddress (property field) which is build by the builder
	WithOptionalBridgeAddressBuilder(func(BridgeAddressBuilder) BridgeAddressBuilder) CALReplyLongBuilder
	// WithSerialInterfaceAddress adds SerialInterfaceAddress (property field)
	WithSerialInterfaceAddress(SerialInterfaceAddress) CALReplyLongBuilder
	// WithSerialInterfaceAddressBuilder adds SerialInterfaceAddress (property field) which is build by the builder
	WithSerialInterfaceAddressBuilder(func(SerialInterfaceAddressBuilder) SerialInterfaceAddressBuilder) CALReplyLongBuilder
	// WithReservedByte adds ReservedByte (property field)
	WithOptionalReservedByte(byte) CALReplyLongBuilder
	// WithReplyNetwork adds ReplyNetwork (property field)
	WithOptionalReplyNetwork(ReplyNetwork) CALReplyLongBuilder
	// WithOptionalReplyNetworkBuilder adds ReplyNetwork (property field) which is build by the builder
	WithOptionalReplyNetworkBuilder(func(ReplyNetworkBuilder) ReplyNetworkBuilder) CALReplyLongBuilder
	// Done is used to finish work on this child and return (or create one if none) to the parent builder
	Done() CALReplyBuilder
	// Build builds the CALReplyLong or returns an error if something is wrong
	Build() (CALReplyLong, error)
	// MustBuild does the same as Build but panics on error
	MustBuild() CALReplyLong
}

// NewCALReplyLongBuilder() creates a CALReplyLongBuilder
func NewCALReplyLongBuilder() CALReplyLongBuilder {
	return &_CALReplyLongBuilder{_CALReplyLong: new(_CALReplyLong)}
}

type _CALReplyLongBuilder struct {
	*_CALReplyLong

	parentBuilder *_CALReplyBuilder

	collectedErr []error
}

var _ (CALReplyLongBuilder) = (*_CALReplyLongBuilder)(nil)

func (b *_CALReplyLongBuilder) setParent(contract CALReplyContract) {
	b.CALReplyContract = contract
	contract.(*_CALReply)._SubType = b._CALReplyLong
}

func (b *_CALReplyLongBuilder) WithMandatoryFields(terminatingByte uint32, serialInterfaceAddress SerialInterfaceAddress) CALReplyLongBuilder {
	return b.WithTerminatingByte(terminatingByte).WithSerialInterfaceAddress(serialInterfaceAddress)
}

func (b *_CALReplyLongBuilder) WithTerminatingByte(terminatingByte uint32) CALReplyLongBuilder {
	b.TerminatingByte = terminatingByte
	return b
}

func (b *_CALReplyLongBuilder) WithOptionalUnitAddress(unitAddress UnitAddress) CALReplyLongBuilder {
	b.UnitAddress = unitAddress
	return b
}

func (b *_CALReplyLongBuilder) WithOptionalUnitAddressBuilder(builderSupplier func(UnitAddressBuilder) UnitAddressBuilder) CALReplyLongBuilder {
	builder := builderSupplier(b.UnitAddress.CreateUnitAddressBuilder())
	var err error
	b.UnitAddress, err = builder.Build()
	if err != nil {
		b.collectedErr = append(b.collectedErr, errors.Wrap(err, "UnitAddressBuilder failed"))
	}
	return b
}

func (b *_CALReplyLongBuilder) WithOptionalBridgeAddress(bridgeAddress BridgeAddress) CALReplyLongBuilder {
	b.BridgeAddress = bridgeAddress
	return b
}

func (b *_CALReplyLongBuilder) WithOptionalBridgeAddressBuilder(builderSupplier func(BridgeAddressBuilder) BridgeAddressBuilder) CALReplyLongBuilder {
	builder := builderSupplier(b.BridgeAddress.CreateBridgeAddressBuilder())
	var err error
	b.BridgeAddress, err = builder.Build()
	if err != nil {
		b.collectedErr = append(b.collectedErr, errors.Wrap(err, "BridgeAddressBuilder failed"))
	}
	return b
}

func (b *_CALReplyLongBuilder) WithSerialInterfaceAddress(serialInterfaceAddress SerialInterfaceAddress) CALReplyLongBuilder {
	b.SerialInterfaceAddress = serialInterfaceAddress
	return b
}

func (b *_CALReplyLongBuilder) WithSerialInterfaceAddressBuilder(builderSupplier func(SerialInterfaceAddressBuilder) SerialInterfaceAddressBuilder) CALReplyLongBuilder {
	builder := builderSupplier(b.SerialInterfaceAddress.CreateSerialInterfaceAddressBuilder())
	var err error
	b.SerialInterfaceAddress, err = builder.Build()
	if err != nil {
		b.collectedErr = append(b.collectedErr, errors.Wrap(err, "SerialInterfaceAddressBuilder failed"))
	}
	return b
}

func (b *_CALReplyLongBuilder) WithOptionalReservedByte(reservedByte byte) CALReplyLongBuilder {
	b.ReservedByte = &reservedByte
	return b
}

func (b *_CALReplyLongBuilder) WithOptionalReplyNetwork(replyNetwork ReplyNetwork) CALReplyLongBuilder {
	b.ReplyNetwork = replyNetwork
	return b
}

func (b *_CALReplyLongBuilder) WithOptionalReplyNetworkBuilder(builderSupplier func(ReplyNetworkBuilder) ReplyNetworkBuilder) CALReplyLongBuilder {
	builder := builderSupplier(b.ReplyNetwork.CreateReplyNetworkBuilder())
	var err error
	b.ReplyNetwork, err = builder.Build()
	if err != nil {
		b.collectedErr = append(b.collectedErr, errors.Wrap(err, "ReplyNetworkBuilder failed"))
	}
	return b
}

func (b *_CALReplyLongBuilder) Build() (CALReplyLong, error) {
	if b.SerialInterfaceAddress == nil {
		b.collectedErr = append(b.collectedErr, errors.New("mandatory field 'serialInterfaceAddress' not set"))
	}
	if err := stdErrors.Join(b.collectedErr...); err != nil {
		return nil, errors.Wrap(err, "error occurred during build")
	}
	return b._CALReplyLong.deepCopy(), nil
}

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

func (b *_CALReplyLongBuilder) Done() CALReplyBuilder {
	if b.parentBuilder == nil {
		b.parentBuilder = NewCALReplyBuilder().(*_CALReplyBuilder)
	}
	return b.parentBuilder
}

func (b *_CALReplyLongBuilder) buildForCALReply() (CALReply, error) {
	return b.Build()
}

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

// CreateCALReplyLongBuilder creates a CALReplyLongBuilder
func (b *_CALReplyLong) CreateCALReplyLongBuilder() CALReplyLongBuilder {
	if b == nil {
		return NewCALReplyLongBuilder()
	}
	return &_CALReplyLongBuilder{_CALReplyLong: b.deepCopy()}
}

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

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
/////////////////////// Accessors for discriminator values.
///////////////////////

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

func (m *_CALReplyLong) GetParent() CALReplyContract {
	return m.CALReplyContract
}

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

func (m *_CALReplyLong) GetTerminatingByte() uint32 {
	return m.TerminatingByte
}

func (m *_CALReplyLong) GetUnitAddress() UnitAddress {
	return m.UnitAddress
}

func (m *_CALReplyLong) GetBridgeAddress() BridgeAddress {
	return m.BridgeAddress
}

func (m *_CALReplyLong) GetSerialInterfaceAddress() SerialInterfaceAddress {
	return m.SerialInterfaceAddress
}

func (m *_CALReplyLong) GetReservedByte() *byte {
	return m.ReservedByte
}

func (m *_CALReplyLong) GetReplyNetwork() ReplyNetwork {
	return m.ReplyNetwork
}

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

func (m *_CALReplyLong) GetIsUnitAddress() bool {
	ctx := context.Background()
	_ = ctx
	unitAddress := m.GetUnitAddress()
	_ = unitAddress
	bridgeAddress := m.GetBridgeAddress()
	_ = bridgeAddress
	reservedByte := m.GetReservedByte()
	_ = reservedByte
	replyNetwork := m.GetReplyNetwork()
	_ = replyNetwork
	return bool(bool((m.GetTerminatingByte() & 0xff) == (0x00)))
}

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

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

func (m *_CALReplyLong) GetTypeName() string {
	return "CALReplyLong"
}

func (m *_CALReplyLong) GetLengthInBits(ctx context.Context) uint16 {
	lengthInBits := uint16(m.CALReplyContract.(*_CALReply).getLengthInBits(ctx))

	// Reserved Field (reserved)
	lengthInBits += 8

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

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

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

	// Simple field (serialInterfaceAddress)
	lengthInBits += m.SerialInterfaceAddress.GetLengthInBits(ctx)

	// Optional Field (reservedByte)
	if m.ReservedByte != nil {
		lengthInBits += 8
	}

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

	return lengthInBits
}

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

func (m *_CALReplyLong) parse(ctx context.Context, readBuffer utils.ReadBuffer, parent *_CALReply, cBusOptions CBusOptions, requestContext RequestContext) (__cALReplyLong CALReplyLong, err error) {
	m.CALReplyContract = parent
	parent._SubType = m
	positionAware := readBuffer
	_ = positionAware
	if pullErr := readBuffer.PullContext("CALReplyLong"); pullErr != nil {
		return nil, errors.Wrap(pullErr, "Error pulling for CALReplyLong")
	}
	currentPos := positionAware.GetPos()
	_ = currentPos

	reservedField0, err := ReadReservedField(ctx, "reserved", ReadByte(readBuffer, 8), byte(0x86))
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing reserved field"))
	}
	m.reservedField0 = reservedField0

	terminatingByte, err := ReadPeekField[uint32](ctx, "terminatingByte", ReadUnsignedInt(readBuffer, uint8(24)), 0)
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'terminatingByte' field"))
	}
	m.TerminatingByte = terminatingByte

	isUnitAddress, err := ReadVirtualField[bool](ctx, "isUnitAddress", (*bool)(nil), bool((terminatingByte&0xff) == (0x00)))
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'isUnitAddress' field"))
	}
	_ = isUnitAddress

	var unitAddress UnitAddress
	_unitAddress, err := ReadOptionalField[UnitAddress](ctx, "unitAddress", ReadComplex[UnitAddress](UnitAddressParseWithBuffer, readBuffer), isUnitAddress)
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'unitAddress' field"))
	}
	if _unitAddress != nil {
		unitAddress = *_unitAddress
		m.UnitAddress = unitAddress
	}

	var bridgeAddress BridgeAddress
	_bridgeAddress, err := ReadOptionalField[BridgeAddress](ctx, "bridgeAddress", ReadComplex[BridgeAddress](BridgeAddressParseWithBuffer, readBuffer), !(isUnitAddress))
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'bridgeAddress' field"))
	}
	if _bridgeAddress != nil {
		bridgeAddress = *_bridgeAddress
		m.BridgeAddress = bridgeAddress
	}

	serialInterfaceAddress, err := ReadSimpleField[SerialInterfaceAddress](ctx, "serialInterfaceAddress", ReadComplex[SerialInterfaceAddress](SerialInterfaceAddressParseWithBuffer, readBuffer))
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'serialInterfaceAddress' field"))
	}
	m.SerialInterfaceAddress = serialInterfaceAddress

	var reservedByte *byte
	reservedByte, err = ReadOptionalField[byte](ctx, "reservedByte", ReadByte(readBuffer, 8), isUnitAddress)
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'reservedByte' field"))
	}
	m.ReservedByte = reservedByte

	// Validation
	if !(bool(bool(isUnitAddress) && bool(bool((*reservedByte) == (0x00)))) || bool(!(isUnitAddress))) {
		return nil, errors.WithStack(utils.ParseValidationError{Message: "wrong reservedByte"})
	}

	var replyNetwork ReplyNetwork
	_replyNetwork, err := ReadOptionalField[ReplyNetwork](ctx, "replyNetwork", ReadComplex[ReplyNetwork](ReplyNetworkParseWithBuffer, readBuffer), !(isUnitAddress))
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'replyNetwork' field"))
	}
	if _replyNetwork != nil {
		replyNetwork = *_replyNetwork
		m.ReplyNetwork = replyNetwork
	}

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

	return m, nil
}

func (m *_CALReplyLong) 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 *_CALReplyLong) SerializeWithWriteBuffer(ctx context.Context, writeBuffer utils.WriteBuffer) error {
	positionAware := writeBuffer
	_ = positionAware
	log := zerolog.Ctx(ctx)
	_ = log
	ser := func() error {
		if pushErr := writeBuffer.PushContext("CALReplyLong"); pushErr != nil {
			return errors.Wrap(pushErr, "Error pushing for CALReplyLong")
		}

		if err := WriteReservedField[byte](ctx, "reserved", byte(0x86), WriteByte(writeBuffer, 8)); err != nil {
			return errors.Wrap(err, "Error serializing 'reserved' field number 1")
		}
		// Virtual field
		isUnitAddress := m.GetIsUnitAddress()
		_ = isUnitAddress
		if _isUnitAddressErr := writeBuffer.WriteVirtual(ctx, "isUnitAddress", m.GetIsUnitAddress()); _isUnitAddressErr != nil {
			return errors.Wrap(_isUnitAddressErr, "Error serializing 'isUnitAddress' field")
		}

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

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

		if err := WriteSimpleField[SerialInterfaceAddress](ctx, "serialInterfaceAddress", m.GetSerialInterfaceAddress(), WriteComplex[SerialInterfaceAddress](writeBuffer)); err != nil {
			return errors.Wrap(err, "Error serializing 'serialInterfaceAddress' field")
		}

		if err := WriteOptionalField[byte](ctx, "reservedByte", m.GetReservedByte(), WriteByte(writeBuffer, 8), true); err != nil {
			return errors.Wrap(err, "Error serializing 'reservedByte' field")
		}

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

		if popErr := writeBuffer.PopContext("CALReplyLong"); popErr != nil {
			return errors.Wrap(popErr, "Error popping for CALReplyLong")
		}
		return nil
	}
	return m.CALReplyContract.(*_CALReply).serializeParent(ctx, writeBuffer, m, ser)
}

func (m *_CALReplyLong) IsCALReplyLong() {}

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

func (m *_CALReplyLong) deepCopy() *_CALReplyLong {
	if m == nil {
		return nil
	}
	_CALReplyLongCopy := &_CALReplyLong{
		m.CALReplyContract.(*_CALReply).deepCopy(),
		m.TerminatingByte,
		utils.DeepCopy[UnitAddress](m.UnitAddress),
		utils.DeepCopy[BridgeAddress](m.BridgeAddress),
		utils.DeepCopy[SerialInterfaceAddress](m.SerialInterfaceAddress),
		utils.CopyPtr[byte](m.ReservedByte),
		utils.DeepCopy[ReplyNetwork](m.ReplyNetwork),
		m.reservedField0,
	}
	_CALReplyLongCopy.CALReplyContract.(*_CALReply)._SubType = m
	return _CALReplyLongCopy
}

func (m *_CALReplyLong) 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()
}
