package goja

import (
	
	
	
	
	

	
)

type arrayIterObject struct {
	baseObject
	obj     *Object
	nextIdx int64
	kind    iterationKind
}

func ( *arrayIterObject) () Value {
	if .obj == nil {
		return .val.runtime.createIterResultObject(_undefined, true)
	}
	if ,  := .obj.self.(*typedArrayObject);  {
		.viewedArrayBuf.ensureNotDetached(true)
	}
	 := toLength(.obj.self.getStr("length", nil))
	 := .nextIdx
	if  >=  {
		.obj = nil
		return .val.runtime.createIterResultObject(_undefined, true)
	}
	.nextIdx++
	 := valueInt()
	if .kind == iterationKindKey {
		return .val.runtime.createIterResultObject(, false)
	}
	 := nilSafe(.obj.self.getIdx(, nil))
	var  Value
	if .kind == iterationKindValue {
		 = 
	} else {
		 = .val.runtime.newArrayValues([]Value{, })
	}
	return .val.runtime.createIterResultObject(, false)
}

func ( *Runtime) ( *Object,  iterationKind) Value {
	 := &Object{runtime: }

	 := &arrayIterObject{
		obj:  ,
		kind: ,
	}
	.class = classObject
	.val = 
	.extensible = true
	.self = 
	.prototype = .getArrayIteratorPrototype()
	.init()

	return 
}

type arrayObject struct {
	baseObject
	values         []Value
	length         uint32
	objCount       int
	propValueCount int
	lengthProp     valueProperty
}

func ( *arrayObject) () {
	.baseObject.init()
	.lengthProp.writable = true

	._put("length", &.lengthProp)
}

func ( *arrayObject) ( uint32,  bool) bool {
	 := true
	if  <= .length {
		if .propValueCount > 0 {
			// Slow path
			for  := len(.values) - 1;  >= int(); -- {
				if ,  := .values[].(*valueProperty);  {
					if !.configurable {
						 = uint32() + 1
						 = false
						break
					}
					.propValueCount--
				}
			}
		}
	}
	if  <= uint32(len(.values)) {
		if  >= 16 &&  < uint32(cap(.values))>>2 {
			 := make([]Value, )
			copy(, .values)
			.values = 
		} else {
			 := .values[:len(.values)]
			for  := range  {
				[] = nil
			}
			.values = .values[:]
		}
	}
	.length = 
	if ! {
		.val.runtime.typeErrorResult(, "Cannot redefine property: length")
	}
	return 
}

func ( *arrayObject) ( uint32,  bool) bool {
	if  == .length {
		return true
	}
	if !.lengthProp.writable {
		.val.runtime.typeErrorResult(, "length is not writable")
		return false
	}
	return ._setLengthInt(, )
}

func ( *arrayObject) ( uint32,  bool) bool {
	if !.lengthProp.writable {
		.val.runtime.typeErrorResult(, "length is not writable")
		return false
	}
	return ._setLengthInt(, )
}

func ( *arrayObject) ( valueInt,  Value) Value {
	 := .getOwnPropIdx()
	if  == nil {
		if .prototype != nil {
			if  == nil {
				return .prototype.self.getIdx(, .val)
			}
			return .prototype.self.getIdx(, )
		}
	}
	if ,  := .(*valueProperty);  {
		if  == nil {
			return .get(.val)
		}
		return .get()
	}
	return 
}

func ( *arrayObject) ( unistring.String) Value {
	if len(.values) > 0 {
		if  := strToArrayIdx();  != math.MaxUint32 {
			if  < uint32(len(.values)) {
				return .values[]
			}
		}
	}
	if  == "length" {
		return .getLengthProp()
	}
	return .baseObject.getOwnPropStr()
}

func ( *arrayObject) ( valueInt) Value {
	if  := toIdx();  != math.MaxUint32 {
		if  < uint32(len(.values)) {
			return .values[]
		}
		return nil
	}

	return .baseObject.getOwnPropStr(.string())
}

func ( *arrayObject) () int {
	return len(.values)
}

func ( *arrayObject) ( int) Value {
	 := .values[]
	if ,  := .(*valueProperty);  {
		 = .get(.val)
	}
	return 
}

func ( *arrayObject) ( int,  int) {
	.values[], .values[] = .values[], .values[]
}

func ( *arrayObject) ( unistring.String,  Value) Value {
	return .getStrWithOwnProp(.getOwnPropStr(), , )
}

func ( *arrayObject) () *valueProperty {
	.lengthProp.value = intToValue(int64(.length))
	return &.lengthProp
}

func ( *arrayObject) ( valueInt,  Value,  bool) bool {
	if  := toIdx();  != math.MaxUint32 {
		return ._setOwnIdx(, , )
	} else {
		return .baseObject.setOwnStr(.string(), , )
	}
}

