// Code generated by sysl DO NOT EDIT.
package petshopmodel

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/anz-bank/sysl/language/go/pkg/relgom/relgomlib"
	"github.com/arr-ai/frozen"
	"github.com/arr-ai/hash"
)

// employeePK is the Key for Employee.
type employeePK struct {
	employeeID int64
}

func (k employeePK) Hash(seed uintptr) uintptr {
	seed = hash.Interface(k.employeeID, seed)
	return seed
}

func (k employeePK) Equal(i interface{}) bool {
	if l, ok := i.(employeePK); ok {
		return (k == l)
	}
	return false
}

// employeeData is the internal representation of a tuple in the model.
type employeeData struct {
	employeePK
	name  *string
	dob   *time.Time
	error int64
}

// MarshalJSON implements json.Marshaler.
func (d *employeeData) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		EmployeeID int64                     `json:"employeeId,omitempty"`
		Name       *string                   `json:"name,omitempty"`
		Dob        *relgomlib.DateTimeString `json:"dob,omitempty"`
		Error      int64                     `json:"error,omitempty"`
	}{EmployeeID: d.employeeID, Name: d.name, Dob: relgomlib.NewDateTimeString(d.dob), Error: d.error})
}

// UnmarshalJSON implements json.Unmarshaler.
func (d *employeeData) UnmarshalJSON(data []byte) error {
	var u struct {
		EmployeeID int64                     `json:"employeeId,omitempty"`
		Name       *string                   `json:"name,omitempty"`
		Dob        *relgomlib.DateTimeString `json:"dob,omitempty"`
		Error      int64                     `json:"error,omitempty"`
	}
	if err := json.Unmarshal(data, &u); err != nil {
		return err
	}
	unstageDob, err := u.Dob.Unstage()
	if err != nil {
		return fmt.Errorf("error unstaging %s.%s: %v", "Employee", "dob", err)
	}
	*d = employeeData{employeePK: employeePK{employeeID: u.EmployeeID}, name: u.Name, dob: unstageDob, error: u.Error}
	return nil
}

// Employee is the public representation tuple in the model.
type Employee struct {
	data  *employeeData
	model PetShopModel
}

// EmployeeID gets the employeeId attribute from the Employee.
func (t Employee) EmployeeID() int64 {
	return t.data.employeeID
}

// Name gets the name attribute from the Employee.
func (t Employee) Name() *string {
	return t.data.name
}

// Dob gets the dob attribute from the Employee.
func (t Employee) Dob() *time.Time {
	return t.data.dob
}

// Error gets the error attribute from the Employee.
func (t Employee) Error() int64 {
	return t.data.error
}

// EmployeeBuilder builds an instance of Employee in the model.
type EmployeeBuilder struct {
	data  employeeData
	model PetShopModel
	mask  [1]uint64
	apply func(t *employeeData) (frozen.Map, error)
}

// WithName sets the name attribute of the EmployeeBuilder.
func (b *EmployeeBuilder) WithName(value string) *EmployeeBuilder {
	relgomlib.UpdateMaskForFieldButPanicIfAlreadySet(&b.mask[0], (uint64(1) << 1))
	b.data.name = &value
	return b
}

// WithDob sets the dob attribute of the EmployeeBuilder.
func (b *EmployeeBuilder) WithDob(value time.Time) *EmployeeBuilder {
	relgomlib.UpdateMaskForFieldButPanicIfAlreadySet(&b.mask[0], (uint64(1) << 2))
	b.data.dob = &value
	return b
}

// WithError sets the error attribute of the EmployeeBuilder.
func (b *EmployeeBuilder) WithError(value int64) *EmployeeBuilder {
	relgomlib.UpdateMaskForFieldButPanicIfAlreadySet(&b.mask[0], (uint64(1) << 3))
	b.data.error = value
	return b
}

var employeeStaticMetadata = &relgomlib.EntityTypeStaticMetadata{PKMask: []uint64{0x1}, RequiredMask: []uint64{0x8}}

