// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016 Datadog, Inc.

//go:build ignore
// +build ignore

// This program generates wrapper implementations of http.ResponseWriter that
// also satisfy http.Flusher, http.Pusher, http.CloseNotifier and http.Hijacker,
// based on whether or not the passed in http.ResponseWriter also satisfies
// them.

package main

import (
	"os"
	"text/template"
)

func main() {
	interfaces := []string{"Flusher", "Pusher", "CloseNotifier", "Hijacker"}
	var combos [][][]string
	for pick := len(interfaces); pick > 0; pick-- {
		combos = append(combos, combinations(interfaces, pick))
	}
	template.Must(template.New("").Parse(tpl)).Execute(os.Stdout, map[string]interface{}{
		"Interfaces":   interfaces,
		"Combinations": combos,
	})
}

// combinations returns all possible unique selections of size `pick` of a list
// of strings for which order does not matter
//
// an example:
//
//	Combinations([cat, dog, bird], 2):
//	  [cat] -> Combinations([dog, bird], 1)
//	    [cat, dog]
//	    [cat, bird]
//	  [dog] -> Combinations([bird], 1)
//	    [dog, bird]
//	  [bird] -> Combinations([], 0)
//	    n/a
func combinations(list []string, pick int) (all [][]string) {
	switch pick {
	case 0:
		// nothing to do
	case 1:
		for i := range list {
			all = append(all, list[i:i+1])
		}
	default:
		// we recursively find combinations by taking each item in the list
		// and then finding the combinations at (pick-1) for the remaining
		// items in the list
		// the reason we start at [i+1:], is because the order of the items in
		// the list doesn't matter, so this will remove all the duplicates we
		// would get otherwise
		for i := range list {
			for _, next := range combinations(list[i+1:], pick-1) {
				all = append(all, append([]string{list[i]}, next...))
			}
		}
	}
	return all
}

var tpl = `// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016 Datadog, Inc.

// Code generated by make_responsewriter.go DO NOT EDIT

package httptrace

import "net/http"


// wrapResponseWriter wraps an underlying http.ResponseWriter so that it can
// trace the http response codes. It also checks for various http interfaces
// (Flusher, Pusher, CloseNotifier, Hijacker) and if the underlying
// http.ResponseWriter implements them it generates an unnamed struct with the
// appropriate fields.
//
// This code is generated because we have to account for all the permutations
// of the interfaces.
//
// In case of any new interfaces or methods we didn't consider here, we also
// implement the rwUnwrapper interface, which is used internally by
// the standard library: https://github.com/golang/go/blob/6d89b38ed86e0bfa0ddaba08dc4071e6bb300eea/src/net/http/responsecontroller.go#L42-L44
func wrapResponseWriter(w http.ResponseWriter) (http.ResponseWriter, *responseWriter) {
{{- range .Interfaces }}
	h{{.}}, ok{{.}} := w.(http.{{.}})
{{- end }}

	mw := newResponseWriter(w)
	type monitoredResponseWriter interface {
		http.ResponseWriter
		Status() int
		Unwrap() http.ResponseWriter
	}
	switch {
{{- range .Combinations }}
	{{- range . }}
	case {{ range $i, $v := . }}{{ if gt $i 0 }} && {{ end }}ok{{ $v }}{{ end }}:
		w = struct {
			monitoredResponseWriter
		{{- range . }}
			http.{{.}}
		{{- end }}
		}{mw{{ range . }}, h{{.}}{{ end }}}
	{{- end }}
{{- end }}
	default:
		w = mw
	}

	return w, mw
}
`
