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

// LevelInformationNormal is the corresponding interface of LevelInformationNormal
type LevelInformationNormal interface {
	fmt.Stringer
	utils.LengthAware
	utils.Serializable
	utils.Copyable
	LevelInformation
	// GetPair1 returns Pair1 (property field)
	GetPair1() LevelInformationNibblePair
	// GetPair2 returns Pair2 (property field)
	GetPair2() LevelInformationNibblePair
	// GetActualLevel returns ActualLevel (virtual field)
	GetActualLevel() uint8
	// GetActualLevelInPercent returns ActualLevelInPercent (virtual field)
	GetActualLevelInPercent() float32
	// IsLevelInformationNormal is a marker method to prevent unintentional type checks (interfaces of same signature)
	IsLevelInformationNormal()
	// CreateBuilder creates a LevelInformationNormalBuilder
	CreateLevelInformationNormalBuilder() LevelInformationNormalBuilder
}

// _LevelInformationNormal is the data-structure of this message
type _LevelInformationNormal struct {
	LevelInformationContract
	Pair1 LevelInformationNibblePair
	Pair2 LevelInformationNibblePair
}

var _ LevelInformationNormal = (*_LevelInformationNormal)(nil)
var _ LevelInformationRequirements = (*_LevelInformationNormal)(nil)

// NewLevelInformationNormal factory function for _LevelInformationNormal
func NewLevelInformationNormal(raw uint16, pair1 LevelInformationNibblePair, pair2 LevelInformationNibblePair) *_LevelInformationNormal {
	_result := &_LevelInformationNormal{
		LevelInformationContract: NewLevelInformation(raw),
		Pair1:                    pair1,
		Pair2:                    pair2,
	}
	_result.LevelInformationContract.(*_LevelInformation)._SubType = _result
	return _result
}

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

// LevelInformationNormalBuilder is a builder for LevelInformationNormal
type LevelInformationNormalBuilder interface {
	utils.Copyable
	// WithMandatoryFields adds all mandatory fields (convenience for using multiple builder calls)
	WithMandatoryFields(pair1 LevelInformationNibblePair, pair2 LevelInformationNibblePair) LevelInformationNormalBuilder
	// WithPair1 adds Pair1 (property field)
	WithPair1(LevelInformationNibblePair) LevelInformationNormalBuilder
	// WithPair2 adds Pair2 (property field)
	WithPair2(LevelInformationNibblePair) LevelInformationNormalBuilder
	// Done is used to finish work on this child and return (or create one if none) to the parent builder
	Done() LevelInformationBuilder
	// Build builds the LevelInformationNormal or returns an error if something is wrong
	Build() (LevelInformationNormal, error)
	// MustBuild does the same as Build but panics on error
	MustBuild() LevelInformationNormal
}

// NewLevelInformationNormalBuilder() creates a LevelInformationNormalBuilder
func NewLevelInformationNormalBuilder() LevelInformationNormalBuilder {
	return &_LevelInformationNormalBuilder{_LevelInformationNormal: new(_LevelInformationNormal)}
}

type _LevelInformationNormalBuilder struct {
	*_LevelInformationNormal

	parentBuilder *_LevelInformationBuilder

	collectedErr []error
}

var _ (LevelInformationNormalBuilder) = (*_LevelInformationNormalBuilder)(nil)

func (b *_LevelInformationNormalBuilder) setParent(contract LevelInformationContract) {
	b.LevelInformationContract = contract
	contract.(*_LevelInformation)._SubType = b._LevelInformationNormal
}

func (b *_LevelInformationNormalBuilder) WithMandatoryFields(pair1 LevelInformationNibblePair, pair2 LevelInformationNibblePair) LevelInformationNormalBuilder {
	return b.WithPair1(pair1).WithPair2(pair2)
}

func (b *_LevelInformationNormalBuilder) WithPair1(pair1 LevelInformationNibblePair) LevelInformationNormalBuilder {
	b.Pair1 = pair1
	return b
}

func (b *_LevelInformationNormalBuilder) WithPair2(pair2 LevelInformationNibblePair) LevelInformationNormalBuilder {
	b.Pair2 = pair2
	return b
}

func (b *_LevelInformationNormalBuilder) Build() (LevelInformationNormal, error) {
	if err := stdErrors.Join(b.collectedErr...); err != nil {
		return nil, errors.Wrap(err, "error occurred during build")
	}
	return b._LevelInformationNormal.deepCopy(), nil
}

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

func (b *_LevelInformationNormalBuilder) Done() LevelInformationBuilder {
	if b.parentBuilder == nil {
		b.parentBuilder = NewLevelInformationBuilder().(*_LevelInformationBuilder)
	}
	return b.parentBuilder
}

func (b *_LevelInformationNormalBuilder) buildForLevelInformation() (LevelInformation, error) {
	return b.Build()
}

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

// CreateLevelInformationNormalBuilder creates a LevelInformationNormalBuilder
func (b *_LevelInformationNormal) CreateLevelInformationNormalBuilder() LevelInformationNormalBuilder {
	if b == nil {
		return NewLevelInformationNormalBuilder()
	}
	return &_LevelInformationNormalBuilder{_LevelInformationNormal: b.deepCopy()}
}

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

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

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

func (m *_LevelInformationNormal) GetParent() LevelInformationContract {
	return m.LevelInformationContract
}

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

