// +build !go1.8

package httpstat

import (
	"context"
	"net"
	"net/http/httptrace"
	"time"
)

// End sets the time when reading response is done.
// This must be called after reading response body.
func (r *Result) End(t time.Time) {
	r.t5 = t

	// This means result is empty (it does nothing).
	// Skip setting value(contentTransfer and total will be zero).
	if r.t0.IsZero() {
		return
	}

	r.contentTransfer = r.t5.Sub(r.t4)
	r.total = r.t5.Sub(r.t0)
}

// ContentTransfer returns the duration of content transfer time.
// It is from first response byte to the given time. The time must
// be time after read body (go-httpstat can not detect that time).
func (r *Result) ContentTransfer(t time.Time) time.Duration {
	return t.Sub(r.t4)
}

// Total returns the duration of total http request.
// It is from dns lookup start time to the given time. The
// time must be time after read body (go-httpstat can not detect that time).
func (r *Result) Total(t time.Time) time.Duration {
	return t.Sub(r.t0)
}

func withClientTrace(ctx context.Context, r *Result) context.Context {
	return httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
		GetConn: func(hostPort string) {
			_, port, err := net.SplitHostPort(hostPort)
			if err != nil {
				return
			}

			// Heuristic way to detect
			if port == "443" {
				r.isTLS = true
			}
		},

		DNSStart: func(i httptrace.DNSStartInfo) {
			r.t0 = time.Now()
		},
		DNSDone: func(i httptrace.DNSDoneInfo) {
			r.t1 = time.Now()
			r.DNSLookup = r.t1.Sub(r.t0)
			r.NameLookup = r.t1.Sub(r.t0)
		},

		ConnectStart: func(_, _ string) {
			// When connecting to IP
			if r.t0.IsZero() {
				r.t0 = time.Now()
				r.t1 = r.t0
			}
		},

		ConnectDone: func(network, addr string, err error) {
			r.t2 = time.Now()
			if r.isTLS {
				r.TCPConnection = r.t2.Sub(r.t1)
				r.Connect = r.t2.Sub(r.t0)
			}
		},

		GotConn: func(i httptrace.GotConnInfo) {
			// Handle when keep alive is enabled and connection is reused.
			// DNSStart(Done) and ConnectStart(Done) is skipped
			if i.Reused {
				r.t0 = time.Now()
				r.t1 = r.t0
				r.t2 = r.t0

				r.isReused = true
			}
		},

		WroteRequest: func(info httptrace.WroteRequestInfo) {
			r.t3 = time.Now()

			// This means DNSStart, Done and ConnectStart is not
			// called. This happens if client doesn't use DialContext
			// or using net package before go1.7.
			if r.t0.IsZero() && r.t1.IsZero() && r.t2.IsZero() {
				r.t0 = time.Now()
				r.t1 = r.t0
				r.t2 = r.t0
				r.t3 = r.t0
			}

			// When connection is reused, TLS handshake is skipped.
			if r.isReused {
				r.t3 = r.t0
			}

			if r.isTLS {
				r.TLSHandshake = r.t3.Sub(r.t2)
				r.Pretransfer = r.t3.Sub(r.t0)
				return
			}

			r.TCPConnection = r.t3.Sub(r.t1)
			r.Connect = r.t3.Sub(r.t0)

			r.TLSHandshake = r.t3.Sub(r.t3)
			r.Pretransfer = r.Connect
		},
		GotFirstResponseByte: func() {
			r.t4 = time.Now()
			r.ServerProcessing = r.t4.Sub(r.t3)
			r.StartTransfer = r.t4.Sub(r.t0)
		},
	})
}
