package dcs

import (
	
	
	
	

	
	

	
	
	
	
	
	
)

var _ Resolver = plain{}

type plain struct {
	dial         DialFunc
	protocol     Protocol
	rand         io.Reader
	network      string
	noObfuscated bool
	preferIPv6   bool
}

func ( plain) ( context.Context,  int,  List) (transport.Conn, error) {
	 := FindPrimaryDCs(.Options, , .preferIPv6)
	if .noObfuscated {
		 := 0
		for ,  := range  {
			if !.TCPObfuscatedOnly {
				[] = 
				++
			}
		}
		 = [:]
	}
	return .connect(, , .Test, )
}

func ( plain) ( context.Context,  int,  List) (transport.Conn, error) {
	 := FindDCs(.Options, , .preferIPv6)
	// Filter (in place) from SliceTricks.
	 := 0
	for ,  := range  {
		if .MediaOnly {
			[] = 
			++
		}
	}
	return .connect(, , .Test, [:])
}

func ( plain) ( context.Context,  int,  List) (transport.Conn, error) {
	return nil, errors.Errorf("can't resolve %d: CDN is unsupported", )
}

func ( plain) ( context.Context,  bool,  tg.DCOption) ( transport.Conn,  error) {
	 := net.JoinHostPort(.IPAddress, strconv.Itoa(.Port))

	,  := .dial(, .network, )
	if  != nil {
		return nil, 
	}
	defer func() {
		if  != nil {
			multierr.AppendInto(&, .Close())
		}
	}()

	 := .protocol
	if .TCPObfuscatedOnly {
		 := .ID
		if  {
			if  < 0 {
				 -= 10000
			} else {
				 += 10000
			}
		}

		var (
			  codec.Codec = codec.Intermediate{}
			              = codec.IntermediateClientStart
			             = obfuscator.Obfuscated2
		)

		,  := mtproxy.ParseSecret(.Secret)
		if  != nil {
			return nil, errors.Wrap(, "check DC secret")
		}

		if .Type == mtproxy.TLS {
			 = obfuscator.FakeTLS
		} else if ,  := .ExpectedCodec();  {
			 = [4]byte{.Tag, .Tag, .Tag, .Tag}
			 = 
		}

		 := (.rand, )
		if  := .Handshake(, , );  != nil {
			return nil, 
		}
		 = 

		 = transport.NewProtocol(func() transport.Codec {
			return codec.NoHeader{Codec: }
		})
	}

	,  := .Handshake()
	if  != nil {
		return nil, errors.Wrap(, "transport handshake")
	}

	return , nil
}

func ( plain) ( context.Context,  int,  bool,  []tg.DCOption) (transport.Conn, error) {
	switch len() {
	case 0:
		return nil, errors.Errorf("no addresses for DC %d", )
	case 1:
		return .dialTransport(, , [0])
	}

	type  struct {
		 transport.Conn
		  error
	}

	// We use unbuffered channel to ensure that only one connection will be returned
	// and all other will be closed.
	 := make(chan )
	 := func( context.Context,  tg.DCOption) {
		,  := .dialTransport(, , )
		select {
		case  <- {
			: ,
			:  ,
		}:
		case <-.Done():
			if  != nil {
				_ = .Close()
			}
		}
	}

	,  := context.WithCancel()
	defer ()

	for ,  := range  {
		go (, )
	}

	 := len()
	var  error
	for {
		select {
		case <-.Done():
			return nil, .Err()
		case  := <-:
			--
			if . != nil {
				 = multierr.Append(, .)
				if  == 0 {
					return nil, 
				}
				continue
			}
			return ., nil
		}
	}
}

// PlainOptions is plain resolver creation options.
type PlainOptions struct {
	// Protocol is the transport protocol to use. Defaults to intermediate.
	Protocol Protocol
	// Dial specifies the dial function for creating unencrypted TCP connections.
	// If Dial is nil, then the resolver dials using package net.
	Dial DialFunc
	// Random source for TCPObfuscated DCs.
	Rand io.Reader
	// Network to use. Defaults to "tcp".
	Network string
	// NoObfuscated denotes to filter out TCP Obfuscated Only DCs.
	NoObfuscated bool
	// PreferIPv6 gives IPv6 DCs higher precedence.
	// Default is to prefer IPv4 DCs over IPv6.
	PreferIPv6 bool
}

func ( *PlainOptions) () {
	if .Protocol == nil {
		.Protocol = transport.Intermediate
	}
	if .Dial == nil {
		var  net.Dialer
		.Dial = .DialContext
	}
	if .Rand == nil {
		.Rand = crypto.DefaultRand()
	}
	if .Network == "" {
		.Network = "tcp"
	}
}

// Plain creates plain DC resolver.
func ( PlainOptions) Resolver {
	.setDefaults()
	return plain{
		dial:         .Dial,
		protocol:     .Protocol,
		rand:         .Rand,
		network:      .Network,
		noObfuscated: .NoObfuscated,
		preferIPv6:   .PreferIPv6,
	}
}