package main

import (
	"flag"
	"fmt"
	"log"
	"regexp"
	"sort"
	"time"

	"code.justin.tv/rhys/nursery/internal/_vendor/github.com/aclements/go-perf/perffile"
)

var (
	libRx = regexp.MustCompile(`([.]so$|[.]so[._][0-9]+)`)

	pidObjects = make(map[int]map[string]struct{})
)

func main() {
	var (
		infile = flag.String("input", "", "Name of input file")
		tick   = flag.Duration("tick", 10*time.Millisecond, "Size of time buckets")
		start  = flag.Int64("start", 0, "Start timestamp (in perf's time units)")
		end    = flag.Int64("end", 0, "End timestamp, exclusive (in perf's time units)")
	)
	flag.Parse()

	perf, err := perffile.Open(*infile)
	if err != nil {
		log.Fatalf("could not open input file %q: %v", *infile, err)
	}

	rs := perf.Records(perffile.RecordsFileOrder)

	tickBuckets := make(map[int]int64)

	pidSamples := make(map[int]int64)
	for rs.Next() {
		inRange := true
		if *start != 0 && rs.Record.Common().Time < uint64(*start) {
			inRange = false
		}
		if *end != 0 && rs.Record.Common().Time >= uint64(*end) {
			inRange = false
		}
		switch r := rs.Record.(type) {
		case *perffile.RecordSample:
			if !inRange {
				continue
			}
			tickBuckets[int(r.Time/uint64(*tick))]++
			pidSamples[r.PID]++
		case *perffile.RecordMmap:
			objs, ok := pidObjects[r.PID]
			if !ok {
				objs = make(map[string]struct{})
				pidObjects[r.PID] = objs
			}
			objs[r.Filename] = struct{}{}
		case *perffile.RecordFork:
			objs, ok := pidObjects[r.PID]
			if !ok {
				objs = make(map[string]struct{})
				pidObjects[r.PID] = objs
				for obj := range pidObjects[r.PPID] {
					objs[obj] = struct{}{}
				}
			}
		}
	}

	var pids []int
	for pid := range pidSamples {
		pids = append(pids, pid)
	}
	sort.Ints(pids)

	objSetSamples := make(map[string]int64)
	for _, pid := range pids {
		var objs []string
		for obj := range pidObjects[pid] {
			if libRx.MatchString(obj) {
				continue
			}
			objs = append(objs, obj)
		}
		sort.Strings(objs)
		objSetSamples[fmt.Sprintf("%q", objs)] += pidSamples[pid]
		log.Printf("pid=%d samples=%d objects=%q", pid, pidSamples[pid], objs)
	}

	var objSets []string
	for objSet := range objSetSamples {
		objSets = append(objSets, objSet)
	}
	sort.Strings(objSets)
	for _, objSet := range objSets {
		log.Printf("samples=%d objects=%s", objSetSamples[objSet], objSet)
	}

	var ticksPresent []int
	for bucket := range tickBuckets {
		ticksPresent = append(ticksPresent, bucket)
	}
	sort.Ints(ticksPresent)
	minBucket, maxBucket := 0, 0
	for _, bucket := range ticksPresent {
		if bucket > 0 && minBucket == 0 {
			minBucket = bucket
		}
		if minBucket > 0 {
			maxBucket = bucket
		}
	}
	for bucket := minBucket; bucket <= maxBucket; bucket++ {
		fmt.Printf("start=%d samples=%d\n", uint64(bucket)*uint64(*tick), tickBuckets[bucket])
	}
}
