package goja

import 

const propNameStack = "stack"

type errorObject struct {
	baseObject
	stack          []StackFrame
	stackPropAdded bool
}

func ( *errorObject) () String {
	var  StringBuilder
	 := writeErrorString(&, .val)
	if  != nil {
		.WriteString()
	}
	.WriteRune('\n')

	for ,  := range .stack {
		.writeASCII("\tat ")
		.WriteToValueBuilder(&)
		.WriteRune('\n')
	}
	return .String()
}

func ( *errorObject) () Value {
	if !.stackPropAdded {
		 := ._putProp(propNameStack, .formatStack(), true, false, true)
		if len(.propNames) > 1 {
			// reorder property names to ensure 'stack' is the first one
			copy(.propNames[1:], .propNames)
			.propNames[0] = propNameStack
		}
		.stackPropAdded = true
		return 
	}
	return nil
}

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

func ( *errorObject) ( unistring.String) Value {
	 := .baseObject.getOwnPropStr()
	if  == nil &&  == propNameStack {
		return .addStackProp()
	}

	return 
}

func ( *errorObject) ( unistring.String,  Value,  bool) bool {
	if  == propNameStack {
		.addStackProp()
	}
	return .baseObject.setOwnStr(, , )
}

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

func ( *errorObject) ( unistring.String,  bool) bool {
	if  == propNameStack {
		.addStackProp()
	}
	return .baseObject.deleteStr(, )
}

func ( *errorObject) ( unistring.String,  PropertyDescriptor,  bool) bool {
	if  == propNameStack {
		.addStackProp()
	}
	return .baseObject.defineOwnPropertyStr(, , )
}

func ( *errorObject) ( unistring.String) bool {
	if .baseObject.hasOwnPropertyStr() {
		return true
	}

	return  == propNameStack && !.stackPropAdded
}

func ( *errorObject) ( bool,  []Value) []Value {
	if  && !.stackPropAdded {
		 = append(, asciiString(propNameStack))
	}
	return .baseObject.stringKeys(, )
}

func ( *errorObject) () iterNextFunc {
	.addStackProp()
	return .baseObject.iterateStringKeys()
}

func ( *errorObject) () {
	.baseObject.init()
	 := .val.runtime.vm
	.stack = .captureStack(make([]StackFrame, 0, len(.callStack)+1), 0)
}

func ( *Runtime) ( *Object,  string) *errorObject {
	 := &Object{runtime: }
	 := &errorObject{
		baseObject: baseObject{
			class:      ,
			val:        ,
			extensible: true,
			prototype:  ,
		},
	}
	.self = 
	.init()
	return 
}

func ( *Runtime) ( []Value,  *Object) *Object {
	 := .newErrorObject(, classError)
	if len() > 0 && [0] != _undefined {
		._putProp("message", [0].ToString(), true, false, true)
	}
	if len() > 1 && [1] != _undefined {
		if ,  := [1].(*Object);  {
			if .hasProperty(asciiString("cause")) {
				.defineOwnPropertyStr("cause", PropertyDescriptor{
					Writable:     FLAG_TRUE,
					Enumerable:   FLAG_FALSE,
					Configurable: FLAG_TRUE,
					Value:        .Get("cause"),
				}, true)
			}
		}
	}
	return .val
}

func ( *Runtime) ( []Value,  *Object) *Object {
	 := .newErrorObject(, classError)
	if len() > 1 && [1] != nil && [1] != _undefined {
		._putProp("message", [1].toString(), true, false, true)
	}
	var  []Value
	if len() > 0 {
		 = .iterableToList([0], nil)
	}
	._putProp("errors", .newArrayValues(), true, false, true)

	if len() > 2 && [2] != _undefined {
		if ,  := [2].(*Object);  {
			if .hasProperty(asciiString("cause")) {
				.defineOwnPropertyStr("cause", PropertyDescriptor{
					Writable:     FLAG_TRUE,
					Enumerable:   FLAG_FALSE,
					Configurable: FLAG_TRUE,
					Value:        .Get("cause"),
				}, true)
			}
		}
	}

	return .val
}

