// Copyright 2016 Mikio Hara. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tcpinfo

import (
	
	
	
	

	
)

var options = [soMax]option{
	soInfo:   {ianaProtocolTCP, sysTCP_INFO, parseInfo},
	soCCInfo: {ianaProtocolTCP, sysTCP_CC_INFO, parseCCInfo},
	soCCAlgo: {ianaProtocolTCP, sysTCP_CONGESTION, parseCCAlgorithm},
}

// Marshal implements the Marshal method of tcpopt.Option interface.
func ( *Info) () ([]byte, error) { return (*[sizeofTCPInfo]byte)(unsafe.Pointer())[:], nil }

// A CAState represents a state of congestion avoidance.
type CAState int

var caStates = map[CAState]string{
	CAOpen:     "open",
	CADisorder: "disorder",
	CACWR:      "congestion window reduced",
	CARecovery: "recovery",
	CALoss:     "loss",
}

func ( CAState) () string {
	,  := caStates[]
	if ! {
		return "<nil>"
	}
	return 
}

// A SysInfo represents platform-specific information.
type SysInfo struct {
	PathMTU                 uint          `json:"path_mtu"`           // path maximum transmission unit
	AdvertisedMSS           MaxSegSize    `json:"adv_mss"`            // advertised maximum segment size
	CAState                 CAState       `json:"ca_state"`           // state of congestion avoidance
	Retransmissions         uint          `json:"rexmits"`            // # of retranmissions on timeout invoked
	Backoffs                uint          `json:"backoffs"`           // # of times retransmission backoff timer invoked
	WindowOrKeepAliveProbes uint          `json:"wnd_ka_probes"`      // # of window or keep alive probes sent
	UnackedSegs             uint          `json:"unacked_segs"`       // # of unack'd segments
	SackedSegs              uint          `json:"sacked_segs"`        // # of sack'd segments
	LostSegs                uint          `json:"lost_segs"`          // # of lost segments
	RetransSegs             uint          `json:"retrans_segs"`       // # of retransmitting segments in transmission queue
	ForwardAckSegs          uint          `json:"fack_segs"`          // # of forward ack segments in transmission queue
	ReorderedSegs           uint          `json:"reord_segs"`         // # of reordered segments allowed
	ReceiverRTT             time.Duration `json:"rcv_rtt"`            // current RTT for receiver
	TotalRetransSegs        uint          `json:"total_retrans_segs"` // # of retransmitted segments
	PacingRate              uint64        `json:"pacing_rate"`        // pacing rate
	ThruBytesAcked          uint64        `json:"thru_bytes_acked"`   // # of bytes for which cumulative acknowledgments have been received
	ThruBytesReceived       uint64        `json:"thru_bytes_rcvd"`    // # of bytes for which cumulative acknowledgments have been sent
	SegsOut                 uint          `json:"segs_out"`           // # of segments sent
	SegsIn                  uint          `json:"segs_in"`            // # of segments received
	NotSentBytes            uint          `json:"not_sent_bytes"`     // # of bytes not sent yet
	MinRTT                  time.Duration `json:"min_rtt"`            // current measured minimum RTT; zero means not available
	DataSegsOut             uint          `json:"data_segs_out"`      // # of segments sent containing a positive length data segment
	DataSegsIn              uint          `json:"data_segs_in"`       // # of segments received containing a positive length data segment
}

var sysStates = [12]State{Unknown, Established, SynSent, SynReceived, FinWait1, FinWait2, TimeWait, Closed, CloseWait, LastAck, Listen, Closing}

const (
	sizeofTCPInfoV4_9    = 0xa0
	sizeofTCPInfoV3_19   = 0x78
	sizeofTCPInfoV2_6_10 = 0x57
)