// Apply applies the built Employee.
func (b *EmployeeBuilder) Apply() (PetShopModel, Employee, error) {
	relgomlib.PanicIfRequiredFieldsNotSet(b.mask[:], employeeStaticMetadata.RequiredMask, ",,,error")
	set, err := b.apply(&b.data)
	if err != nil {
		return PetShopModel{}, Employee{}, err
	}
	model := b.model.relations.With(employeeKey, employeeRelationData{set})
	return PetShopModel{model}, Employee{&b.data, b.model}, nil
}

// employeeRelationData represents a set of Employee.
type employeeRelationData struct {
	set frozen.Map
}

// Count returns the number of tuples in d.
func (d employeeRelationData) Count() int {
	return d.set.Count()
}

// MarshalJSON implements json.Marshaler.
func (d employeeRelationData) MarshalJSON() ([]byte, error) {
	a := make([]*employeeData, 0, d.set.Count())
	for i := d.set.Range(); i.Next(); {
		a = append(a, i.Value().(*employeeData))
	}
	return json.Marshal(a)
}

// UnmarshalJSON implements json.Unmarshaler.
func (d *employeeRelationData) UnmarshalJSON(data []byte) error {
	a := []*employeeData{}
	if err := json.Unmarshal(data, &a); err != nil {
		return err
	}
	set := frozen.NewMap()
	for _, e := range a {
		set = set.With(e.employeePK, e)
	}
	*d = employeeRelationData{set}
	return nil
}

// EmployeeRelation represents a set of Employee.
type EmployeeRelation struct {
	employeeRelationData
	model PetShopModel
}

// Insert creates a builder to insert a new Employee.
func (r EmployeeRelation) Insert() *EmployeeBuilder {
	model, id := r.model.newID()
	return &EmployeeBuilder{model: model, apply: func(t *employeeData) (frozen.Map, error) {
		t.employeeID = int64(id)
		set := r.model.GetEmployee().set.With(t.employeePK, t)
		return set, nil
	}}
}

// Update creates a builder to update t in the model.
func (r EmployeeRelation) Update(t Employee) *EmployeeBuilder {
	b := &EmployeeBuilder{data: *t.data, model: r.model, apply: func(t *employeeData) (frozen.Map, error) {
		set := r.model.GetEmployee().set.With(t.employeePK, t)
		return set, nil
	}}
	copy(b.mask[:], employeeStaticMetadata.PKMask)
	return b
}

// Delete deletes t from the model.
func (r EmployeeRelation) Delete(t Employee) (PetShopModel, error) {
	set := r.model.GetEmployee().set.Without(frozen.NewSet(t.data.employeePK))
	relations := r.model.relations.With(employeeKey, employeeRelationData{set: set})
	return PetShopModel{relations: relations}, nil
}

// Lookup searches Employee by primary key.
func (r EmployeeRelation) Lookup(employeeID int64) (Employee, bool) {
	if t, has := r.set.Get(employeePK{employeeID: employeeID}); has {
		return Employee{data: t.(*employeeData), model: r.model}, true
	}
	return Employee{}, false
}

// DeleteWhere deletes tuples matching `where` from r.
func (r EmployeeRelation) DeleteWhere(where func(t Employee) bool) (PetShopModel, error) {
	model := r.model
	for i := r.Iterator(); i.MoveNext(); {
		t := i.Current()
		if where(t) {
			var err error
			if model, err = model.GetEmployee().Delete(t); err != nil {
				return PetShopModel{}, err
			}
		}
	}
	return model, nil
}

// Iterator returns an iterator over Employee tuples in r.
func (r EmployeeRelation) Iterator() EmployeeIterator {
	return &employeeIterator{model: r.model, i: r.set.Range()}
}

// employeeIterator provides for iteration over a set of employeeIterator tuples.
type EmployeeIterator interface {
	MoveNext() bool
	Current() Employee
}

type employeeIterator struct {
	model PetShopModel
	i     *frozen.MapIterator
	t     *Employee
}

// MoveNext implements seq.Setable.
func (i *employeeIterator) MoveNext() bool {
	if i.i.Next() {
		i.t = &Employee{data: i.i.Value().(*employeeData), model: i.model}
		return true
	}
	return false
}

// Current implements seq.Setable.
func (i *employeeIterator) Current() Employee {
	if i.t == nil {
		panic("no current Employee")
	}
	return *i.t
}
