package main

import (
	"flag"
	"io/ioutil"
	"log"
	"math"
	"sort"

	"code.justin.tv/release/trace/pbmsg"
	"github.com/golang/protobuf/proto"
)

func main() {
	dataname := "/dev/stdin"
	flag.StringVar(&dataname, "file", dataname, "Name of file containing EventSet")
	flag.Parse()

	buf, err := ioutil.ReadFile(dataname)
	if err != nil {
		log.Fatalf("read err=%q", err)
	}

	var es pbmsg.EventSet

	err = proto.Unmarshal(buf, &es)
	if err != nil {
		log.Fatalf("unmarshal err=%q", err)
	}

	pathlen := make(map[int]int)
	kinds := make(map[int]int)
	services := make(map[string]int)
	events := make(map[[2]uint64][]*pbmsg.Event)
	for _, ev := range es.Event {
		pathlen[len(ev.Path)]++
		kinds[int(ev.GetKind())]++
		services[ev.GetSvcname()]++

		if txid := [2]uint64{}; copy(txid[:], ev.TransactionId) != 0 {
			events[txid] = append(events[txid], ev)
		}
	}

	eventCount := make(map[int]int)
	duration := make(map[int]int)
	outDuration := make(map[int]int)
	for _, evs := range events {
		eventCount[len(evs)]++

		var start, end int64
		var outStart, outEnd int64
		for _, ev := range evs {
			switch ev.GetKind() {
			case pbmsg.Event_REQUEST_HEAD_RECEIVED:
				start = ev.GetTime()
			case pbmsg.Event_RESPONSE_HEAD_PREPARED:
				end = ev.GetTime()
			case pbmsg.Event_REQUEST_HEAD_PREPARED:
				outStart = ev.GetTime()
			case pbmsg.Event_RESPONSE_HEAD_RECEIVED:
				outEnd = ev.GetTime()
			}
		}
		if start != 0 && end != 0 {
			delta := end - start
			rounded := int(math.Exp2(math.Floor(math.Log2(float64(delta))*10) / 10))
			duration[int(rounded)]++
		}
		if outStart != 0 && outEnd != 0 {
			delta := outEnd - outStart
			rounded := int(math.Exp2(math.Floor(math.Log2(float64(delta))*10) / 10))
			outDuration[int(rounded)]++
		}
	}

	svcNames := make([]string, 0, len(services))
	for name := range services {
		svcNames = append(svcNames, name)
	}
	sort.Strings(svcNames)
	for _, name := range svcNames {
		log.Printf("service=%q count=%d", name, services[name])
	}

	for _, pair := range sortedInts(pathlen) {
		log.Printf("pathlen=%d count=%d", pair[0], pair[1])
	}
	for _, pair := range sortedInts(kinds) {
		log.Printf("kind=%s count=%d", pbmsg.Event_Kind_name[int32(pair[0])], pair[1])
	}
	for _, pair := range sortedInts(eventCount) {
		log.Printf("event-count=%d count=%d", pair[0], pair[1])
	}
	for _, pair := range sortedInts(duration) {
		log.Printf("duration=%-10d count=%d", pair[0], pair[1])
	}
	for _, pair := range sortedInts(outDuration) {
		log.Printf("out-duration=%-10d count=%d", pair[0], pair[1])
	}

	for _, evs := range events {
		if len(evs) == 6 {
			for _, ev := range evs {
				log.Printf("hostname=%q service=%q kind=%q", ev.GetHostname(), ev.GetSvcname(), ev.GetKind())
			}
			break
		}
	}
}

func sortedInts(in map[int]int) [][2]int {
	keys := make([]int, 0, len(in))
	for k := range in {
		keys = append(keys, k)
	}
	sort.Ints(keys)
	out := make([][2]int, 0, len(in))
	for _, k := range keys {
		out = append(out, [2]int{k, in[k]})
	}
	return out
}
