// Copyright (C) MongoDB, Inc. 2019-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

// Code generated by operationgen. DO NOT EDIT.

package {{$.PackageName}}

import "go.mongodb.org/mongo-driver/x/mongo/driver"

{{$.EscapeDocumentation $.Documentation}}
type {{$.Name}} struct {

{{- /* Request Parameter Fields */ -}}
{{- range $name, $field := $.Request}}
	{{$name}} {{$field.DeclarationType}}
{{- end -}}

{{- /* Built In Types */ -}}
{{- range $builtin := $.Properties.Builtins}}
	{{$builtin.ReferenceName}} {{$builtin.Type}}
{{- end -}}

{{- /* Retryability */ -}}
{{- if $.Properties.Retryable.Mode}}
	retry *driver.RetryMode
{{- end -}}

{{- /* Response field is below. It will be one of the following. */ -}}

{{- /* Response Type */ -}}
{{- if $.Response.Name}}
	result {{$.ResultType}}
{{- end -}}
{{- /* If we have a response type of batch cursor, we use this result instead */ -}}
{{- if or (eq $.Response.Type "batch cursor") (eq $.Response.Type "list collections batch cursor")}}
	result driver.CursorResponse
{{- end -}}
}

{{if $.Response.Name}}
type {{$.Response.Name}} struct {
{{- range $name, $field := $.Response.Field}}
    {{$.EscapeDocumentation $field.Documentation}}
    {{$.Title $name}} {{$field.DeclarationType}}
{{- end -}}
}

func build{{$.Response.Name}}(response bsoncore.Document, srvr driver.Server) ({{$.Response.Name}}, error) {
	elements, err := response.Elements()
	if err != nil {
		return {{$.Response.Name}}{}, err
	}
	{{$.Response.ShortName}} := {{$.Response.Name}}{}
	for _, element := range elements {
		switch element.Key() {
        {{- $.Response.BuildMethod -}}
		}
	}
	return {{$.Response.ShortName}}, nil
}
{{end}}

// New{{$.Name}} constructs and returns a new {{$.Name}}.
func New{{$.Name}}({{$.ConstructorParameters}}) *{{$.Name}} {
	return &{{$.Name}}{
        {{range $field := $.ConstructorFields -}}
            {{$field}}
        {{end -}}
	}
}

{{if $.Response.Name}}
// Result returns the result of executing this operation.
func ({{$.ShortName}} *{{$.Name}}) Result() {{$.ResultType}} { return {{$.ShortName}}.result }
{{- end}}

{{- /* Batch Cursor Result */ -}}
{{if eq $.Response.Type "batch cursor" -}}
// Result returns the result of executing this operation.
func ({{$.ShortName}} *{{$.Name}}) Result(opts driver.CursorOptions) (*driver.BatchCursor, error) {
{{- /*
    This is all done inline to avoid creating messy output.
    We need to handle setting the client session and the cluster clock. If this operation has those
    fields, we use the reference to them. If it doesn't have those fields then we set the parameter
    to nil.
*/ -}}
    return driver.NewBatchCursor(
    {{- $.ShortName}}.result,

    {{- /* Client Session Parameter */ -}}
    {{- if $builtin := $.Properties.IsEnabled "client session" -}}
        {{$.ShortName}}.{{$builtin.ReferenceName}}
    {{- else -}}
        nil
    {{- end -}},

    {{- /* Cluster Clock Parameter */ -}}
    {{- if $builtin := $.Properties.IsEnabled "cluster clock" -}}
        {{$.ShortName}}.{{$builtin.ReferenceName}}
    {{- else -}}
        nil
    {{- end -}}, opts)
}{{end}}

{{- /* List Collections Batch Cursor Result */ -}}
{{if eq $.Response.Type "list collections batch cursor" -}}
// Result returns the result of executing this operation.
func ({{$.ShortName}} *{{$.Name}}) Result(opts driver.CursorOptions) (*driver.ListCollectionsBatchCursor, error) {
{{- /*
    This is all done inline to avoid creating messy output.
    We need to handle setting the client session and the cluster clock. If this operation has those
    fields, we use the reference to them. If it doesn't have those fields then we set the parameter
    to nil.
*/ -}}
    bc, err := driver.NewBatchCursor(
    {{- $.ShortName}}.result,

    {{- /* Client Session Parameter */ -}}
    {{- if $builtin := $.Properties.IsEnabled "client session" -}}
        {{$.ShortName}}.{{$builtin.ReferenceName}}
    {{- else -}}
        nil
    {{- end -}},

    {{- /* Cluster Clock Parameter */ -}}
    {{- if $builtin := $.Properties.IsEnabled "cluster clock" -}}
        {{$.ShortName}}.{{$builtin.ReferenceName}}
    {{- else -}}
        nil
    {{- end -}}, opts)
	if err != nil {
		return nil, err
	}
	desc := {{$.ShortName}}.result.Desc
	if desc.WireVersion == nil || desc.WireVersion.Max < 3 {
		return driver.NewLegacyListCollectionsBatchCursor(bc)
	}
	return driver.NewListCollectionsBatchCursor(bc)
}{{end}}

