// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package websocket

import (
	
	
	
	
)

// PreparedMessage caches on the wire representations of a message payload.
// Use PreparedMessage to efficiently send a message payload to multiple
// connections. PreparedMessage is especially useful when compression is used
// because the CPU and memory expensive compression operation can be executed
// once for a given set of compression options.
type PreparedMessage struct {
	messageType int
	data        []byte
	mu          sync.Mutex
	frames      map[prepareKey]*preparedFrame
}

// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.
type prepareKey struct {
	isServer         bool
	compress         bool
	compressionLevel int
}

// preparedFrame contains data in wire representation.
type preparedFrame struct {
	once sync.Once
	data []byte
}

// NewPreparedMessage returns an initialized PreparedMessage. You can then send
// it to connection using WritePreparedMessage method. Valid wire
// representation will be calculated lazily only once for a set of current
// connection options.
func ( int,  []byte) (*PreparedMessage, error) {
	 := &PreparedMessage{
		messageType: ,
		frames:      make(map[prepareKey]*preparedFrame),
		data:        ,
	}

	// Prepare a plain server frame.
	, ,  := .frame(prepareKey{isServer: true, compress: false})
	if  != nil {
		return nil, 
	}

	// To protect against caller modifying the data argument, remember the data
	// copied to the plain server frame.
	.data = [len()-len():]
	return , nil
}

func ( *PreparedMessage) ( prepareKey) (int, []byte, error) {
	.mu.Lock()
	,  := .frames[]
	if ! {
		 = &preparedFrame{}
		.frames[] = 
	}
	.mu.Unlock()

	var  error
	.once.Do(func() {
		// Prepare a frame using a 'fake' connection.
		// TODO: Refactor code in conn.go to allow more direct construction of
		// the frame.
		 := make(chan struct{}, 1)
		 <- struct{}{}
		var  prepareConn
		 := &Conn{
			conn:                   &,
			mu:                     ,
			isServer:               .isServer,
			compressionLevel:       .compressionLevel,
			enableWriteCompression: true,
			writeBuf:               make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),
		}
		if .compress {
			.newCompressionWriter = compressNoContextTakeover
		}
		 = .WriteMessage(.messageType, .data)
		.data = .buf.Bytes()
	})
	return .messageType, .data, 
}

type prepareConn struct {
	buf bytes.Buffer
	net.Conn
}

func ( *prepareConn) ( []byte) (int, error)        { return .buf.Write() }
func ( *prepareConn) ( time.Time) error { return nil }