package main

import (
	"context"
	"crypto/tls"
	"encoding/binary"
	"flag"
	"fmt"
	"log"
	"strings"
	"time"

	quic "github.com/AeonDave/mp-quic-go"
	"mpquic-exp/client/scheduler"
)

func main() {
	schedName := flag.String("scheduler", "roundrobin", "Scheduler to use")
	listScheds := flag.Bool("list-schedulers", false, "List available schedulers")
	addr := flag.String("addr", "127.0.0.1:4242", "Server address")
	duration := flag.Int("duration", 10, "Duration in seconds")
	messageSize := flag.Int("message-size", 1024, "Size of the full application message in bytes (min 36)")
	chunkSize := flag.Int("chunk-size", 0, "Chunk size. If > 0, splits message-size into multiple chunks (min 36)")
	flag.Parse()

	if *listScheds {
		fmt.Println("Available schedulers:")
		for _, name := range scheduler.List() {
			fmt.Println(" -", name)
		}
		return
	}

	if *messageSize < 36 {
		*messageSize = 36 // 4 len + 8 seq + 8 ts + 8 chunk_idx + 8 tot_chunks
	}

	tlsConf := &tls.Config{
		InsecureSkipVerify: true,
		NextProtos:         []string{"mpquic-exp"},
	}

	sched, err := scheduler.Get(*schedName)
	if err != nil {
		log.Fatalf("Error: %v. Available: %s", err, strings.Join(scheduler.List(), ", "))
	}

	quicConfig := &quic.Config{
		MaxPaths:            4,
		MultipathController: quic.NewDefaultMultipathController(sched),
	}

	fmt.Printf("Connecting to %s using %s scheduler...\n", *addr, *schedName)
	conn, err := quic.DialAddr(context.Background(), *addr, tlsConf, quicConfig)
	if err != nil {
		log.Fatal(err)
	}

	stream, err := conn.OpenStreamSync(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Stream opened, sending data for %d seconds (Message Size: %d bytes, Chunk Size: %d)...\n", *duration, *messageSize, *chunkSize)

	end := time.Now().Add(time.Duration(*duration) * time.Second)
	
	var msgId uint64 = 0
	totalBytes := 0
	totalChunksSent := 0
	
	actualChunkSize := *messageSize
	if *chunkSize > 36 && *chunkSize <= *messageSize {
		actualChunkSize = *chunkSize
	}

	totalChunks := uint64((*messageSize + actualChunkSize - 1) / actualChunkSize)
	
	for time.Now().Before(end) {
		msgId++
		sendTime := uint64(time.Now().UnixNano())
		
		bytesLeft := *messageSize
		
		for chunkIdx := uint64(0); chunkIdx < totalChunks; chunkIdx++ {
			currentLength := actualChunkSize
			if bytesLeft < actualChunkSize {
				currentLength = bytesLeft
			}
			if currentLength < 36 {
				currentLength = 36
			}
			
			payload := make([]byte, currentLength)
			binary.BigEndian.PutUint32(payload[0:4], uint32(currentLength))
			binary.BigEndian.PutUint64(payload[4:12], msgId)
			binary.BigEndian.PutUint64(payload[12:20], sendTime)
			binary.BigEndian.PutUint64(payload[20:28], chunkIdx)
			binary.BigEndian.PutUint64(payload[28:36], totalChunks)

			n, err := stream.Write(payload)
			if err != nil {
				log.Fatal("Stream write error:", err)
			}
			totalBytes += n
			bytesLeft -= currentLength
			totalChunksSent++
		}
	}
	
	fmt.Printf("Finished sending %d messages (%d chunks), %d bytes (%.2f MB)\n", msgId, totalChunksSent, totalBytes, float64(totalBytes)/1024/1024)
}
