package object

import (
	"errors"

	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs"
	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
)

// ECHeader represents erasure coding header.
type ECHeader struct {
	parent              oid.ID
	parentSplitID       *SplitID
	parentSplitParentID *oid.ID
	parentAttributes    []Attribute
	index               uint32
	total               uint32
	header              []byte
	headerLength        uint32
}

type ECParentInfo struct {
	// EC-parent's ID.
	ID oid.ID

	// EC-parent's split ID if the parent is a part of Split itself.
	SplitID *SplitID

	// EC-parent's parent split ID if the parent is a part of Split itself.
	SplitParentID *oid.ID

	// EC-parent's attributes.
	Attributes []Attribute
}

// NewECHeader constructs new erasure coding header.
func NewECHeader(ecParentInfo ECParentInfo, index, total uint32, header []byte, headerLength uint32) *ECHeader {
	return &ECHeader{
		parent:              ecParentInfo.ID,
		parentSplitID:       ecParentInfo.SplitID,
		parentSplitParentID: ecParentInfo.SplitParentID,
		parentAttributes:    ecParentInfo.Attributes,
		index:               index,
		total:               total,
		header:              header,
		headerLength:        headerLength,
	}
}

// WriteToV2 converts SDK structure to v2-api one.
func (e *ECHeader) WriteToV2(h *object.ECHeader) {
	var parent refs.ObjectID
	e.parent.WriteToV2(&parent)
	h.ParentSplitID = e.parentSplitID.ToV2()

	if e.parentSplitParentID != nil {
		parentSplitParentID := new(refs.ObjectID)
		e.parentSplitParentID.WriteToV2(parentSplitParentID)
		h.ParentSplitParentID = parentSplitParentID
	}

	h.Parent = &parent

	attrs := make([]object.Attribute, len(e.parentAttributes))
	for i := range e.parentAttributes {
		attrs[i] = *e.parentAttributes[i].ToV2()
	}
	h.ParentAttributes = attrs

	h.Index = e.index
	h.Total = e.total
	h.Header = e.header
	h.HeaderLength = e.headerLength
}

// ReadFromV2 converts v2-api structure to SDK one.
func (e *ECHeader) ReadFromV2(h *object.ECHeader) error {
	if h == nil {
		return nil
	}
	if h.Parent == nil {
		return errors.New("empty parent")
	}

	attrs := make([]Attribute, len(h.ParentAttributes))
	for i := range h.ParentAttributes {
		attrs[i] = *NewAttributeFromV2(&h.ParentAttributes[i])
	}
	e.parentAttributes = attrs

	_ = e.parent.ReadFromV2(*h.Parent)
	e.parentSplitID = NewSplitIDFromV2(h.ParentSplitID)
	if h.ParentSplitParentID != nil {
		if e.parentSplitParentID == nil {
			e.parentSplitParentID = new(oid.ID)
		}
		_ = e.parentSplitParentID.ReadFromV2(*h.ParentSplitParentID)
	}
	e.index = h.Index
	e.total = h.Total
	e.header = h.Header
	e.headerLength = h.HeaderLength
	return nil
}

func (o *Object) ECHeader() *ECHeader {
	ec := (*object.Object)(o).GetHeader().GetEC()
	if ec == nil {
		return nil
	}

	h := new(ECHeader)
	_ = h.ReadFromV2(ec)
	return h
}

func (o *Object) SetECHeader(ec *ECHeader) {
	o.setHeaderField(func(h *object.Header) {
		if ec == nil {
			h.SetEC(nil)
			return
		}

		v2 := new(object.ECHeader)
		ec.WriteToV2(v2)
		h.SetEC(v2)
	})
}

func (e *ECHeader) Parent() oid.ID {
	return e.parent
}

func (e *ECHeader) SetParent(id oid.ID) {
	e.parent = id
}

func (e *ECHeader) ParentSplitID() *SplitID {
	return e.parentSplitID
}

func (e *ECHeader) SetParentSplitID(parentSplitID *SplitID) {
	e.parentSplitID = parentSplitID
}

func (e *ECHeader) ParentSplitParentID() *oid.ID {
	return e.parentSplitParentID
}

func (e *ECHeader) SetParentSplitParentID(parentSplitParentID *oid.ID) {
	e.parentSplitParentID = parentSplitParentID
}

func (e *ECHeader) ParentAttributes() []Attribute {
	return e.parentAttributes
}

func (e *ECHeader) SetParentAttributes(attrs []Attribute) {
	e.parentAttributes = attrs
}

func (e *ECHeader) Index() uint32 {
	return e.index
}

func (e *ECHeader) SetIndex(i uint32) {
	e.index = i
}

func (e *ECHeader) Total() uint32 {
	return e.total
}

func (e *ECHeader) SetTotal(i uint32) {
	e.total = i
}

func (e *ECHeader) Header() []byte {
	return e.header
}

func (e *ECHeader) SetHeader(header []byte) {
	e.header = header
}

func (e *ECHeader) HeaderLength() uint32 {
	return e.headerLength
}

func (e *ECHeader) SetHeaderLength(l uint32) {
	e.headerLength = l
}