package faketls

import (
	
	
	

	

	
	
)

// FakeTLS implements FakeTLS obfuscation protocol.
type FakeTLS struct {
	rand  io.Reader
	clock clock.Clock
	conn  io.ReadWriter

	version     [2]byte
	firstPacket bool

	readBuf    bytes.Buffer
	readBufMux sync.Mutex
}

// NewFakeTLS creates new FakeTLS.
func ( io.Reader,  io.ReadWriter) *FakeTLS {
	return &FakeTLS{
		rand:    ,
		clock:   clock.System,
		conn:    ,
		version: Version12Bytes,
		readBuf: bytes.Buffer{},
	}
}

// Handshake performs FakeTLS handshake.
func ( *FakeTLS) ( [4]byte,  int,  mtproxy.Secret) error {
	.readBufMux.Lock()
	.readBuf.Reset()
	.readBufMux.Unlock()

	var  [32]byte
	if ,  := .rand.Read([:]);  != nil {
		return errors.Wrap(, "generate sessionID")
	}

	,  := writeClientHello(.conn, .clock, , .CloakHost, .Secret)
	if  != nil {
		return errors.Wrap(, "send ClientHello")
	}

	if  := readServerHello(.conn, , .Secret);  != nil {
		return errors.Wrap(, "receive ServerHello")
	}
	return nil
}

// Write implements io.Writer.
func ( *FakeTLS) ( []byte) ( int,  error) {
	// Some proxies require sending first packet with RecordTypeChangeCipherSpec.
	//
	// See https://github.com/tdlib/td/blob/master/td/mtproto/TcpTransport.cpp#L266.
	if !.firstPacket {
		_,  = writeRecord(.conn, record{
			Type:    RecordTypeChangeCipherSpec,
			Version: .version,
			Data:    []byte("\x01"),
		})
		if  != nil {
			return 0, errors.Wrap(, "write first TLS packet")
		}
		.firstPacket = true
	}
	,  = writeRecord(.conn, record{
		Type:    RecordTypeApplication,
		Version: .version,
		Data:    ,
	})
	if  != nil {
		return 0, errors.Wrap(, "write TLS record")
	}
	return
}

// Read implements io.Reader.
func ( *FakeTLS) ( []byte) ( int,  error) {
	.readBufMux.Lock()
	defer .readBufMux.Unlock()

	if .readBuf.Len() > 0 {
		return .readBuf.Read()
	}

	,  := readRecord(.conn)
	if  != nil {
		return 0, errors.Wrap(, "read TLS record")
	}

	switch .Type {
	case RecordTypeChangeCipherSpec:
	case RecordTypeApplication:
	case RecordTypeHandshake:
		return 0, errors.New("unexpected record type handshake")
	default:
		return 0, errors.Errorf("unsupported record type %v", .Type)
	}
	.readBuf.Write(.Data)

	return .readBuf.Read()
}