package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"
)

// <-- add a struct type to send responses back on the channel
type result struct {
	path  string
	count int
	err   error
}

func main() {
	start := time.Now()

	wg := sync.WaitGroup{}
	ch := make(chan result)

	// begin watching the channel to process results.
	// Do this in a go routine so it doesn't block program execution
	go func() {
		for r := range ch {
			func() {
				defer wg.Done()
				if r.err != nil {
					fmt.Println(r.err)
					return
				}
				fmt.Printf("%s has %d characters\n", r.path, r.count)
			}()
		}
	}()

	root := "./files"
	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			wg.Add(1) // make sure to track each go routine you launch in the wait group
			go charCount(path, ch)
		}
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}

	wg.Wait() // wait for all go routines to finish
	close(ch) // close the channel to exit the range go routine

	fmt.Println("process took", time.Since(start))
}

func charCount(path string, ch chan result) { // <-- Accept the channel as an argument
	f, err := os.Open(path)
	if err != nil {
		// <-- send back the error on a channel
		ch <- result{path: path, count: 0, err: fmt.Errorf("error opening %s: %s\n", path, err)}
		return
	}
	defer f.Close()
	var count int
	buff := make([]byte, bytes.MinRead)
	for {
		i, err := f.Read(buff)
		count += i
		if err == io.EOF {
			// <-- send back the result on a channel
			ch <- result{path: path, count: count}
			return
		}
		if err != nil {
			ch <- result{path: path, count: 0, err: fmt.Errorf("error reading file %s: %s\n", path, err)}
			return
		}
	}
}