func parseInfo( []byte) (tcpopt.Option, error) {
	if len() < sizeofTCPInfoV4_9 {
		return parseInfo3_19()
	}
	 := (*tcpInfo)(unsafe.Pointer(&[0]))
	 := &Info{State: sysStates[.State]}
	if .Options&sysTCPI_OPT_WSCALE != 0 {
		.Options = append(.Options, WindowScale(.Pad_cgo_0[0]>>4))
		.PeerOptions = append(.PeerOptions, WindowScale(.Pad_cgo_0[0]&0x0f))
	}
	if .Options&sysTCPI_OPT_SACK != 0 {
		.Options = append(.Options, SACKPermitted(true))
		.PeerOptions = append(.PeerOptions, SACKPermitted(true))
	}
	if .Options&sysTCPI_OPT_TIMESTAMPS != 0 {
		.Options = append(.Options, Timestamps(true))
		.PeerOptions = append(.PeerOptions, Timestamps(true))
	}
	.SenderMSS = MaxSegSize(.Snd_mss)
	.ReceiverMSS = MaxSegSize(.Rcv_mss)
	.RTT = time.Duration(.Rtt) * time.Microsecond
	.RTTVar = time.Duration(.Rttvar) * time.Microsecond
	.RTO = time.Duration(.Rto) * time.Microsecond
	.ATO = time.Duration(.Ato) * time.Microsecond
	.LastDataSent = time.Duration(.Last_data_sent) * time.Millisecond
	.LastDataReceived = time.Duration(.Last_data_recv) * time.Millisecond
	.LastAckReceived = time.Duration(.Last_ack_recv) * time.Millisecond
	.FlowControl = &FlowControl{
		ReceiverWindow: uint(.Rcv_space),
	}
	.CongestionControl = &CongestionControl{
		SenderSSThreshold:   uint(.Snd_ssthresh),
		ReceiverSSThreshold: uint(.Rcv_ssthresh),
		SenderWindowSegs:    uint(.Snd_cwnd),
	}
	.Sys = &SysInfo{
		PathMTU:                 uint(.Pmtu),
		AdvertisedMSS:           MaxSegSize(.Advmss),
		CAState:                 CAState(.Ca_state),
		Retransmissions:         uint(.Retransmits),
		Backoffs:                uint(.Backoff),
		WindowOrKeepAliveProbes: uint(.Probes),
		UnackedSegs:             uint(.Unacked),
		SackedSegs:              uint(.Sacked),
		LostSegs:                uint(.Lost),
		RetransSegs:             uint(.Retrans),
		ForwardAckSegs:          uint(.Fackets),
		ReorderedSegs:           uint(.Reordering),
		ReceiverRTT:             time.Duration(.Rcv_rtt) * time.Microsecond,
		TotalRetransSegs:        uint(.Total_retrans),
		PacingRate:              uint64(.Pacing_rate),
		ThruBytesAcked:          uint64(.Bytes_acked),
		ThruBytesReceived:       uint64(.Bytes_received),
		SegsIn:                  uint(.Segs_in),
		SegsOut:                 uint(.Segs_out),
		NotSentBytes:            uint(.Notsent_bytes),
		MinRTT:                  time.Duration(.Min_rtt) * time.Microsecond,
		DataSegsIn:              uint(.Data_segs_in),
		DataSegsOut:             uint(.Data_segs_out),
	}
	return , nil
}

