package operation

import (
	"context"
	"time"
)

// A Starter links together Op creation sites (client and server
// implementations) with measurement sites (metrics reporting, tracing,
// profile cost attribution).
type Starter struct {
	OpMonitors []OpMonitor
}

// StartOp marks the start of an operation with the provided name. It calls
// the each entry in OpMonitors in order, notifying them that an operation has
// begun and retaining the MonitorPoint values they return (if any).
//
// The context value is passed through each OpMonitor, so later entries may
// see changes made by earlier ones. The final context value is returned.
//
// The caller of StartOp must call the End method of the returned Op once,
// when the operation is complete. At that time, the package calls the End
// function of each MonitorPoint in reverse (nested) order.
func (sm *Starter) StartOp(ctx context.Context, name Name) (context.Context, *Op) {
	op := &Op{
		name:      name,
		startTime: time.Now(),
	}

	for _, layer := range sm.monitors() {
		var mon *MonitorPoints
		ctx, mon = layer.MonitorOp(ctx, name)
		if mon != nil {
			op.monitors = append(op.monitors, mon)
		}
	}

	ctx = NewViewContext(ctx, op.View())

	return ctx, op
}

func (sm *Starter) monitors() []OpMonitor {
	if sm == nil {
		return nil
	}
	return sm.OpMonitors
}
