package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

// runGuided is what happens when somebody double-clicks the program instead of
// typing its name at a prompt.
//
// Without this, Explorer opens a console, main() finds no arguments, prints
// the usage text to stderr and exits — and Windows destroys the window in the
// same instant. From the other side of the screen that is indistinguishable
// from a crash. So when we know we were double-clicked, we ask the one
// question the program actually needs and stay on screen until the reader is
// done.
//
// This path is entered ONLY when there are no arguments and both ends of the
// program are a real console. Any scripted or piped use takes exactly the same
// code path it always did.
//
// A benchmark has to write something to measure a write, so this is the one
// guided flow in the range that is not purely read-only. What it writes is one
// temporary file of its own, inside the folder the reader names, and it
// removes that file again on every path including errors. It never reads,
// renames, overwrites or deletes anything that was already there. The guided
// run also uses a smaller test file than the command line does, so nobody
// double-clicks this and waits five minutes for a quarter of a gigabyte.
func runGuided() {
	in := bufio.NewScanner(os.Stdin)

	fmt.Println()
	fmt.Println("  DiskOps")
	fmt.Println("  Find out how fast a drive really is.")
	fmt.Println()
	fmt.Println("  Pick a folder and DiskOps writes one temporary test file into it,")
	fmt.Println("  times how long that takes, reads it all back, times that too, and")
	fmt.Println("  tells you the speed of each in megabytes per second. Point it at a")
	fmt.Println("  folder on a USB stick, an external drive or your main disk to see")
	fmt.Println("  the difference.")
	fmt.Println()
	fmt.Println("  Its own test file is deleted afterwards. Your existing files are")
	fmt.Println("  never read, moved, changed or removed.")
	fmt.Println()

	suggested := suggestedFolder()
	for {
		fmt.Println("  Which folder shall I test?")
		fmt.Println("  (the speed measured is the speed of the drive that folder is on)")
		if suggested != "" {
			fmt.Printf("  (press Enter for %s)\n", suggested)
		}
		fmt.Print("  > ")

		if !in.Scan() {
			// stdin closed on us; there is nothing sensible left to ask.
			return
		}
		answer := strings.TrimSpace(in.Text())
		answer = strings.Trim(answer, `"`)
		if answer == "" {
			answer = suggested
		}
		if answer == "" {
			fmt.Println()
			fmt.Println("  I need a folder to test. Try again, or close this window.")
			fmt.Println()
			continue
		}

		info, err := os.Stat(answer)
		switch {
		case err != nil:
			fmt.Println()
			fmt.Printf("  I cannot find %q.\n", answer)
			fmt.Println("  Tip: you can drag a folder from Explorer onto this window to")
			fmt.Println("  paste its location, then press Enter.")
			fmt.Println()
			continue
		case !info.IsDir():
			fmt.Println()
			fmt.Printf("  %q is a file, not a folder. Give me the folder it sits in.\n", answer)
			fmt.Println()
			continue
		case !writable(answer):
			// The benchmark stops dead on a folder it cannot write to. Find
			// that out with one tiny file first, so the reader gets a plain
			// sentence and another go instead.
			fmt.Println()
			fmt.Printf("  I am not allowed to write in %q, so I cannot time a write there.\n", answer)
			fmt.Println("  Try a folder inside your own documents, or a drive you own.")
			fmt.Println()
			continue
		}

		fmt.Println()
		fmt.Printf("  Testing %s. This writes a %s file, then reads it back.\n", answer, guidedTestSize)
		fmt.Println("  It takes a few seconds. The test file is removed when I am done.")
		fmt.Println()
		runBench([]string{answer, "--size", guidedTestSize})
		break
	}

	fmt.Println()
	fmt.Println("  Done, and the test file is gone.")
	fmt.Println("  The command-line version can use a larger test file for a more")
	fmt.Println("  realistic number: diskops help")
	pause(in)
}

// guidedTestSize is deliberately smaller than the command-line default of
// 256MB. Somebody who double-clicked wants an answer now, and a folder on a
// slow USB stick would otherwise leave them staring at a blank window.
const guidedTestSize = "64MB"

// suggestedFolder offers somewhere to test that is certain to exist and is
// certain to be writable, so the reader can get a real number by pressing one
// key.
func suggestedFolder() string {
	home, err := os.UserHomeDir()
	if err != nil {
		return ""
	}
	return home
}

// writable reports whether we can create a file in dir. The benchmark needs
// exactly this, and asking cheaply here turns a hard stop into a question.
func writable(dir string) bool {
	f, err := os.CreateTemp(dir, ".diskops-probe-*")
	if err != nil {
		return false
	}
	name := f.Name()
	f.Close()
	os.Remove(name)
	return true
}

// pause keeps the console window open. Explorer closes it the moment the
// process exits, so without this the reader never sees the output.
func pause(in *bufio.Scanner) {
	fmt.Println()
	fmt.Print("  Press Enter to close this window. ")
	in.Scan()
}