func ({{$.ShortName}} *{{$.Name}}) processResponse(response bsoncore.Document, srvr driver.Server, desc description.Server) error {
	var err error
    {{if $.Response.Name -}}
        {{$.ShortName}}.result, err = build{{$.Response.Name}}(response, srvr)
    {{end -}}
    {{if or (eq $.Response.Type "batch cursor") (eq $.Response.Type "list collections batch cursor") -}}
        {{$.ShortName}}.result, err = driver.NewCursorResponse(response, srvr, desc)
    {{end -}}
	return err
}

// Execute runs this operations and returns an error if the operaiton did not execute successfully.
func ({{$.ShortName}} *{{$.Name}}) Execute(ctx context.Context) error {
	if {{$.ShortName}}.deployment == nil {
		return errors.New("the {{$.Name}} operation must have a Deployment set before Execute can be called")
	}
    {{if $.Properties.Batches -}}
    batches := &driver.Batches{
        Identifier: "{{$.Properties.Batches}}",
        Documents: {{$.ShortName}}.{{$.Properties.Batches}},
        {{if index $.Request "ordered" -}}
        Ordered: {{$.ShortName}}.ordered,
        {{- end}}
    }{{end}}
{{with $builtins := $.Properties.BuiltinsMap}}
	return driver.Operation{
		CommandFn: {{$.ShortName}}.command,
		ProcessResponseFn: {{$.ShortName}}.processResponse,

		{{- if $.Properties.Batches}}
        Batches: batches,
		{{- end -}}

        {{- if $.Properties.Retryable.Mode}}
        RetryMode: {{$.ShortName}}.retry,
        {{- end -}}

        {{- if eq $.Properties.Retryable.Type "writes"}}
        Type: driver.Write,
        {{- end -}}

        {{- if eq $.Properties.Retryable.Type "reads"}}
        Type: driver.Read,
        {{- end -}}

        {{- range $b := $.Properties.ExecuteBuiltins}}
        {{$b.ExecuteName}}: {{$.ShortName}}.{{$b.ReferenceName}},
        {{- end -}}

        {{- if $.Properties.MinimumWriteConcernWireVersion}}
        MinimumWriteConcernWireVersion: {{$.Properties.MinimumWriteConcernWireVersion}},
        {{- end -}}

        {{- if $.Properties.MinimumReadConcernWireVersion}}
        MinimumReadConcernWireVersion: {{$.Properties.MinimumReadConcernWireVersion}},
        {{- end -}}

        {{- if ne $.Properties.Legacy ""}}
        Legacy: {{$.Properties.LegacyOperationKind}},
        {{- end}}
	}.Execute(ctx, nil)
{{end}}
}

func ({{$.ShortName}} *{{$.Name}}) command(dst []byte, desc description.SelectedServer) ([]byte, error) {
    {{$.CommandMethod -}}
	return dst, nil
}

{{range $name, $field := $.Request}}
{{$.EscapeDocumentation $field.Documentation}}
func ({{$.ShortName}} *{{$.Name}}) {{$.Title $name}}({{$name}} {{$field.ParameterType}}) *{{$.Name}} {
	if {{$.ShortName}} == nil {
		{{$.ShortName}} = new({{$.Name}})
	}

	{{$.ShortName}}.{{$name}} = {{if $field.PointerType}}&{{end}}{{$name}}
	return {{$.ShortName}}
}
{{end}}

{{range $builtin := $.Properties.Builtins}}
{{$.EscapeDocumentation $builtin.Documentation}}
func ({{$.ShortName}} *{{$.Name}}) {{$builtin.SetterName}}({{$builtin.ReferenceName}} {{$builtin.Type}}) *{{$.Name}} {
	if {{$.ShortName}} == nil {
		{{$.ShortName}} = new({{$.Name}})
	}

	{{$.ShortName}}.{{$builtin.ReferenceName}} = {{$builtin.ReferenceName}}
	return {{$.ShortName}}
}
{{end}}

{{if $.Properties.Retryable.Mode}}
// Retry enables retryable mode for this operation. Retries are handled automatically in driver.Operation.Execute based
// on how the operation is set.
func ({{$.ShortName}} *{{$.Name}}) Retry(retry driver.RetryMode) *{{$.Name}} {
	if {{$.ShortName}} == nil {
		{{$.ShortName}} = new({{$.Name}})
	}

	{{$.ShortName}}.retry = &retry
	return {{$.ShortName}}
}
{{end}}