func (m *_LevelInformationNormal) GetPair1() LevelInformationNibblePair {
	return m.Pair1
}

func (m *_LevelInformationNormal) GetPair2() LevelInformationNibblePair {
	return m.Pair2
}

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

func (m *_LevelInformationNormal) GetActualLevel() uint8 {
	ctx := context.Background()
	_ = ctx
	return uint8(m.GetPair2().NibbleValue()<<uint8(4) | m.GetPair1().NibbleValue())
}

func (m *_LevelInformationNormal) GetActualLevelInPercent() float32 {
	ctx := context.Background()
	_ = ctx
	return float32(float32(float32(float32(100))*float32((float32(m.GetActualLevel())+float32(float32(2))))) / float32(float32(255)))
}

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

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

func (m *_LevelInformationNormal) GetTypeName() string {
	return "LevelInformationNormal"
}

func (m *_LevelInformationNormal) GetLengthInBits(ctx context.Context) uint16 {
	lengthInBits := uint16(m.LevelInformationContract.(*_LevelInformation).getLengthInBits(ctx))

	// Simple field (pair1)
	lengthInBits += 8

	// Simple field (pair2)
	lengthInBits += 8

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

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

	return lengthInBits
}

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

func (m *_LevelInformationNormal) parse(ctx context.Context, readBuffer utils.ReadBuffer, parent *_LevelInformation) (__levelInformationNormal LevelInformationNormal, err error) {
	m.LevelInformationContract = parent
	parent._SubType = m
	positionAware := readBuffer
	_ = positionAware
	if pullErr := readBuffer.PullContext("LevelInformationNormal"); pullErr != nil {
		return nil, errors.Wrap(pullErr, "Error pulling for LevelInformationNormal")
	}
	currentPos := positionAware.GetPos()
	_ = currentPos

	pair1, err := ReadEnumField[LevelInformationNibblePair](ctx, "pair1", "LevelInformationNibblePair", ReadEnum(LevelInformationNibblePairByValue, ReadUnsignedByte(readBuffer, uint8(8))))
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'pair1' field"))
	}
	m.Pair1 = pair1

	pair2, err := ReadEnumField[LevelInformationNibblePair](ctx, "pair2", "LevelInformationNibblePair", ReadEnum(LevelInformationNibblePairByValue, ReadUnsignedByte(readBuffer, uint8(8))))
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'pair2' field"))
	}
	m.Pair2 = pair2

	actualLevel, err := ReadVirtualField[uint8](ctx, "actualLevel", (*uint8)(nil), pair2.NibbleValue()<<uint8(4)|pair1.NibbleValue())
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'actualLevel' field"))
	}
	_ = actualLevel

	actualLevelInPercent, err := ReadVirtualField[float32](ctx, "actualLevelInPercent", (*float32)(nil), float32(float32(float32(100))*float32((float32(actualLevel)+float32(float32(2)))))/float32(float32(255)))
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Error parsing 'actualLevelInPercent' field"))
	}
	_ = actualLevelInPercent

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

	return m, nil
}

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

		if err := WriteSimpleEnumField[LevelInformationNibblePair](ctx, "pair1", "LevelInformationNibblePair", m.GetPair1(), WriteEnum[LevelInformationNibblePair, uint8](LevelInformationNibblePair.GetValue, LevelInformationNibblePair.PLC4XEnumName, WriteUnsignedByte(writeBuffer, 8))); err != nil {
			return errors.Wrap(err, "Error serializing 'pair1' field")
		}

		if err := WriteSimpleEnumField[LevelInformationNibblePair](ctx, "pair2", "LevelInformationNibblePair", m.GetPair2(), WriteEnum[LevelInformationNibblePair, uint8](LevelInformationNibblePair.GetValue, LevelInformationNibblePair.PLC4XEnumName, WriteUnsignedByte(writeBuffer, 8))); err != nil {
			return errors.Wrap(err, "Error serializing 'pair2' field")
		}
		// Virtual field
		actualLevel := m.GetActualLevel()
		_ = actualLevel
		if _actualLevelErr := writeBuffer.WriteVirtual(ctx, "actualLevel", m.GetActualLevel()); _actualLevelErr != nil {
			return errors.Wrap(_actualLevelErr, "Error serializing 'actualLevel' field")
		}
		// Virtual field
		actualLevelInPercent := m.GetActualLevelInPercent()
		_ = actualLevelInPercent
		if _actualLevelInPercentErr := writeBuffer.WriteVirtual(ctx, "actualLevelInPercent", m.GetActualLevelInPercent()); _actualLevelInPercentErr != nil {
			return errors.Wrap(_actualLevelInPercentErr, "Error serializing 'actualLevelInPercent' field")
		}

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

func (m *_LevelInformationNormal) IsLevelInformationNormal() {}

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

func (m *_LevelInformationNormal) deepCopy() *_LevelInformationNormal {
	if m == nil {
		return nil
	}
	_LevelInformationNormalCopy := &_LevelInformationNormal{
		m.LevelInformationContract.(*_LevelInformation).deepCopy(),
		m.Pair1,
		m.Pair2,
	}
	_LevelInformationNormalCopy.LevelInformationContract.(*_LevelInformation)._SubType = m
	return _LevelInformationNormalCopy
}

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