package kcp

import (
	"bytes"
	"container/list"
	"encoding/binary"
	"fmt"
	"math/rand"
	"sync"
	"testing"
	"time"
)

func iclock() int32 {
	return int32(currentMs())
}

type DelayPacket struct {
	_ptr  []byte
	_size int
	_ts   int32
}

func (p *DelayPacket) Init(size int, src []byte) {
	p._ptr = make([]byte, size)
	p._size = size
	copy(p._ptr, src[:size])
}

func (p *DelayPacket) ptr() []byte    { return p._ptr }
func (p *DelayPacket) size() int      { return p._size }
func (p *DelayPacket) ts() int32      { return p._ts }
func (p *DelayPacket) setts(ts int32) { p._ts = ts }

type DelayTunnel struct{ *list.List }
type LatencySimulator struct {
	current                        int32
	lostrate, rttmin, rttmax, nmax int
	p12                            DelayTunnel
	p21                            DelayTunnel
	r12                            *rand.Rand
	r21                            *rand.Rand
}

// lostrate: 往返一周丢包率的百分比，默认 10%
// rttmin：rtt最小值，默认 60
// rttmax：rtt最大值，默认 125
//func (p *LatencySimulator)Init(int lostrate = 10, int rttmin = 60, int rttmax = 125, int nmax = 1000):
func (p *LatencySimulator) Init(lostrate, rttmin, rttmax, nmax int) {
	p.r12 = rand.New(rand.NewSource(9))
	p.r21 = rand.New(rand.NewSource(99))
	p.p12 = DelayTunnel{list.New()}
	p.p21 = DelayTunnel{list.New()}
	p.current = iclock()
	p.lostrate = lostrate / 2 // 上面数据是往返丢包率，单程除以2
	p.rttmin = rttmin / 2
	p.rttmax = rttmax / 2
	p.nmax = nmax
}

// 发送数据
// peer - 端点0/1，从0发送，从1接收；从1发送从0接收
func (p *LatencySimulator) send(peer int, data []byte, size int) int {
	rnd := 0
	if peer == 0 {
		rnd = p.r12.Intn(100)
	} else {
		rnd = p.r21.Intn(100)
	}
	//println("!!!!!!!!!!!!!!!!!!!!", rnd, p.lostrate, peer)
	if rnd < p.lostrate {
		return 0
	}
	pkt := &DelayPacket{}
	pkt.Init(size, data)
	p.current = iclock()
	delay := p.rttmin
	if p.rttmax > p.rttmin {
		delay += rand.Int() % (p.rttmax - p.rttmin)
	}
	pkt.setts(p.current + int32(delay))
	if peer == 0 {
		p.p12.PushBack(pkt)
	} else {
		p.p21.PushBack(pkt)
	}
	return 1
}

// 接收数据
func (p *LatencySimulator) recv(peer int, data []byte, maxsize int) int32 {
	var it *list.Element
	if peer == 0 {
		it = p.p21.Front()
		if p.p21.Len() == 0 {
			return -1
		}
	} else {
		it = p.p12.Front()
		if p.p12.Len() == 0 {
			return -1
		}
	}
	pkt := it.Value.(*DelayPacket)
	p.current = iclock()
	if p.current < pkt.ts() {
		return -2
	}
	if maxsize < pkt.size() {
		return -3
	}
	if peer == 0 {
		p.p21.Remove(it)
	} else {
		p.p12.Remove(it)
	}
	maxsize = pkt.size()
	copy(data, pkt.ptr()[:maxsize])
	return int32(maxsize)
}

//=====================================================================
//=====================================================================

// 模拟网络
var vnet *LatencySimulator

