package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"os"
	"slices"
	"unicode/utf8"
)

func byteCounter(f *os.File) {
	fileInfo, err := f.Stat()
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
	fmt.Println(fileInfo.Size())
}

func lineCounter(f io.Reader) {
	buf := make([]byte, 32*1024)
	count := 0
	lineSep := []byte{'\n'}

	for {
		c, err := f.Read(buf) // c==0 if EOF

		switch {
		case err == io.EOF:
			fmt.Println(count)
			return

		case err != nil:
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}

		count += bytes.Count(buf[:c], lineSep)
	}
}

func wordCounter(f io.Reader) {
	buf := make([]byte, 32*1024)
	count := 0
	wordSep := []byte{' ', '\t', '\n', '\r', '\f', '\v'}
	inWord := false

	for {
		c, err := f.Read(buf)

		switch {
		case err == io.EOF:
			if inWord {
				count++
			}
			fmt.Println(count)
			return

		case err != nil:
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}

		for _, b := range buf[:c] {
			if slices.Contains(wordSep, b) {
				if inWord {
					count++
				}
				inWord = false
			} else {
				inWord = true
			}
		}
	}
}

func charCounter(f *os.File) {
	var line string
	var err error
	r := bufio.NewReader(f)
	count := 0

	for err == nil {
		line, err = r.ReadString('\n')
		count += utf8.RuneCountInString(line)
	}

	if err != io.EOF {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	fmt.Println(count)
}

func main() {

	cFlag := flag.Bool("c", false, "print the byte count")
	lFlag := flag.Bool("l", false, "print the line count")
	wFlag := flag.Bool("w", false, "print the word count")
	mFlag := flag.Bool("m", false, "print the character count")
	flag.Parse()
	fileName := flag.Arg(0)

	if len(fileName) == 0 {
		fmt.Fprintf(os.Stderr, "gowc [flags] filename\n")
		flag.Usage()
		os.Exit(1)
	}

	f, err := os.Open(fileName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
	defer f.Close()

	switch {
	case *cFlag:
		byteCounter(f)
	case *lFlag:
		lineCounter(f)
	case *wFlag:
		wordCounter(f)
	case *mFlag:
		charCounter(f)
	}
}
