// This is the entry point for the mlr executable.
package main

import (
	"fmt"
	"os"
	"runtime"
	"runtime/debug"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"

	"github.com/johnkerl/miller/v6/pkg/entrypoint"
	"github.com/pkg/profile" // for trace.out
)

func main() {
	// For mlr --time
	startTime := time.Now()

	// Respect env $GOMAXPROCS, if provided, else set default.
	haveSetGoMaxProcs := false
	goMaxProcsString := os.Getenv("GOMAXPROCS")
	if goMaxProcsString != "" {
		goMaxProcs, err := strconv.Atoi(goMaxProcsString)
		if err != nil {
			runtime.GOMAXPROCS(goMaxProcs)
			haveSetGoMaxProcs = true
		}
	}
	if !haveSetGoMaxProcs {
		// As of Go 1.16 this is the default anyway. For 1.15 and below we need
		// to explicitly set this.
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	debug.SetGCPercent(500) // Empirical: See README-profiling.md

	//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// CPU profiling
	//
	// We do this here, not in the command-line parser, since
	// pprof.StopCPUProfile() needs to be called at the very end of everything.
	// Putting this pprof logic into a go func running in parallel with main,
	// and properly stopping the profile only when main ends via chan-sync,
	// results in a zero-length pprof file.
	//
	// Please see README-profiling.md for more information.

	if len(os.Args) >= 3 && os.Args[1] == "--cpuprofile" {
		profFilename := os.Args[2]
		handle, err := os.Create(profFilename)
		if err != nil {
			fmt.Fprintln(os.Stderr, os.Args[0], ": ", "Could not start CPU profile: ", err)
			return
		}
		defer handle.Close()

		if err := pprof.StartCPUProfile(handle); err != nil {
			fmt.Fprintln(os.Stderr, os.Args[0], ": ", "Could not start CPU profile: ", err)
			return
		}
		defer pprof.StopCPUProfile()

		fmt.Fprintf(os.Stderr, "CPU profile started.\n")
		defer fmt.Fprintf(os.Stderr, "CPU profile finished.\ngo tool pprof -http=:8080 %s\n", profFilename)
	}

	if len(os.Args) >= 2 && os.Args[1] == "--traceprofile" {
		defer profile.Start(profile.TraceProfile, profile.ProfilePath(".")).Stop()
		defer fmt.Fprintf(os.Stderr, "go tool trace trace.out\n")
	}

	//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// This will obtain os.Args and go from there.  All the usual contents of
	// main() are put into this package for ease of testing.
	mainReturn := entrypoint.Main()

	//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Timing
	//
	// The system 'time' command is built-in, of course but it's nice to have
	// simply wall-time without the real/user/sys distinction. Also, making
	// this a Miller built-in is nice for Windows.
	if mainReturn.PrintElapsedTime {
		endTime := time.Now()
		startNanos := startTime.UnixNano()
		endNanos := endTime.UnixNano()
		seconds := float64(endNanos-startNanos) / 1e9
		fmt.Fprintf(os.Stderr, "%.6f", seconds)
		for _, arg := range os.Args {
			if strings.Contains(arg, " ") || strings.Contains(arg, "\t") {
				fmt.Fprintf(os.Stderr, " '%s'", arg)
			} else {
				fmt.Fprintf(os.Stderr, " %s", arg)
			}
		}
		fmt.Fprintf(os.Stderr, "\n")
	}
}
