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.
//
// 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.
//
// FolderSync COPIES OVER and can DELETE files in a destination. The guided
// session therefore never performs a sync: it calls buildSyncPlan, which only
// reads and compares, and prints the plan. It never calls copyFile,
// versionFile or os.Remove, and it never asks for a version history directory,
// because a plan does not need one. It does not even create the destination
// folder — the destination must already exist before the plan can be shown.
func runGuided() {
	in := bufio.NewScanner(os.Stdin)

	fmt.Println()
	fmt.Println("  FolderSync")
	fmt.Println("  See what it would take to bring one folder up to date from another.")
	fmt.Println()
	fmt.Println("  Give it the folder to copy FROM and the folder to copy TO, and it")
	fmt.Println("  compares them file by file and lists what is new and what has")
	fmt.Println("  changed.")
	fmt.Println()
	fmt.Println("  This window compares and reports only. Nothing is copied, changed,")
	fmt.Println("  deleted or created in either folder.")
	fmt.Println()

	src := askFolder(in, "Which folder shall I copy FROM?", suggestedFolder())
	if src == "" {
		return
	}
	fmt.Println()
	dst := askFolder(in, "And which folder would it be copied TO?", "")
	if dst == "" {
		return
	}

	fmt.Println()
	fmt.Println("  Comparing. On large folders this can take a minute, because every")
	fmt.Println("  file that exists in both places is checksummed.")
	fmt.Println()
	comparePlan(src, dst)

	fmt.Println()
	fmt.Println("  That was a comparison only. Neither folder was touched.")
	fmt.Println("  The command-line version can carry the changes out, keeping a")
	fmt.Println("  recoverable copy of anything it replaces: foldersync help")
	pause(in)
}

// askFolder asks one question until it gets a folder that really exists,
// returning "" only when stdin has closed and there is nothing left to ask.
func askFolder(in *bufio.Scanner, question, suggested string) string {
	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 ""
		}
		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
	}
}

// comparePlan prints the same comparison the "sync" command prints when it is
// only reporting: which files are missing from the destination and which ones
// differ. Deletions are not considered at all here — buildSyncPlan is called
// with deletion planning switched off, so nothing in this output could ever
// remove a file.
func comparePlan(src, dst string) {
	plan, err := buildSyncPlan(src, dst, false)
	if err != nil {
		fmt.Println("  I could not compare those two folders.")
		fmt.Println("  One of them may hold something Windows will not let this program")
		fmt.Println("  read. Try a folder of ordinary documents.")
		return
	}

	if len(plan) == 0 {
		fmt.Printf("  Nothing to do: %s already matches %s.\n", dst, src)
		return
	}

	fmt.Printf("  From: %s\n", src)
	fmt.Printf("  To:   %s\n", dst)
	fmt.Println()

	var copies, updates int
	for _, a := range plan {
		switch a.kind {
		case actionCopy:
			fmt.Printf("  new       %s\n", a.relPath)
			copies++
		case actionUpdate:
			fmt.Printf("  changed   %s\n", a.relPath)
			updates++
		}
	}

	fmt.Println()
	fmt.Printf("  %d file(s) are missing from the destination and %d have changed.\n", copies, updates)
	fmt.Println("  Nothing was copied and nothing was overwritten.")
}

// 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 ""
	}
	// The folder people most often want a second copy of.
	for _, name := range []string{"Documents", "Desktop"} {
		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()
}
