package main

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"strconv"
)

// This time, instead of decrementing every single fish for every day, I only compute the amount of fish
// I store the amount of fish per their day counter
// the fishies are the stored in a merely 8 integer long slice that stores the amount of fish.
// The index of the slice represents the amount of days left in their cycle
func main() {
	fishies, err := readInput("input")
	if err != nil {
		fmt.Printf("Error reading or something\n%s", err)
		return
	}

	fmt.Printf("Initializing fishies array...\n")
	fish, _ := splitFishies(fishies)
	fmt.Printf("Done!\n")

	fishiesptr := &fish
	// Run for i days
	for i := 0; i < 256; i++ {
		newfishies, _ := nextDay(*fishiesptr)
		fishiesptr = &newfishies
		amount := countFishies(*fishiesptr)
		fmt.Printf("On day %d: there are %d fishies\n", i+1, amount)
	}
}

func countFishies(fishies []int) int {
	counter := 0
	for i := 0; i < len(fishies); i++ {
		counter += fishies[i]
	}
	return counter
}

// Read the fishies slice and returns the slice of new fishies
func nextDay(fishies []int) ([]int, error) {
	// Move fishies at 0 into buffer, move all fishies down the stack
	// Add the 0 day fishies (from buffer) to the fishies at day 6
	// Add the same amount of fishies from buffer to the 8 day counter
	// Return the new splits

	buffer := fishies[0]
	for i := 0; i < len(fishies)-1; i++ {
		fishies[i] = fishies[i+1]
	}
	fishies[6] += buffer
	fishies[8] = buffer
	return fishies, nil
}

// Returns a slice of the amount of fishies per day, ordered from 0 days to 8
func splitFishies(fishies []int) ([]int, error) {
	fishiesButSplit := make([]int, 9)
	for i := 0; i < len(fishies); i++ {
		fishiesButSplit[fishies[i]]++
	}

	return fishiesButSplit, nil
}

func readInput(inputfile string) ([]int, error) {
	file, err := os.OpenFile(inputfile, 0, 0)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	splitfunc := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
		nextIndex := bytes.IndexByte(data, ',')
		if nextIndex > 0 {
			// Returning the next position aka taking only the stuff up to nextIndex and slicing off everything else
			buffer := data[:nextIndex]
			// nextIndex plus 1 because index is at the comma, we want the next number
			return nextIndex + 1, bytes.TrimSpace(buffer), nil
		}

		// If we are at the end of the buffer, then return the entire buffer, but only if there is data
		if atEOF {
			if len(data) > 0 {
				return len(data), bytes.TrimSpace(data), nil
			}
		}

		// https://github.com/kgrz/reading-files-in-go/blob/master/comma-separated-string.go
		// For more info
		return 0, nil, nil
	}

	scanner := bufio.NewScanner(file)
	scanner.Split(splitfunc)

	fishies := make([]int, 0)
	for scanner.Scan() {
		fish, err := strconv.Atoi(scanner.Text())
		if err != nil {
			fmt.Printf("Error converting string to integer\n")
			return nil, err
		}
		fishies = append(fishies, fish)
	}
	return fishies, nil
}