// +build ignore

package main

import (
	"os"
	"text/template"
)

func main() {
	Run(srcTemplate, testTemplate, os.Args...)
}

var srcTemplate = template.Must(template.New("").Parse(`package {{.Package}}

// go generate {{.Args}}
// GENERATED CODE FOLLOWS; DO NOT EDIT.

import (
	"context"

	"github.com/mesos/mesos-go/api/v1/lib"
	"github.com/mesos/mesos-go/api/v1/lib/client"
	"github.com/mesos/mesos-go/api/v1/lib/httpcli"
{{range .Imports}}{{/* a "calls" package is assumed to be imported */}}
	{{ printf "%q" . -}}
{{end}}
)

{{.RequireType "C" -}}{{/* C is assumed to be a struct type */ -}}
{{.RequirePrototype "C" -}}
// ResponseClassifier determines the appropriate response class for the given call.
type ResponseClassifier func(*{{.Type "C"}}) (client.ResponseClass, error)

// ClientFunc sends a Request to Mesos and returns the generated Response.
type ClientFunc func(client.Request, client.ResponseClass, ...httpcli.RequestOpt) (mesos.Response, error)

{{/* classifyResponse must be declared in the package wherein this generated module lives */ -}}
// DefaultResponseClassifier is a pluggable classifier.
var DefaultResponseClassifier = ResponseClassifier(classifyResponse)

// NewSender generates a sender that uses the Mesos v1 HTTP API for encoding/decoding requests/responses.
// The ResponseClass is inferred from the first object generated by the given Request.
func NewSender(cf ClientFunc, ro ...httpcli.RequestOpt) calls.Sender {
	return calls.SenderFunc(func(ctx context.Context, r calls.Request) (mesos.Response, error) {
		var (
			obj     = r.Call()
			rc, err = DefaultResponseClassifier(obj)
		)
		if err != nil {
			return nil, err
		}

		var req client.Request

		switch r := r.(type) {
		case calls.RequestStreaming:
			req = calls.Push(r, obj)
		default:
			req = calls.NonStreaming(obj)
		}

		return cf(req, rc, append(ro, httpcli.Context(ctx))...)
	})
}
`))
var testTemplate = template.Must(template.New("").Parse(`package {{.Package}}

// go generate {{.Args}}
// GENERATED CODE FOLLOWS; DO NOT EDIT.

import (
	"context"
	"testing"

	"github.com/mesos/mesos-go/api/v1/lib"
	"github.com/mesos/mesos-go/api/v1/lib/client"
	"github.com/mesos/mesos-go/api/v1/lib/httpcli"
{{range .Imports}}{{/* a "calls" package is assumed to be imported */}}
	{{ printf "%q" . -}}
{{end}}
)

func TestNewSender(t *testing.T) {
	ch := make(chan client.Request, 1)
	cf := ClientFunc(func(r client.Request, _ client.ResponseClass, _ ...httpcli.RequestOpt) (_ mesos.Response, _ error) {
		ch <- r
		return
	})
	check := func(_ mesos.Response, err error) {
		if err != nil {
			t.Fatal(err)
		}
	}
	sent := func() client.Request {
		select {
		case r := <-ch:
			return r
		default:
			t.Fatal("no request was sent")
		}
		return nil
	}
	sender := NewSender(cf)
	c := &{{.Prototype "C"}}

	check(sender.Send(context.Background(), calls.NonStreaming(c)))
	r := sent()
	if _, ok := r.(client.RequestStreaming); ok {
		t.Fatalf("expected non-streaming request instead of %v", r)
	}

	check(sender.Send(context.Background(), calls.Empty().Push(c)))
	r = sent()
	if _, ok := r.(client.RequestStreaming); !ok {
		t.Fatalf("expected streaming request instead of %v", r)
	}

	// expect this to fail because newly created call structs don't have a type
	// that can be used for classifying an expected response type.
	_, err := sender.Send(context.Background(), calls.Empty().Push(new({{.Type "C"}})))
	if err == nil {
		t.Fatal("expected send to fail w/ malformed call")
	}
}
`))
