package goja

import (
	
	
	
	
	
	

	
)

type sparseArrayItem struct {
	idx   uint32
	value Value
}

type sparseArrayObject struct {
	baseObject
	items          []sparseArrayItem
	length         uint32
	propValueCount int
	lengthProp     valueProperty
}

func ( *sparseArrayObject) ( uint32) int {
	return sort.Search(len(.items), func( int) bool {
		return .items[].idx >= 
	})
}

func ( *sparseArrayObject) ( uint32,  bool) bool {
	 := true
	if  <= .length {
		if .propValueCount > 0 {
			// Slow path
			for  := len(.items) - 1;  >= 0; -- {
				 := .items[]
				if .idx <=  {
					break
				}
				if ,  := .value.(*valueProperty);  {
					if !.configurable {
						 = .idx + 1
						 = false
						break
					}
					.propValueCount--
				}
			}
		}
	}

	 := .findIdx()

	 := .items[:]
	for  := range  {
		[].value = nil
	}
	.items = .items[:]
	.length = 
	if ! {
		.val.runtime.typeErrorResult(, "Cannot redefine property: length")
	}
	return 
}

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

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

func ( *sparseArrayObject) ( uint32) Value {
	 := .findIdx()
	if  < len(.items) && .items[].idx ==  {
		return .items[].value
	}

	return nil
}

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

func ( *sparseArrayObject) ( 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 ( *sparseArrayObject) () *valueProperty {
	.lengthProp.value = intToValue(int64(.length))
	return &.lengthProp
}

func ( *sparseArrayObject) ( unistring.String) Value {
	if  := strToArrayIdx();  != math.MaxUint32 {
		return ._getIdx()
	}
	if  == "length" {
		return .getLengthProp()
	}
	return .baseObject.getOwnPropStr()
}

func ( *sparseArrayObject) ( valueInt) Value {
	if  := toIdx();  != math.MaxUint32 {
		return ._getIdx()
	}
	return .baseObject.getOwnPropStr(.string())
}

func ( *sparseArrayObject) ( uint32,  Value) {
	 := .findIdx()
	.items = append(.items, sparseArrayItem{})
	copy(.items[+1:], .items[:])
	.items[] = sparseArrayItem{
		idx:   ,
		value: ,
	}
}

func ( *sparseArrayObject) ( uint32,  Value,  bool) bool {
	var  Value
	 := .findIdx()
	if  < len(.items) && .items[].idx ==  {
		 = .items[].value
	}

	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
		}

		if  >= .length {
			if !.setLengthInt(+1, ) {
				return false
			}
		}

		if .expand() {
			.items = append(.items, sparseArrayItem{})
			copy(.items[+1:], .items[:])
			.items[] = sparseArrayItem{
				idx:   ,
				value: ,
			}
		} else {
			 := .val.self.(*arrayObject)
			.values[] = 
			.objCount++
			return true
		}
	} else {
		if ,  := .(*valueProperty);  {
			if !.isWritable() {
				.val.runtime.typeErrorResult()
				return false
			}
			.set(.val, )
		} else {
			.items[].value = 
		}
	}
	return true
}

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

func ( *sparseArrayObject) ( valueInt,  Value,  bool) bool {
	if  := toIdx();  != math.MaxUint32 {
		return ._setOwnIdx(, , )
	}

	return .baseObject.setOwnStr(.string(), , )
}

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

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

type sparseArrayPropIter struct {
	a   *sparseArrayObject
	idx int
}

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

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

func ( *sparseArrayObject) () iterNextFunc {
	return (&sparseArrayPropIter{
		a: ,
	}).next
}

func ( *sparseArrayObject) ( bool,  []Value) []Value {
	if  {
		for ,  := range .items {
			 = append(, asciiString(strconv.FormatUint(uint64(.idx), 10)))
		}
	} else {
		for ,  := range .items {
			if ,  := .value.(*valueProperty);  && !.enumerable {
				continue
			}
			 = append(, asciiString(strconv.FormatUint(uint64(.idx), 10)))
		}
	}

	return .baseObject.stringKeys(, )
}

func ( *sparseArrayObject) ( []Value,  int) {
	.items = make([]sparseArrayItem, 0, )
	for ,  := range  {
		if  != nil {
			.items = append(.items, sparseArrayItem{
				idx:   uint32(),
				value: ,
			})
		}
	}
}

func ( *sparseArrayObject) ( unistring.String) bool {
	if  := strToArrayIdx();  != math.MaxUint32 {
		 := .findIdx()
		return  < len(.items) && .items[].idx == 
	} else {
		return .baseObject.hasOwnPropertyStr()
	}
}

func ( *sparseArrayObject) ( valueInt) bool {
	if  := toIdx();  != math.MaxUint32 {
		 := .findIdx()
		return  < len(.items) && .items[].idx == 
	}

	return .baseObject.hasOwnPropertyStr(.string())
}

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

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

	return false
}

func ( *sparseArrayObject) ( uint32) bool {
	if  := len(.items);  >= 1024 {
		if  := .items[-1].idx;  >  {
			 = 
		}
		if (bits.UintSize == 64 ||  < math.MaxInt32) && int()>>3 <  {
			//log.Println("Switching sparse->standard")
			 := &arrayObject{
				baseObject:     .baseObject,
				length:         .length,
				propValueCount: .propValueCount,
			}
			.setValuesFromSparse(.items, int())
			.val.self = 
			.lengthProp.writable = .lengthProp.writable
			._put("length", &.lengthProp)
			return false
		}
	}
	return true
}

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

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

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

func ( *sparseArrayObject) ( uint32,  bool) bool {
	 := .findIdx()
	if  < len(.items) && .items[].idx ==  {
		if ,  := .items[].value.(*valueProperty);  {
			if !.configurable {
				.val.runtime.typeErrorResult(, "Cannot delete property '%d' of %s", , .val.toString())
				return false
			}
			.propValueCount--
		}
		copy(.items[:], .items[+1:])
		.items[len(.items)-1].value = nil
		.items = .items[:len(.items)-1]
	}
	return true
}

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

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

func ( *sparseArrayObject) () int {
	if len(.items) > 0 {
		return toIntStrict(int64(.items[len(.items)-1].idx) + 1)
	}

	return 0
}

func ( *sparseArrayObject) ( *objectExportCtx) interface{} {
	if ,  := .get(.val);  {
		return 
	}
	 := make([]interface{}, .length)
	.put(.val, )
	var  uint32
	for ,  := range .items {
		 := .idx
		for  := ;  < ; ++ {
			if .prototype != nil {
				if  := .prototype.self.getIdx(valueInt(), nil);  != nil {
					[] = exportValue(, )
				}
			}
		}
		 := .value
		if  != nil {
			if ,  := .(*valueProperty);  {
				 = .get(.val)
			}
			[] = exportValue(, )
		}
		 =  + 1
	}
	for  := ;  < .length; ++ {
		if .prototype != nil {
			if  := .prototype.self.getIdx(valueInt(), nil);  != nil {
				[] = exportValue(, )
			}
		}
	}
	return 
}

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

func ( *sparseArrayObject) ( 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 ,  := range .items {
			 := .value
			if ,  := .(*valueProperty);  {
				 = .get(.val)
			}
			 := toIntStrict(int64(.idx))
			if  >=  {
				break
			}
			 := .toReflectValue(, .Index(), )
			if  != nil {
				return fmt.Errorf("could not convert array element %v to %v at %d: %w", .value, , , )
			}
		}
		return nil
	}
	return .baseObject.exportToArrayOrSlice(, , )
}