//go:build !js

package websocket

import (
	
	
	
	
	

	
)

// opcode represents a WebSocket opcode.
type opcode int

// https://tools.ietf.org/html/rfc6455#section-11.8.
const (
	opContinuation opcode = iota
	opText
	opBinary
	// 3 - 7 are reserved for further non-control frames.
	_
	_
	_
	_
	_
	opClose
	opPing
	opPong
	// 11-16 are reserved for further control frames.
)

// header represents a WebSocket frame header.
// See https://tools.ietf.org/html/rfc6455#section-5.2.
type header struct {
	fin    bool
	rsv1   bool
	rsv2   bool
	rsv3   bool
	opcode opcode

	payloadLength int64

	masked  bool
	maskKey uint32
}

// readFrameHeader reads a header from the reader.
// See https://tools.ietf.org/html/rfc6455#section-5.2.
func readFrameHeader( *bufio.Reader,  []byte) ( header,  error) {
	defer errd.Wrap(&, "failed to read frame header")

	,  := .ReadByte()
	if  != nil {
		return header{}, 
	}

	.fin = &(1<<7) != 0
	.rsv1 = &(1<<6) != 0
	.rsv2 = &(1<<5) != 0
	.rsv3 = &(1<<4) != 0

	.opcode = opcode( & 0xf)

	,  = .ReadByte()
	if  != nil {
		return header{}, 
	}

	.masked = &(1<<7) != 0

	 :=  &^ (1 << 7)
	switch {
	case  < 126:
		.payloadLength = int64()
	case  == 126:
		_,  = io.ReadFull(, [:2])
		.payloadLength = int64(binary.BigEndian.Uint16())
	case  == 127:
		_,  = io.ReadFull(, )
		.payloadLength = int64(binary.BigEndian.Uint64())
	}
	if  != nil {
		return header{}, 
	}

	if .payloadLength < 0 {
		return header{}, fmt.Errorf("received negative payload length: %v", .payloadLength)
	}

	if .masked {
		_,  = io.ReadFull(, [:4])
		if  != nil {
			return header{}, 
		}
		.maskKey = binary.LittleEndian.Uint32()
	}

	return , nil
}

// maxControlPayload is the maximum length of a control frame payload.
// See https://tools.ietf.org/html/rfc6455#section-5.5.
const maxControlPayload = 125

// writeFrameHeader writes the bytes of the header to w.
// See https://tools.ietf.org/html/rfc6455#section-5.2
func writeFrameHeader( header,  *bufio.Writer,  []byte) ( error) {
	defer errd.Wrap(&, "failed to write frame header")

	var  byte
	if .fin {
		 |= 1 << 7
	}
	if .rsv1 {
		 |= 1 << 6
	}
	if .rsv2 {
		 |= 1 << 5
	}
	if .rsv3 {
		 |= 1 << 4
	}

	 |= byte(.opcode)

	 = .WriteByte()
	if  != nil {
		return 
	}

	 := byte(0)
	if .masked {
		 |= 1 << 7
	}

	switch {
	case .payloadLength > math.MaxUint16:
		 |= 127
	case .payloadLength > 125:
		 |= 126
	case .payloadLength >= 0:
		 |= byte(.payloadLength)
	}
	 = .WriteByte()
	if  != nil {
		return 
	}

	switch {
	case .payloadLength > math.MaxUint16:
		binary.BigEndian.PutUint64(, uint64(.payloadLength))
		_,  = .Write()
	case .payloadLength > 125:
		binary.BigEndian.PutUint16(, uint16(.payloadLength))
		_,  = .Write([:2])
	}
	if  != nil {
		return 
	}

	if .masked {
		binary.LittleEndian.PutUint32(, .maskKey)
		_,  = .Write([:4])
		if  != nil {
			return 
		}
	}

	return nil
}