package dup

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

// cf. TGPL p. 11
// NOTE: A map is a reference to the data structure created by make (p. 12)
func countLines(f *os.File, counts map[string]int, names map[string]string) {
	input := bufio.NewScanner(f)
	const sep = ", "

	seenLine := make(map[string]bool)
	// keeps of whether the filename was
	// already added to the given line
	// to prevent duplicate filenames

	for input.Scan() { // TODO: error handling
		counts[input.Text()]++
		if names != nil {
			if seenLine[input.Text()] {
				continue
			}
			seenLine[input.Text()] = true
			if names[input.Text()] == "" {
				names[input.Text()] = f.Name()
			} else {
				// NOTE: inefficient
				names[input.Text()] += sep + f.Name()
			}
		}
	}
}

// Exercise 1.4: Modify dup2 to print the names of all files in which each duplicated line occurs.
func Dup2(printFname bool) {
	var names map[string]string
	if printFname {
		names = make(map[string]string)
	}

	fnames := os.Args[1:]
	counts := make(map[string]int)

	if len(fnames) == 0 {
		countLines(os.Stdin, counts, names)
	} else {
		for _, fname := range fnames {
			f, err := os.Open(fname)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Dup2: %v\n", err)
				continue
			}
			countLines(f, counts, names)
			f.Close()
		}
	}

	for line, cnt := range counts {
		if cnt > 1 {
			// cool: %q format specifier
			if printFname {
				fmt.Printf("%d duplicates of\t%q (found in file(s) %q)\n", cnt, line, names[line])
			} else {
				fmt.Printf("%d duplicates of\t%q\n", cnt, line)
			}
		}
	}
}
