/*
 *
 * Copyright 2017 gRPC authors.
 *
 * 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package base

import (
	
	

	
	
	
	
)

var logger = grpclog.Component("balancer")

type baseBuilder struct {
	name          string
	pickerBuilder PickerBuilder
	config        Config
}

func ( *baseBuilder) ( balancer.ClientConn,  balancer.BuildOptions) balancer.Balancer {
	 := &baseBalancer{
		cc:            ,
		pickerBuilder: .pickerBuilder,

		subConns: resolver.NewAddressMap(),
		scStates: make(map[balancer.SubConn]connectivity.State),
		csEvltr:  &balancer.ConnectivityStateEvaluator{},
		config:   .config,
		state:    connectivity.Connecting,
	}
	// Initialize picker to a picker that always returns
	// ErrNoSubConnAvailable, because when state of a SubConn changes, we
	// may call UpdateState with this picker.
	.picker = NewErrPicker(balancer.ErrNoSubConnAvailable)
	return 
}

func ( *baseBuilder) () string {
	return .name
}

type baseBalancer struct {
	cc            balancer.ClientConn
	pickerBuilder PickerBuilder

	csEvltr *balancer.ConnectivityStateEvaluator
	state   connectivity.State

	subConns *resolver.AddressMap
	scStates map[balancer.SubConn]connectivity.State
	picker   balancer.Picker
	config   Config

	resolverErr error // the last error reported by the resolver; cleared on successful resolution
	connErr     error // the last connection error; cleared upon leaving TransientFailure
}

func ( *baseBalancer) ( error) {
	.resolverErr = 
	if .subConns.Len() == 0 {
		.state = connectivity.TransientFailure
	}

	if .state != connectivity.TransientFailure {
		// The picker will not change since the balancer does not currently
		// report an error.
		return
	}
	.regeneratePicker()
	.cc.UpdateState(balancer.State{
		ConnectivityState: .state,
		Picker:            .picker,
	})
}

func ( *baseBalancer) ( balancer.ClientConnState) error {
	// TODO: handle s.ResolverState.ServiceConfig?
	if logger.V(2) {
		logger.Info("base.baseBalancer: got new ClientConn state: ", )
	}
	// Successful resolution; clear resolver error and ensure we return nil.
	.resolverErr = nil
	// addrsSet is the set converted from addrs, it's used for quick lookup of an address.
	 := resolver.NewAddressMap()
	for ,  := range .ResolverState.Addresses {
		.Set(, nil)
		if ,  := .subConns.Get(); ! {
			// a is a new address (not existing in b.subConns).
			,  := .cc.NewSubConn([]resolver.Address{}, balancer.NewSubConnOptions{HealthCheckEnabled: .config.HealthCheck})
			if  != nil {
				logger.Warningf("base.baseBalancer: failed to create new SubConn: %v", )
				continue
			}
			.subConns.Set(, )
			.scStates[] = connectivity.Idle
			.csEvltr.RecordTransition(connectivity.Shutdown, connectivity.Idle)
			.Connect()
		}
	}
	for ,  := range .subConns.Keys() {
		,  := .subConns.Get()
		 := .(balancer.SubConn)
		// a was removed by resolver.
		if ,  := .Get(); ! {
			.cc.RemoveSubConn()
			.subConns.Delete()
			// Keep the state of this sc in b.scStates until sc's state becomes Shutdown.
			// The entry will be deleted in UpdateSubConnState.
		}
	}
	// If resolver state contains no addresses, return an error so ClientConn
	// will trigger re-resolve. Also records this as an resolver error, so when
	// the overall state turns transient failure, the error message will have
	// the zero address information.
	if len(.ResolverState.Addresses) == 0 {
		.ResolverError(errors.New("produced zero addresses"))
		return balancer.ErrBadResolverState
	}

	.regeneratePicker()
	.cc.UpdateState(balancer.State{ConnectivityState: .state, Picker: .picker})
	return nil
}

// mergeErrors builds an error from the last connection error and the last
// resolver error.  Must only be called if b.state is TransientFailure.
func ( *baseBalancer) () error {
	// connErr must always be non-nil unless there are no SubConns, in which
	// case resolverErr must be non-nil.
	if .connErr == nil {
		return fmt.Errorf("last resolver error: %v", .resolverErr)
	}
	if .resolverErr == nil {
		return fmt.Errorf("last connection error: %v", .connErr)
	}
	return fmt.Errorf("last connection error: %v; last resolver error: %v", .connErr, .resolverErr)
}

// regeneratePicker takes a snapshot of the balancer, and generates a picker
// from it. The picker is
//   - errPicker if the balancer is in TransientFailure,
//   - built by the pickerBuilder with all READY SubConns otherwise.
func ( *baseBalancer) () {
	if .state == connectivity.TransientFailure {
		.picker = NewErrPicker(.mergeErrors())
		return
	}
	 := make(map[balancer.SubConn]SubConnInfo)

	// Filter out all ready SCs from full subConn map.
	for ,  := range .subConns.Keys() {
		,  := .subConns.Get()
		 := .(balancer.SubConn)
		if ,  := .scStates[];  &&  == connectivity.Ready {
			[] = SubConnInfo{Address: }
		}
	}
	.picker = .pickerBuilder.Build(PickerBuildInfo{ReadySCs: })
}

func ( *baseBalancer) ( balancer.SubConn,  balancer.SubConnState) {
	 := .ConnectivityState
	if logger.V(2) {
		logger.Infof("base.baseBalancer: handle SubConn state change: %p, %v", , )
	}
	,  := .scStates[]
	if ! {
		if logger.V(2) {
			logger.Infof("base.baseBalancer: got state changes for an unknown SubConn: %p, %v", , )
		}
		return
	}
	if  == connectivity.TransientFailure &&
		( == connectivity.Connecting ||  == connectivity.Idle) {
		// Once a subconn enters TRANSIENT_FAILURE, ignore subsequent IDLE or
		// CONNECTING transitions to prevent the aggregated state from being
		// always CONNECTING when many backends exist but are all down.
		if  == connectivity.Idle {
			.Connect()
		}
		return
	}
	.scStates[] = 
	switch  {
	case connectivity.Idle:
		.Connect()
	case connectivity.Shutdown:
		// When an address was removed by resolver, b called RemoveSubConn but
		// kept the sc's state in scStates. Remove state for this sc here.
		delete(.scStates, )
	case connectivity.TransientFailure:
		// Save error to be reported via picker.
		.connErr = .ConnectionError
	}

	.state = .csEvltr.RecordTransition(, )

	// Regenerate picker when one of the following happens:
	//  - this sc entered or left ready
	//  - the aggregated state of balancer is TransientFailure
	//    (may need to update error message)
	if ( == connectivity.Ready) != ( == connectivity.Ready) ||
		.state == connectivity.TransientFailure {
		.regeneratePicker()
	}
	.cc.UpdateState(balancer.State{ConnectivityState: .state, Picker: .picker})
}

// Close is a nop because base balancer doesn't have internal state to clean up,
// and it doesn't need to call RemoveSubConn for the SubConns.
func ( *baseBalancer) () {
}

// ExitIdle is a nop because the base balancer attempts to stay connected to
// all SubConns at all times.
func ( *baseBalancer) () {
}

// NewErrPicker returns a Picker that always returns err on Pick().
func ( error) balancer.Picker {
	return &errPicker{err: }
}

// NewErrPickerV2 is temporarily defined for backward compatibility reasons.
//
// Deprecated: use NewErrPicker instead.
var NewErrPickerV2 = NewErrPicker

type errPicker struct {
	err error // Pick() always returns this err.
}

func ( *errPicker) ( balancer.PickInfo) (balancer.PickResult, error) {
	return balancer.PickResult{}, .err
}