// 29 april 2021
// If I get time, it would be fun to do the second uebung in go.
// For the sake of speed, we can do all the numbers at once, put
// them in arrays, rather than doing i/o on every step.

package main

import (
	"errors"
	"flag"
	"fmt"
	"math/rand"
	"os"
	"strconv"

	"gonum.org/v1/plot"
	"gonum.org/v1/plot/vg"
)

const (
	exitSuccess = iota
	exitFailure
)

// usage prints out anything the caller gives us then bails.
func usage(e string) {
	u := "usage: pi seed nsteps nstep_nprint"
	fmt.Fprintln(os.Stderr, e+"\n"+u)
	os.Exit(exitFailure)
}

// getnums does the work of filling out the arrays of results
func getnums(cmdArgs cmdArgs) {
	rand.Seed(cmdArgs.seed)
	inCnt := make([]uint32, cmdArgs.nstep)
	{ // The braces limit the lifetime of inPoint, to save a bit of memory
		inPoint := make([]bool, cmdArgs.nstep)
		for i := 0; i < cmdArgs.nstep; i++ { // Fill out the array with true/false
			x := rand.Float32()
			y := rand.Float32()
			if (x*x + y*y) <= 1 {
				inPoint[i] = true
			}
		}
		if inPoint[0] {
			inCnt[0] = 1
		}
		for i := 1; i < cmdArgs.nstep; i++ {
			inCnt[i] = inCnt[i-1]
			if inPoint[i] {
				inCnt[i]++
			}
		}
	}
	for i := 0; i < cmdArgs.nstep; i = i + cmdArgs.nstepPrint {
		pi := 4 * float32(inCnt[i]) / float32(i+1)
		fmt.Println(i, inCnt[i], pi)
	}
}

// doplot makes a primitive plot without any interesting options
func doplot(plotName string) error {
	p := plot.New()
	p.Title.Text = "ugly plot"
	if err := p.Save(15*vg.Centimeter, 10*vg.Centimeter, plotName); err != nil {
		return err
	}
	return nil
}

type cmdArgs struct {
	seed              int64
	nstep, nstepPrint int
	plotName          string
	doStdout          bool // don't print boring table
}

// cmdline gets our command line arguments and maybe a flag or two.
func cmdline(cmdArgs *cmdArgs) error {
	var err error
	var suppress bool
	flag.StringVar(&cmdArgs.plotName, "p", "", "Plot filename")
	flag.BoolVar(&suppress, "s", false, "Suppress stdout")
	flag.Parse()
	if flag.NArg() != 3 {
		return errors.New("Wrong number of command line args")
	}
	a := flag.Args() // not necessary, but makes error messages cleaner
	if cmdArgs.seed, err = strconv.ParseInt(a[0], 10, 64); err != nil {
		return errors.New("Could not convert first arg " + a[1] + " to int")
	}
	if cmdArgs.nstep, err = strconv.Atoi(a[1]); err != nil {
		return errors.New("Could not convert second arg " + a[2] + " to int")
	}
	if cmdArgs.nstepPrint, err = strconv.Atoi(a[2]); err != nil {
		return errors.New("Could not convert third arg " + a[3] + " to int")
	}
	cmdArgs.doStdout = true
	if suppress {
		cmdArgs.doStdout = false
	}
	return nil
}

// main. The rules say ./pi seed nsteps nstep_print
func main() {
	var cmdArgs cmdArgs
	if err := cmdline(&cmdArgs); err != nil {
		usage(err.Error())
	}
	fmt.Println("doStdout is", cmdArgs.doStdout)
	getnums(cmdArgs)
	if err := doplot(cmdArgs.plotName); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(exitFailure)
	}
}