func writeErrorString( *StringBuilder,  *Object) String {
	var ,  String
	 := .self.getStr("name", nil)
	if  == nil ||  == _undefined {
		 = asciiString("Error")
	} else {
		 = .toString()
	}
	 := .self.getStr("message", nil)
	if  == nil ||  == _undefined {
		 = stringEmpty
	} else {
		 = .toString()
	}
	if .Length() == 0 {
		return 
	}
	if .Length() == 0 {
		return 
	}
	.WriteString()
	.WriteString(asciiString(": "))
	.WriteString()
	return nil
}

func ( *Runtime) ( FunctionCall) Value {
	var  StringBuilder
	 := writeErrorString(&, .toObject(.This))
	if  != nil {
		return 
	}
	return .String()
}

func ( *Runtime) ( String,  *Object) *Object {
	 := .newBaseObject(.getErrorPrototype(), classObject)
	._putProp("message", stringEmpty, true, false, true)
	._putProp("name", , true, false, true)
	._putProp("constructor", , true, false, true)
	return .val
}

func ( *Runtime) () *Object {
	 := .global.ErrorPrototype
	if  == nil {
		 = .NewObject()
		.global.ErrorPrototype = 
		 := .self
		._putProp("message", stringEmpty, true, false, true)
		._putProp("name", stringError, true, false, true)
		._putProp("toString", .newNativeFunc(.error_toString, "toString", 0), true, false, true)
		._putProp("constructor", .getError(), true, false, true)
	}
	return 
}

func ( *Runtime) () *Object {
	 := .global.Error
	if  == nil {
		 = &Object{runtime: }
		.global.Error = 
		.newNativeFuncConstruct(, .builtin_Error, "Error", .getErrorPrototype(), 1)
	}
	return 
}

func ( *Runtime) () *Object {
	 := .global.AggregateError
	if  == nil {
		 = &Object{runtime: }
		.global.AggregateError = 
		.newNativeFuncConstructProto(, .builtin_AggregateError, "AggregateError", .createErrorPrototype(stringAggregateError, ), .getError(), 2)
	}
	return 
}

func ( *Runtime) () *Object {
	 := .global.TypeError
	if  == nil {
		 = &Object{runtime: }
		.global.TypeError = 
		.newNativeFuncConstructProto(, .builtin_Error, "TypeError", .createErrorPrototype(stringTypeError, ), .getError(), 1)
	}
	return 
}

func ( *Runtime) () *Object {
	 := .global.ReferenceError
	if  == nil {
		 = &Object{runtime: }
		.global.ReferenceError = 
		.newNativeFuncConstructProto(, .builtin_Error, "ReferenceError", .createErrorPrototype(stringReferenceError, ), .getError(), 1)
	}
	return 
}

func ( *Runtime) () *Object {
	 := .global.SyntaxError
	if  == nil {
		 = &Object{runtime: }
		.global.SyntaxError = 
		.newNativeFuncConstructProto(, .builtin_Error, "SyntaxError", .createErrorPrototype(stringSyntaxError, ), .getError(), 1)
	}
	return 
}

func ( *Runtime) () *Object {
	 := .global.RangeError
	if  == nil {
		 = &Object{runtime: }
		.global.RangeError = 
		.newNativeFuncConstructProto(, .builtin_Error, "RangeError", .createErrorPrototype(stringRangeError, ), .getError(), 1)
	}
	return 
}

func ( *Runtime) () *Object {
	 := .global.EvalError
	if  == nil {
		 = &Object{runtime: }
		.global.EvalError = 
		.newNativeFuncConstructProto(, .builtin_Error, "EvalError", .createErrorPrototype(stringEvalError, ), .getError(), 1)
	}
	return 
}

func ( *Runtime) () *Object {
	 := .global.URIError
	if  == nil {
		 = &Object{runtime: }
		.global.URIError = 
		.newNativeFuncConstructProto(, .builtin_Error, "URIError", .createErrorPrototype(stringURIError, ), .getError(), 1)
	}
	return 
}

func ( *Runtime) () *Object {
	 := .global.GoError
	if  == nil {
		 = &Object{runtime: }
		.global.GoError = 
		.newNativeFuncConstructProto(, .builtin_Error, "GoError", .createErrorPrototype(stringGoError, ), .getError(), 1)
	}
	return 
}