func parseInfo3_19( []byte) (tcpopt.Option, error) {
	if len() < sizeofTCPInfoV3_19 {
		return parseInfo2_6_10()
	}
	 := (*tcpInfo3_19)(unsafe.Pointer(&[0]))
	 := &Info{State: sysStates[.State]}
	if .Options&sysTCPI_OPT_WSCALE != 0 {
		.Options = append(.Options, WindowScale(.Pad_cgo_0[0]>>4))
		.PeerOptions = append(.PeerOptions, WindowScale(.Pad_cgo_0[0]&0x0f))
	}
	if .Options&sysTCPI_OPT_SACK != 0 {
		.Options = append(.Options, SACKPermitted(true))
		.PeerOptions = append(.PeerOptions, SACKPermitted(true))
	}
	if .Options&sysTCPI_OPT_TIMESTAMPS != 0 {
		.Options = append(.Options, Timestamps(true))
		.PeerOptions = append(.PeerOptions, Timestamps(true))
	}
	.SenderMSS = MaxSegSize(.Snd_mss)
	.ReceiverMSS = MaxSegSize(.Rcv_mss)
	.RTT = time.Duration(.Rtt) * time.Microsecond
	.RTTVar = time.Duration(.Rttvar) * time.Microsecond
	.RTO = time.Duration(.Rto) * time.Microsecond
	.ATO = time.Duration(.Ato) * time.Microsecond
	.LastDataSent = time.Duration(.Last_data_sent) * time.Millisecond
	.LastDataReceived = time.Duration(.Last_data_recv) * time.Millisecond
	.LastAckReceived = time.Duration(.Last_ack_recv) * time.Millisecond
	.FlowControl = &FlowControl{
		ReceiverWindow: uint(.Rcv_space),
	}
	.CongestionControl = &CongestionControl{
		SenderSSThreshold:   uint(.Snd_ssthresh),
		ReceiverSSThreshold: uint(.Rcv_ssthresh),
		SenderWindowSegs:    uint(.Snd_cwnd),
	}
	.Sys = &SysInfo{
		PathMTU:                 uint(.Pmtu),
		AdvertisedMSS:           MaxSegSize(.Advmss),
		CAState:                 CAState(.Ca_state),
		Retransmissions:         uint(.Retransmits),
		Backoffs:                uint(.Backoff),
		WindowOrKeepAliveProbes: uint(.Probes),
		UnackedSegs:             uint(.Unacked),
		SackedSegs:              uint(.Sacked),
		LostSegs:                uint(.Lost),
		RetransSegs:             uint(.Retrans),
		ForwardAckSegs:          uint(.Fackets),
		ReorderedSegs:           uint(.Reordering),
		ReceiverRTT:             time.Duration(.Rcv_rtt) * time.Microsecond,
		TotalRetransSegs:        uint(.Total_retrans),
		PacingRate:              uint64(.Pacing_rate),
	}
	return , nil
}

func parseInfo2_6_10( []byte) (tcpopt.Option, error) {
	if len() < sizeofTCPInfoV2_6_10 {
		return nil, errors.New("short buffer")
	}
	 := (*tcpInfo2_6_10)(unsafe.Pointer(&[0]))
	 := &Info{State: sysStates[.State]}
	if .Options&sysTCPI_OPT_WSCALE != 0 {
		.Options = append(.Options, WindowScale(.Pad_cgo_0[0]>>4))
		.PeerOptions = append(.PeerOptions, WindowScale(.Pad_cgo_0[0]&0x0f))
	}
	if .Options&sysTCPI_OPT_SACK != 0 {
		.Options = append(.Options, SACKPermitted(true))
		.PeerOptions = append(.PeerOptions, SACKPermitted(true))
	}
	if .Options&sysTCPI_OPT_TIMESTAMPS != 0 {
		.Options = append(.Options, Timestamps(true))
		.PeerOptions = append(.PeerOptions, Timestamps(true))
	}
	.SenderMSS = MaxSegSize(.Snd_mss)
	.ReceiverMSS = MaxSegSize(.Rcv_mss)
	.RTT = time.Duration(.Rtt) * time.Microsecond
	.RTTVar = time.Duration(.Rttvar) * time.Microsecond
	.RTO = time.Duration(.Rto) * time.Microsecond
	.ATO = time.Duration(.Ato) * time.Microsecond
	.LastDataSent = time.Duration(.Last_data_sent) * time.Millisecond
	.LastDataReceived = time.Duration(.Last_data_recv) * time.Millisecond
	.LastAckReceived = time.Duration(.Last_ack_recv) * time.Millisecond
	.FlowControl = &FlowControl{
		ReceiverWindow: uint(.Rcv_space),
	}
	.CongestionControl = &CongestionControl{
		SenderSSThreshold:   uint(.Snd_ssthresh),
		ReceiverSSThreshold: uint(.Rcv_ssthresh),
		SenderWindowSegs:    uint(.Snd_cwnd),
	}
	.Sys = &SysInfo{
		PathMTU:                 uint(.Pmtu),
		AdvertisedMSS:           MaxSegSize(.Advmss),
		CAState:                 CAState(.Ca_state),
		Retransmissions:         uint(.Retransmits),
		Backoffs:                uint(.Backoff),
		WindowOrKeepAliveProbes: uint(.Probes),
		UnackedSegs:             uint(.Unacked),
		SackedSegs:              uint(.Sacked),
		LostSegs:                uint(.Lost),
		RetransSegs:             uint(.Retrans),
		ForwardAckSegs:          uint(.Fackets),
		ReorderedSegs:           uint(.Reordering),
		ReceiverRTT:             time.Duration(.Rcv_rtt) * time.Microsecond,
		TotalRetransSegs:        uint(.Total_retrans),
	}
	return , nil
}

