package main

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"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 questions
// the program actually needs and stay on screen until the reader is done.
//
// The guided session runs "verify" and only "verify". That is the half of
// SyncProof that never writes a byte: it re-reads what is already at each
// destination and checks it against the source by SHA-256. Copying files to a
// destination stays a command-line act, typed on purpose — nobody should be
// able to start writing to a backup drive by double-clicking an icon.
//
// 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.
func runGuided() {
	in := bufio.NewScanner(os.Stdin)

	fmt.Println()
	fmt.Println("  SyncProof")
	fmt.Println("  Prove your copies really arrived, byte for byte.")
	fmt.Println()
	fmt.Println("  Give it the folder holding the originals and the copy you want")
	fmt.Println("  checked — a backup drive, a network share, a memory stick — and it")
	fmt.Println("  re-reads every file on both sides and compares their fingerprints,")
	fmt.Println("  so you find out which files are missing or have gone bad.")
	fmt.Println()
	fmt.Println("  It only reads. Nothing is copied, changed or deleted.")
	fmt.Println()

	src, ok := askFolder(in, "Which folder holds the originals?", suggestedFolder())
	if !ok {
		return
	}

	fmt.Println()
	var dests []string
	for {
		question := "And which copy shall I check?"
		if len(dests) > 0 {
			question = fmt.Sprintf("Another copy to check? (%d so far — press Enter to start)", len(dests))
		}
		fmt.Printf("  %s\n", question)
		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 == "" {
			if len(dests) > 0 {
				break
			}
			fmt.Println()
			fmt.Println("  I need the folder holding the copy. Try again, or close this")
			fmt.Println("  window.")
			fmt.Println()
			continue
		}
		if !folderExists(answer) {
			fmt.Println()
			fmt.Printf("  I cannot find a folder at %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
		}
		dests = append(dests, answer)
		fmt.Printf("  Added %s\n", answer)
		fmt.Println()
	}

	args := []string{src}
	for _, d := range dests {
		args = append(args, "--dest", d)
	}

	fmt.Println()
	fmt.Println("  Working. Every file on both sides is read in full, so on a large")
	fmt.Println("  folder this can take a few minutes.")
	fmt.Println()
	run("verify", args)

	fmt.Println()
	fmt.Println("  Done. Nothing on your disk was changed.")
	fmt.Println("  There is a command-line version too: syncproof --help")
	pause(in)
}

// askFolder puts one question, offering an optional one-keypress default, and
// keeps asking until the answer names a folder that really exists. It returns
// false only when stdin closes and there is nothing left to ask.
func askFolder(in *bufio.Scanner, question, suggested string) (string, bool) {
	for {
		fmt.Printf("  %s\n", question)
		if suggested != "" {
			fmt.Printf("  (press Enter for %s)\n", suggested)
		}
		fmt.Print("  > ")

		if !in.Scan() {
			return "", false
		}
		answer := strings.TrimSpace(in.Text())
		answer = strings.Trim(answer, `"`)
		if answer == "" {
			answer = suggested
		}
		if answer == "" {
			fmt.Println()
			fmt.Println("  I need a folder here. Try again, or close this window.")
			fmt.Println()
			continue
		}
		if !folderExists(answer) {
			fmt.Println()
			fmt.Printf("  I cannot find a folder at %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
		}
		return answer, true
	}
}

// folderExists answers the only question the prompts care about, and says no
// to a plain file rather than letting it through to a confusing error later.
func folderExists(path string) bool {
	info, err := os.Stat(path)
	return err == nil && info.IsDir()
}

// suggestedFolder offers somewhere that is certain to exist, so the reader can
// answer the first question with one keypress.
func suggestedFolder() string {
	home, err := os.UserHomeDir()
	if err != nil {
		return ""
	}
	// A home directory is the honest default, but the folders people copy to a
	// backup drive and later want proof of are the ones worth offering first.
	for _, name := range []string{"Documents", "Pictures"} {
		candidate := filepath.Join(home, name)
		if info, err := os.Stat(candidate); err == nil && info.IsDir() {
			return candidate
		}
	}
	return home
}

// 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()
}
