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 two
// questions the program actually needs and stay on screen until the reader is
// done.
//
// The guided session is deliberately a PREVIEW ONLY. SyncGuard can write to
// and remove files from a destination folder, and nobody should be able to
// trigger that by double-clicking an icon and pressing Enter twice. So this
// runs the same dry run the command line does by default: it reports the plan
// and touches nothing. Actually performing a sync stays a command-line act,
// typed on purpose.
//
// 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("  SyncGuard")
	fmt.Println("  Compare two folders and show what a copy would change.")
	fmt.Println()
	fmt.Println("  It reads every file in both folders, compares them by content, and")
	fmt.Println("  lists what is missing from the second folder and what has changed")
	fmt.Println("  since it was last copied there.")
	fmt.Println()
	fmt.Println("  This is a preview only. Nothing is copied, changed or deleted here.")
	fmt.Println()

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

	fmt.Println()
	dst, ok := askFolder(in, "And which folder should be compared against it?", "")
	if !ok {
		return
	}

	if sameFolder(src, dst) {
		fmt.Println()
		fmt.Println("  Those are the same folder, so there is nothing to compare.")
		fmt.Println("  Run it again and give me two different folders.")
		pause(in)
		return
	}

	fmt.Println()
	fmt.Println("  Working. Every file is read and checksummed, so on a large folder")
	fmt.Println("  this can take a few minutes.")
	fmt.Println()
	cmdMirror([]string{src, dst})

	fmt.Println()
	fmt.Println("  That was a preview. Nothing on your disk was changed.")
	fmt.Println("  The command-line version can carry the plan out once you are happy")
	fmt.Println("  with it: syncguard --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() {
			// stdin closed on us; there is nothing sensible left to ask.
			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
		}

		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
		}
		return answer, true
	}
}

// sameFolder catches the reader pointing both questions at one place, which
// would otherwise produce a confusingly empty plan.
func sameFolder(a, b string) bool {
	absA, errA := filepath.Abs(a)
	absB, errB := filepath.Abs(b)
	if errA != nil || errB != nil {
		return a == b
	}
	return filepath.Clean(absA) == filepath.Clean(absB)
}

// 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 actually
	// mirror to a backup drive 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()
}
