package main

import (
	"fmt"
	"math/rand"
	"runtime"

	"screenconnect-go/pkg/codec"
	"screenconnect-go/pkg/messages"
	"screenconnect-go/pkg/serializer"
)

func try(name string, f func()) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("%s: PANIC: %v\n", name, r)
		}
	}()
	f()
}

func main() {
	// Case 4 first: StreamEncoder unbounded buffer growth over a session.
	{
		enc, err := codec.NewStreamEncoder()
		if err != nil {
			panic(err)
		}
		frame := make([]byte, 1920*1080*3)
		rng := rand.New(rand.NewSource(1))
		for i := range frame {
			frame[i] = byte(rng.Intn(256))
		}
		var before, after runtime.MemStats
		runtime.GC()
		runtime.ReadMemStats(&before)
		var sentBytes int
		for i := 0; i < 30; i++ {
			out, err := enc.Encode(frame)
			if err != nil {
				panic(err)
			}
			sentBytes += len(out)
		}
		runtime.GC()
		runtime.ReadMemStats(&after)
		fmt.Printf("StreamEncoder: 30 frames (10s of 3fps 1080p) sent=%d bytes, heap grew %d MB; growth continues linearly for session lifetime\n",
			sentBytes, (after.HeapAlloc-before.HeapAlloc)/(1024*1024))
	}

	// Case 1: ReadString with hostile varint length (int(n) goes negative).
	try("ReadString(hostile varint 0xFFFFFFFFFFFFFFF0)", func() {
		buf := serializer.AppendVarint(nil, 0xFFFFFFFFFFFFFFF0)
		d := serializer.NewDecoder(buf)
		s, err := d.ReadString()
		fmt.Printf("ReadString: no panic: %q err=%v\n", s, err)
	})

	// Case 3: readSlice hostile count via SessionInfoMessage wire bytes.
	try("SessionInfoMessage(hostile connections count 2^40)", func() {
		e := serializer.NewEncoder()
		e.WriteU8(byte(messages.IDSessionInfoMessage))
		e.WriteLong(0)  // Requests
		e.WriteInt(0)  // SharePrinterMode
		e.WriteInt(0)  // AnnotationMode
		e.WriteInt(14) // ScreenCodecID
		e.WriteInt(0)  // AlternateScreenCodecID
		e.WriteUUID(serializer.UUID{})
		e.WriteLong(1 << 40) // Connections count -> make([]T, 0, 2^40)
		d := serializer.NewDecoder(e.Bytes())
		m, err := messages.ReadMessage(d)
		fmt.Printf("SessionInfoMessage: no panic: %T err=%v\n", m, err)
	})

	// Case 2: huge but positive length within int64 -> take() behavior.
	try("ReadString(huge length 2^40)", func() {
		buf := serializer.AppendVarint(nil, 1<<40)
		d := serializer.NewDecoder(buf)
		s, err := d.ReadString()
		fmt.Printf("ReadString(2^40): no panic: len=%d err=%v\n", len(s), err)
	})
}