func ( *arrayObject) ( uint32,  Value,  bool) bool {
	var  Value
	if  < uint32(len(.values)) {
		 = .values[]
	}

	if  == nil {
		if  := .prototype;  != nil {
			// we know it's foreign because prototype loops are not allowed
			if ,  := .self.setForeignIdx(valueInt(), , .val, );  {
				return 
			}
		}
		// new property
		if !.extensible {
			.val.runtime.typeErrorResult(, "Cannot add property %d, object is not extensible", )
			return false
		} else {
			if  >= .length {
				if !.setLengthInt(+1, ) {
					return false
				}
			}
			if  >= uint32(len(.values)) {
				if !.expand() {
					.val.self.(*sparseArrayObject).add(, )
					return true
				}
			}
			.objCount++
		}
	} else {
		if ,  := .(*valueProperty);  {
			if !.isWritable() {
				.val.runtime.typeErrorResult()
				return false
			}
			.set(.val, )
			return true
		}
	}
	.values[] = 
	return true
}

func ( *arrayObject) ( unistring.String,  Value,  bool) bool {
	if  := strToArrayIdx();  != math.MaxUint32 {
		return ._setOwnIdx(, , )
	} else {
		if  == "length" {
			return .setLength(.val.runtime.toLengthUint32(), )
		} else {
			return .baseObject.setOwnStr(, , )
		}
	}
}

func ( *arrayObject) ( valueInt, ,  Value,  bool) (bool, bool) {
	return ._setForeignIdx(, .getOwnPropIdx(), , , )
}

func ( *arrayObject) ( unistring.String, ,  Value,  bool) (bool, bool) {
	return ._setForeignStr(, .getOwnPropStr(), , , )
}

type arrayPropIter struct {
	a     *arrayObject
	limit int
	idx   int
}

func ( *arrayPropIter) () (propIterItem, iterNextFunc) {
	for .idx < len(.a.values) && .idx < .limit {
		 := asciiString(strconv.Itoa(.idx))
		 := .a.values[.idx]
		.idx++
		if  != nil {
			return propIterItem{name: , value: }, .
		}
	}

	return .a.baseObject.iterateStringKeys()()
}

func ( *arrayObject) () iterNextFunc {
	return (&arrayPropIter{
		a:     ,
		limit: len(.values),
	}).next
}

func ( *arrayObject) ( bool,  []Value) []Value {
	for ,  := range .values {
		 := strconv.Itoa()
		if  != nil {
			if ! {
				if ,  := .(*valueProperty);  && !.enumerable {
					continue
				}
			}
			 = append(, asciiString())
		}
	}
	return .baseObject.stringKeys(, )
}

func ( *arrayObject) ( unistring.String) bool {
	if  := strToArrayIdx();  != math.MaxUint32 {
		return  < uint32(len(.values)) && .values[] != nil
	} else {
		return .baseObject.hasOwnPropertyStr()
	}
}

func ( *arrayObject) ( valueInt) bool {
	if  := toIdx();  != math.MaxUint32 {
		return  < uint32(len(.values)) && .values[] != nil
	}
	return .baseObject.hasOwnPropertyStr(.string())
}

func ( *arrayObject) ( valueInt) bool {
	if .hasOwnPropertyIdx() {
		return true
	}

	if .prototype != nil {
		return .prototype.self.hasPropertyIdx()
	}

	return false
}

func ( *arrayObject) ( uint32) bool {
	 :=  + 1
	if  > uint32(len(.values)) {
		if  < uint32(cap(.values)) {
			.values = .values[:]
		} else {
			if  > 4096 && (.objCount == 0 || /uint32(.objCount) > 10) {
				//log.Println("Switching standard->sparse")
				 := &sparseArrayObject{
					baseObject:     .baseObject,
					length:         .length,
					propValueCount: .propValueCount,
				}
				.setValues(.values, .objCount+1)
				.val.self = 
				.lengthProp.writable = .lengthProp.writable
				._put("length", &.lengthProp)
				return false
			} else {
				if bits.UintSize == 32 {
					if  >= math.MaxInt32 {
						panic(.val.runtime.NewTypeError("Array index overflows int"))
					}
				}
				 := int()
				 := make([]Value, , growCap(, len(.values), cap(.values)))
				copy(, .values)
				.values = 
			}
		}
	}
	return true
}

