"""An object definition.
   
   Examples (multi-line):
   
       shared object red extends Color(255, 0, 0) {
           string => "Red";
       }
   
       object stdoutWriter satisfies Writer {
           shared actual void close() => flush();
           shared actual void flush() {}
           shared actual void write(String string) {
               process.write(string);
           }
           shared actual void writeLine(String line) {
               process.writeLine(line);
           }
           shared actual void writeBytes({Byte*} bytes) {
               throw AssertionError("Can’t write bytes");
           }
       }"""
shared class ObjectDefinition(name, body, extendedType = null, satisfiedTypes = null, annotations = Annotations())
        extends Declaration() {
    
    "The name of the object.
     
     (The name of the associated anonymous class is generated by the compiler
     and cannot be specified by the program.)"
    shared actual LIdentifier name;
    "The body of the associated anonymous class."
    shared ClassBody body;
    "The extended type of the associated anonymous class, if present."
    shared ExtendedType? extendedType;
    "The satisfied types of the associated anonymous class, if present."
    shared SatisfiedTypes? satisfiedTypes;
    "The annotations of the object and the associated anonymous class."
    shared actual Annotations annotations;
    
    shared actual <Annotations|LIdentifier|ExtendedType|SatisfiedTypes|ClassBody>[] children
            = concatenate(
                [annotations],
                [name],
                emptyOrSingleton(extendedType),
                emptyOrSingleton(satisfiedTypes),
                [body]
            );
    
    shared actual Result transform<out Result>(Transformer<Result> transformer)
            => transformer.transformObjectDefinition(this);
    
    shared actual void visit(Visitor visitor)
            => visitor.visitObjectDefinition(this);
    
    shared actual Boolean equals(Object that) {
        if (is ObjectDefinition that) {
            if (exists extendedType) {
                if (exists extendedType_ = that.extendedType) {
                    if (extendedType != extendedType_) {
                        return false;
                    }
                } else {
                    return false;
                }
            } else if (that.extendedType exists) {
                return false;
            }
            if (exists satisfiedTypes) {
                if (exists satisfiedTypes_ = that.satisfiedTypes) {
                    if (satisfiedTypes != satisfiedTypes_) {
                        return false;
                    }
                } else {
                    return false;
                }
            } else if (that.satisfiedTypes exists) {
                return false;
            }
            return name==that.name && body==that.body && annotations==that.annotations;
        } else {
            return false;
        }
    }
    
    shared actual Integer hash
            => 31 * (name.hash + 31 * (body.hash + 31 * ((extendedType?.hash else 0) + 31 * ((satisfiedTypes?.hash else 0) + 31*annotations.hash))));
    
    shared ObjectDefinition copy(LIdentifier name = this.name, ClassBody body = this.body, ExtendedType? extendedType = this.extendedType, SatisfiedTypes? satisfiedTypes = this.satisfiedTypes, Annotations annotations = this.annotations) {
        value ret = ObjectDefinition(name, body, extendedType, satisfiedTypes, annotations);
        copyExtraInfoTo(ret);
        return ret;
    }
}