package goja

type argumentsObject struct {
	baseObject
	length int
}

type mappedProperty struct {
	valueProperty
	v *Value
}

func (a *argumentsObject) getPropStr(name string) Value {
	if prop, ok := a.values[name].(*mappedProperty); ok {
		return *prop.v
	}
	return a.baseObject.getPropStr(name)
}

func (a *argumentsObject) getProp(n Value) Value {
	return a.getPropStr(n.String())
}

func (a *argumentsObject) init() {
	a.baseObject.init()
	a._putProp("length", intToValue(int64(a.length)), true, false, true)
}

func (a *argumentsObject) put(n Value, val Value, throw bool) {
	a.putStr(n.String(), val, throw)
}

func (a *argumentsObject) putStr(name string, val Value, throw bool) {
	if prop, ok := a.values[name].(*mappedProperty); ok {
		if !prop.writable {
			a.val.runtime.typeErrorResult(throw, "Property is not writable: %s", name)
			return
		}
		*prop.v = val
		return
	}
	a.baseObject.putStr(name, val, throw)
}

func (a *argumentsObject) deleteStr(name string, throw bool) bool {
	if prop, ok := a.values[name].(*mappedProperty); ok {
		if !a.checkDeleteProp(name, &prop.valueProperty, throw) {
			return false
		}
		a._delete(name)
		return true
	}

	return a.baseObject.deleteStr(name, throw)
}

func (a *argumentsObject) delete(n Value, throw bool) bool {
	return a.deleteStr(n.String(), throw)
}

type argumentsPropIter1 struct {
	a         *argumentsObject
	idx       int
	recursive bool
}

type argumentsPropIter struct {
	wrapped iterNextFunc
}

func (i *argumentsPropIter) next() (propIterItem, iterNextFunc) {
	var item propIterItem
	item, i.wrapped = i.wrapped()
	if i.wrapped == nil {
		return propIterItem{}, nil
	}
	if prop, ok := item.value.(*mappedProperty); ok {
		item.value = *prop.v
	}
	return item, i.next
}

func (a *argumentsObject) _enumerate(recursive bool) iterNextFunc {
	return (&argumentsPropIter{
		wrapped: a.baseObject._enumerate(recursive),
	}).next

}

func (a *argumentsObject) enumerate(all, recursive bool) iterNextFunc {
	return (&argumentsPropIter{
		wrapped: a.baseObject.enumerate(all, recursive),
	}).next
}

func (a *argumentsObject) defineOwnProperty(n Value, descr objectImpl, throw bool) bool {
	name := n.String()
	if mapped, ok := a.values[name].(*mappedProperty); ok {
		existing := &valueProperty{
			configurable: mapped.configurable,
			writable:     true,
			enumerable:   mapped.enumerable,
			value:        mapped.get(a.val),
		}

		val, ok := a.baseObject._defineOwnProperty(n, existing, descr, throw)
		if !ok {
			return false
		}

		if prop, ok := val.(*valueProperty); ok {
			if !prop.accessor {
				*mapped.v = prop.value
			}
			if prop.accessor || !prop.writable {
				a._put(name, prop)
				return true
			}
			mapped.configurable = prop.configurable
			mapped.enumerable = prop.enumerable
		} else {
			*mapped.v = val
			mapped.configurable = true
			mapped.enumerable = true
		}

		return true
	}

	return a.baseObject.defineOwnProperty(n, descr, throw)
}

func (a *argumentsObject) getOwnProp(name string) Value {
	if mapped, ok := a.values[name].(*mappedProperty); ok {
		return *mapped.v
	}

	return a.baseObject.getOwnProp(name)
}

func (a *argumentsObject) export() interface{} {
	arr := make([]interface{}, a.length)
	for i, _ := range arr {
		v := a.get(intToValue(int64(i)))
		if v != nil {
			arr[i] = v.Export()
		}
	}
	return arr
}