// 测试用例
func test(mode int) {
	// 创建模拟网络：丢包率10%，Rtt 60ms~125ms
	vnet = &LatencySimulator{}
	vnet.Init(10, 60, 125, 1000)

	// 创建两个端点的 kcp对象，第一个参数 conv是会话编号，同一个会话需要相同
	// 最后一个是 user参数，用来传递标识
	output1 := func(buf []byte, size int) {
		if vnet.send(0, buf, size) != 1 {
		}
	}
	output2 := func(buf []byte, size int) {
		if vnet.send(1, buf, size) != 1 {
		}
	}
	kcp1 := NewKCP(0x11223344, output1)
	kcp2 := NewKCP(0x11223344, output2)

	current := uint32(iclock())
	slap := current + 20
	index := 0
	next := 0
	var sumrtt uint32
	count := 0
	maxrtt := 0

	// 配置窗口大小：平均延迟200ms，每20ms发送一个包，
	// 而考虑到丢包重发，设置最大收发窗口为128
	kcp1.WndSize(128, 128)
	kcp2.WndSize(128, 128)

	// 判断测试用例的模式
	if mode == 0 {
		// 默认模式
		kcp1.NoDelay(0, 10, 0, 0)
		kcp2.NoDelay(0, 10, 0, 0)
	} else if mode == 1 {
		// 普通模式，关闭流控等
		kcp1.NoDelay(0, 10, 0, 1)
		kcp2.NoDelay(0, 10, 0, 1)
	} else {
		// 启动快速模式
		// 第二个参数 nodelay-启用以后若干常规加速将启动
		// 第三个参数 interval为内部处理时钟，默认设置为 10ms
		// 第四个参数 resend为快速重传指标，设置为2
		// 第五个参数 为是否禁用常规流控，这里禁止
		kcp1.NoDelay(1, 10, 2, 1)
		kcp2.NoDelay(1, 10, 2, 1)
	}

	buffer := make([]byte, 2000)
	var hr int32

	ts1 := iclock()

	for {
		time.Sleep(1 * time.Millisecond)
		current = uint32(iclock())
		kcp1.Update()
		kcp2.Update()

		// 每隔 20ms，kcp1发送数据
		for ; current >= slap; slap += 20 {
			buf := new(bytes.Buffer)
			binary.Write(buf, binary.LittleEndian, uint32(index))
			index++
			binary.Write(buf, binary.LittleEndian, uint32(current))
			// 发送上层协议包
			kcp1.Send(buf.Bytes())
			//println("now", iclock())
		}

		// 处理虚拟网络：检测是否有udp包从p1->p2
		for {
			hr = vnet.recv(1, buffer, 2000)
			if hr < 0 {
				break
			}
			// 如果 p2收到udp，则作为下层协议输入到kcp2
			kcp2.Input(buffer[:hr], true, false)
		}

		// 处理虚拟网络：检测是否有udp包从p2->p1
		for {
			hr = vnet.recv(0, buffer, 2000)
			if hr < 0 {
				break
			}
			// 如果 p1收到udp，则作为下层协议输入到kcp1
			kcp1.Input(buffer[:hr], true, false)
			//println("@@@@", hr, r)
		}

		// kcp2接收到任何包都返回回去
		for {
			hr = int32(kcp2.Recv(buffer[:10]))
			// 没有收到包就退出
			if hr < 0 {
				break
			}
			// 如果收到包就回射
			buf := bytes.NewReader(buffer)
			var sn uint32
			binary.Read(buf, binary.LittleEndian, &sn)
			kcp2.Send(buffer[:hr])
		}

		// kcp1收到kcp2的回射数据
		for {
			hr = int32(kcp1.Recv(buffer[:10]))
			buf := bytes.NewReader(buffer)
			// 没有收到包就退出
			if hr < 0 {
				break
			}
			var sn uint32
			var ts, rtt uint32
			binary.Read(buf, binary.LittleEndian, &sn)
			binary.Read(buf, binary.LittleEndian, &ts)
			rtt = uint32(current) - ts

			if sn != uint32(next) {
				// 如果收到的包不连续
				//for i:=0;i<8 ;i++ {
				//println("---", i, buffer[i])
				//}
				println("ERROR sn ", count, "<->", next, sn)
				return
			}

			next++
			sumrtt += rtt
			count++
			if rtt > uint32(maxrtt) {
				maxrtt = int(rtt)
			}

			//println("[RECV] mode=", mode, " sn=", sn, " rtt=", rtt)
		}

		if next > 100 {
			break
		}
	}

	ts1 = iclock() - ts1

	names := []string{"default", "normal", "fast"}
	fmt.Printf("%s mode result (%dms):\n", names[mode], ts1)
	fmt.Printf("avgrtt=%d maxrtt=%d\n", int(sumrtt/uint32(count)), maxrtt)
}

func TestNetwork(t *testing.T) {
	test(0) // 默认模式，类似 TCP：正常模式，无快速重传，常规流控
	test(1) // 普通模式，关闭流控等
	test(2) // 快速模式，所有开关都打开，且关闭流控
}

func BenchmarkFlush(b *testing.B) {
	kcp := NewKCP(1, func(buf []byte, size int) {})
	kcp.snd_buf = make([]segment, 1024)
	for k := range kcp.snd_buf {
		kcp.snd_buf[k].xmit = 1
		kcp.snd_buf[k].resendts = currentMs() + 10000
	}
	b.ResetTimer()
	b.ReportAllocs()
	var mu sync.Mutex
	for i := 0; i < b.N; i++ {
		mu.Lock()
		kcp.flush(false)
		mu.Unlock()
	}
}
