package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/fs"
	"os"
	"path/filepath"
)

func main() {
	// Direktori kerja sementara agar program aman dijalankan berulang.
	dir, err := os.MkdirTemp("", "contoh-baca-*")
	if err != nil {
		fmt.Println("gagal membuat direktori:", err)
		return
	}
	defer os.RemoveAll(dir)

	berkas := filepath.Join(dir, "catatan.txt")
	isi := "baris pertama\nbaris kedua\nbaris ketiga\n"
	if err := os.WriteFile(berkas, []byte(isi), 0644); err != nil {
		fmt.Println("gagal menulis berkas:", err)
		return
	}

	// Pola 1: os.ReadFile memuat seluruh isi berkas ke memori.
	data, err := os.ReadFile(berkas)
	if err != nil {
		fmt.Println("gagal membaca berkas:", err)
		return
	}
	fmt.Printf("os.ReadFile : %d byte\n", len(data))

	// Pola 2: os.Open menghasilkan handle, io.ReadAll membaca isinya.
	n, err := bacaDenganHandle(berkas)
	if err != nil {
		fmt.Println("gagal membaca berkas:", err)
		return
	}
	fmt.Printf("io.ReadAll  : %d byte\n", n)

	// Pola 3: bufio.Scanner membaca baris demi baris.
	baris, err := tampilkanBaris(berkas)
	if err != nil {
		fmt.Println("gagal memindai berkas:", err)
		return
	}
	fmt.Printf("scanner     : %d baris\n", baris)

	// Pola 4: Read mengisi buffer potongan demi potongan.
	total, err := bacaPerPotongan(berkas)
	if err != nil {
		fmt.Println("gagal membaca potongan:", err)
		return
	}
	fmt.Printf("loop Read   : %d byte\n", total)

	// Berkas yang tidak ada menghasilkan error nyata dari sistem operasi.
	if _, err := os.ReadFile("berkas-hilang.txt"); err != nil {
		fmt.Println("error baca  :", err)
		fmt.Println("ErrNotExist :", errors.Is(err, fs.ErrNotExist))
	}
}

// bacaDenganHandle membuka berkas lalu membaca seluruh isinya
// melalui handle yang dijamin tertutup oleh defer.
func bacaDenganHandle(berkas string) (int, error) {
	f, err := os.Open(berkas)
	if err != nil {
		return 0, err
	}
	defer f.Close()

	data, err := io.ReadAll(f)
	if err != nil {
		return 0, err
	}
	return len(data), nil
}

// tampilkanBaris mencetak setiap baris berkas lalu mengembalikan jumlahnya.
func tampilkanBaris(berkas string) (int, error) {
	f, err := os.Open(berkas)
	if err != nil {
		return 0, err
	}
	defer f.Close()

	baris := 0
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		baris++
		fmt.Println("  " + scanner.Text())
	}
	return baris, scanner.Err()
}

// bacaPerPotongan membaca berkas dengan buffer 16 byte per panggilan Read.
func bacaPerPotongan(berkas string) (int, error) {
	f, err := os.Open(berkas)
	if err != nil {
		return 0, err
	}
	defer f.Close()

	buf := make([]byte, 16)
	total := 0
	for {
		n, err := f.Read(buf)
		if n > 0 {
			total += n
		}
		if err == io.EOF {
			return total, nil
		}
		if err != nil {
			return total, err
		}
	}
}