func ( *Runtime) ( *valueProperty,  PropertyDescriptor,  func(uint32, bool) bool,  bool) bool {
	var  uint32
	 := true
	if .Value != nil {
		 = .toLengthUint32(.Value)
	}

	if .Configurable == FLAG_TRUE || .Enumerable == FLAG_TRUE || .Getter != nil || .Setter != nil {
		 = false
		goto 
	}

	if .Value != nil {
		 := uint32(.value.ToInteger())
		if  !=  {
			 = (, false)
		}
	} else {
		 = true
	}

	if .Writable != FLAG_NOT_SET {
		 := .Writable.Bool()
		if .writable {
			.writable = 
		} else {
			if  {
				 = false
				goto 
			}
		}
	}

:
	if ! {
		.typeErrorResult(, "Cannot redefine property: length")
	}

	return 
}

func ( *arrayObject) ( uint32,  PropertyDescriptor,  bool) bool {
	var  Value
	if  < uint32(len(.values)) {
		 = .values[]
	}
	,  := .baseObject._defineOwnProperty(unistring.String(strconv.FormatUint(uint64(), 10)), , , )
	if  {
		if  >= .length {
			if !.setLengthInt(+1, ) {
				return false
			}
		}
		if .expand() {
			.values[] = 
			.objCount++
			if ,  := .(*valueProperty);  {
				.propValueCount++
			}
		} else {
			.val.self.(*sparseArrayObject).add(, )
		}
	}
	return 
}

func ( *arrayObject) ( unistring.String,  PropertyDescriptor,  bool) bool {
	if  := strToArrayIdx();  != math.MaxUint32 {
		return ._defineIdxProperty(, , )
	}
	if  == "length" {
		return .val.runtime.defineArrayLength(.getLengthProp(), , .setLength, )
	}
	return .baseObject.defineOwnPropertyStr(, , )
}

func ( *arrayObject) ( valueInt,  PropertyDescriptor,  bool) bool {
	if  := toIdx();  != math.MaxUint32 {
		return ._defineIdxProperty(, , )
	}
	return .baseObject.defineOwnPropertyStr(.string(), , )
}

func ( *arrayObject) ( uint32,  bool) bool {
	if  < uint32(len(.values)) {
		if  := .values[];  != nil {
			if ,  := .(*valueProperty);  {
				if !.configurable {
					.val.runtime.typeErrorResult(, "Cannot delete property '%d' of %s", , .val.toString())
					return false
				}
				.propValueCount--
			}
			.values[] = nil
			.objCount--
		}
	}
	return true
}

func ( *arrayObject) ( unistring.String,  bool) bool {
	if  := strToArrayIdx();  != math.MaxUint32 {
		return ._deleteIdxProp(, )
	}
	return .baseObject.deleteStr(, )
}

func ( *arrayObject) ( valueInt,  bool) bool {
	if  := toIdx();  != math.MaxUint32 {
		return ._deleteIdxProp(, )
	}
	return .baseObject.deleteStr(.string(), )
}

func ( *arrayObject) ( *objectExportCtx) interface{} {
	if ,  := .get(.val);  {
		return 
	}
	 := make([]interface{}, .length)
	.put(.val, )
	if .propValueCount == 0 && .length == uint32(len(.values)) && uint32(.objCount) == .length {
		for ,  := range .values {
			if  != nil {
				[] = exportValue(, )
			}
		}
	} else {
		for  := uint32(0);  < .length; ++ {
			 := .getIdx(valueInt(), nil)
			if  != nil {
				[] = exportValue(, )
			}
		}
	}
	return 
}

func ( *arrayObject) () reflect.Type {
	return reflectTypeArray
}

func ( *arrayObject) ( reflect.Value,  reflect.Type,  *objectExportCtx) error {
	 := .val.runtime
	if  := .getSym(SymIterator, nil);  == .getArrayValues() ||  == nil {
		 := toIntStrict(int64(.length))
		if .Kind() == reflect.Array {
			if .Len() !=  {
				return fmt.Errorf("cannot convert an Array into an array, lengths mismatch (have %d, need %d)", , .Len())
			}
		} else {
			.Set(reflect.MakeSlice(, , ))
		}
		.putTyped(.val, , .Interface())
		for  := 0;  < ; ++ {
			if  >= len(.values) {
				break
			}
			 := .values[]
			if ,  := .(*valueProperty);  {
				 = .get(.val)
			}
			 := .toReflectValue(, .Index(), )
			if  != nil {
				return fmt.Errorf("could not convert array element %v to %v at %d: %w", , , , )
			}
		}
		return nil
	}
	return .baseObject.exportToArrayOrSlice(, , )
}

func ( *arrayObject) ( []sparseArrayItem,  int) {
	.values = make([]Value, +1)
	for ,  := range  {
		.values[.idx] = .value
	}
	.objCount = len()
}

func toIdx( valueInt) uint32 {
	if  >= 0 &&  < math.MaxUint32 {
		return uint32()
	}
	return math.MaxUint32
}