// A VegasInfo represents Vegas congestion control information.
type VegasInfo struct {
	Enabled    bool          `json:"enabled"`
	RoundTrips uint          `json:"rnd_trips"` // # of round-trips
	RTT        time.Duration `json:"rtt"`       // round-trip time
	MinRTT     time.Duration `json:"min_rtt"`   // minimum round-trip time
}

// Algorithm implements the Algorithm method of CCAlgorithmInfo
// interface.
func ( *VegasInfo) () string { return "vegas" }

// A CEState represents a state of ECN congestion encountered (CE)
// codepoint.
type CEState int

// A DCTCPInfo represents Datacenter TCP congestion control
// information.
type DCTCPInfo struct {
	Enabled         bool    `json:"enabled"`
	CEState         CEState `json:"ce_state"`    // state of ECN CE codepoint
	Alpha           uint    `json:"alpha"`       // fraction of bytes sent
	ECNAckedBytes   uint    `json:"ecn_acked"`   // # of acked bytes with ECN
	TotalAckedBytes uint    `json:"total_acked"` // total # of acked bytes
}

// Algorithm implements the Algorithm method of CCAlgorithmInfo
// interface.
func ( *DCTCPInfo) () string { return "dctcp" }

// A BBRInfo represents Bottleneck Bandwidth and Round-trip
// propagation time-based congestion control information.
type BBRInfo struct {
	MaxBW          uint64        `json:"max_bw"`      // maximum-filtered bandwidth in bps
	MinRTT         time.Duration `json:"min_rtt"`     // minimum-filtered round-trip time
	PacingGain     uint          `json:"pacing_gain"` // pacing gain shifted left 8 bits
	CongWindowGain uint          `json:"cwnd_gain"`   // congestion window gain shifted left 8 bits
}

// Algorithm implements the Algorithm method of CCAlgorithmInfo
// interface.
func ( *BBRInfo) () string { return "bbr" }

func parseCCAlgorithmInfo( string,  []byte) (CCAlgorithmInfo, error) {
	if strings.HasPrefix(, "dctcp") {
		if len() < sizeofTCPDCTCPInfo {
			return nil, errors.New("short buffer")
		}
		 := (*tcpDCTCPInfo)(unsafe.Pointer(&[0]))
		 := &DCTCPInfo{Alpha: uint(.Alpha)}
		if .Enabled != 0 {
			.Enabled = true
		}
		return , nil
	}
	if strings.HasPrefix(, "bbr") {
		if len() < sizeofTCPBBRInfo {
			return nil, errors.New("short buffer")
		}
		 := (*tcpBBRInfo)(unsafe.Pointer(&[0]))
		return &BBRInfo{
			MaxBW:          uint64(.Bw_hi)<<32 | uint64(.Bw_lo),
			MinRTT:         time.Duration(.Min_rtt) * time.Microsecond,
			PacingGain:     uint(.Pacing_gain),
			CongWindowGain: uint(.Cwnd_gain),
		}, nil
	}
	if len() < sizeofTCPVegasInfo {
		return nil, errors.New("short buffer")
	}
	 := (*tcpVegasInfo)(unsafe.Pointer(&[0]))
	 := &VegasInfo{
		RoundTrips: uint(.Rttcnt),
		RTT:        time.Duration(.Rtt) * time.Microsecond,
		MinRTT:     time.Duration(.Minrtt) * time.Microsecond,
	}
	if .Enabled != 0 {
		.Enabled = true
	}
	return , nil
}