package tcp

import (
	"crypto/tls"
	"fmt"
	"net"
	"time"
)

// NewDialer returns an initialized Dialer.
func NewDialer(header byte, tlsConfig *tls.Config) *Dialer {
	return &Dialer{
		header:    header,
		tlsConfig: tlsConfig,
	}
}

// Dialer supports dialing a cluster service.
type Dialer struct {
	header    byte
	tlsConfig *tls.Config
}

// Dial dials the cluster service at the given addr and returns a connection.
func (d *Dialer) Dial(addr string, timeout time.Duration) (conn net.Conn, retErr error) {
	dialer := &net.Dialer{Timeout: timeout}

	if d.tlsConfig == nil {
		conn, retErr = dialer.Dial("tcp", addr)
	} else {
		conn, retErr = tls.DialWithDialer(dialer, "tcp", addr, d.tlsConfig)
	}
	if retErr != nil {
		return nil, retErr
	}

	defer func() {
		if retErr != nil && conn != nil {
			conn.Close()
		}
	}()

	// Write a marker byte to indicate message type.
	if err := conn.SetWriteDeadline(time.Now().Add(timeout)); err != nil {
		return nil, fmt.Errorf("failed to set WriteDeadline for header: %s", err.Error())
	}
	if _, err := conn.Write([]byte{d.header}); err != nil {
		return nil, err
	}
	return conn, nil
}
