package main
import (
	"bufio"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
)
// Where the output will be stored
const filename = "CA.txt"
// Read existing data from the file and find the next ID
func getNextID() int {
	file, err := os.Open(filename)
	if err != nil {
		// No file exists, ID starts from 2
		return 2
	}
	defer file.Close()
	maxID := 0
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" {
			continue
		}
		parts := strings.Split(line, ",")
		if len(parts) == 0 {
			continue
		}
		idStr := strings.Trim(parts[0], "[ca/:]")
		id, err := strconv.Atoi(idStr)
		if err == nil && id > maxID {
			maxID = id
		}
	}
	return maxID + 1
}
// Color struct with R, G, B as int and A as float64
type C struct {
	R, G, B int
	A       float64
}
// Generate random color
func rC(rMin, rMax, gMin, gMax, bMin, bMax int, aMin, aMax float64) C {
	return C{
	R: rand.Intn(rMax-rMin+1) + rMin,
	G: rand.Intn(gMax-gMin+1) + gMin,
	B: rand.Intn(bMax-bMin+1) + bMin,
	A: aMin + rand.Float64()*(aMax-aMin),
	}
}
// Define Morandi, Macaron and no color palettes
var (
	moC = rC(0, 255, 0, 255, 0, 255, 0.25, 0.5)
	maC = rC(0, 255, 0, 255, 0, 255, 1.0, 1.0)
	nC  = C{0, 0, 0, 0}
)
// Convert color struct to []interface{} {R, G, B, A*255 rounded}
func habCeCtCwsbCwspCaCInts2Intf(c C) []interface{} {
	return []interface{}{c.R, c.G, c.B, int(c.A*255 + 0.5)}
}
// Determine body shape based on firstsexgenes, ((1,1),(2,2),others) = (op 1-3,op 7-9,op 4-6)
func dbyS(fsg1, fsg2 int) int {
	switch {
	case fsg1 == 1 && fsg2 == 1:
		return rand.Intn(3) + 1
	case fsg1 == 2 && fsg2 == 2:
		return rand.Intn(3) + 7
	default:
		return rand.Intn(3) + 4
	}
}
// Determine tail, tooth, wing, antenna shape based on firstsexgenes (op 1-100, op 404 = no shape)
func dtlSthSwSaS(fsg1, fsg2, maxID int) (int, int, int, int) {
	switch {
	case fsg1 == 1 && fsg2 == 1:
		return maxID, maxID, 404, 404
	case fsg1 == 2 && fsg2 == 2:
		return 404, 404, 404, maxID
	default:
		return 404, 404, maxID, maxID
	}
}
// Generate random bloodlineIDs and random percentage, pick one random bloodlineID from total bloodlineIDs to have percentage 1
func rBarP() ([]int, []float64) {
	t := 100
	rB := make([]int, t)
	rP := make([]float64, t)
	for i := range rB {
		rB[i] = i + 1
	}
	// Pick one random bloodlineID to have percentage 1
	hBID := rand.Intn(t)
	rP[hBID] = 1.0
	return rB, rP
}
// Find the bloodlineID with the highest bloodline percentage
func fhBID(rB []int, rP []float64) int {
	hBID := 0
	for i := 1; i < len(rP); i++ {
		if rP[i] > rP[hBID] {
			hBID = i
		}
	}
	return rB[hBID]
}
// Generate random NPC
func gNPC() []interface{} {
	// Get the next sequential NPC ID
	playerID := getNextID()
	// Firstsexgene1, firstsexgene2 (op 1-2); secondsexgene1, secondsexgene2 (op 1-3)
	fsg1, fsg2 := rand.Intn(2)+1, rand.Intn(2)+1
	ssg1, ssg2 := rand.Intn(3)+1, rand.Intn(3)+1
	// Skin colors (predefined realistic RGBA range)
	sC := []int{rand.Intn(56) + 200, rand.Intn(51) + 100, rand.Intn(51) + 80, 255}
	// Determine color palettes for hairandbrow, eye, tail, wingsub, wingstripe and antenna color based on firstsexgenes combination 
	var habC, eC, tC, wsbC, wspC, aC C
	switch {
		case fsg1 == 1 && fsg2 == 1:
			habC, eC, tC = moC, moC, moC
			wsbC, wspC, aC = nC, nC, nC
		case fsg1 == 2 && fsg2 == 2:
			habC, eC = moC, moC
			tC, wsbC, wspC = nC, nC, nC
			aC = maC
		default:
			habC, eC, wsbC, wspC, aC = maC, maC, maC, maC, maC
			tC = nC
	}
	// Random shape attributes for face, brow, eye, ear, nose, mouth, fronthair, backhair, body, tail, tooth, wing and antenna shape (op 1-10)
	rS := func() int { return rand.Intn(10) + 1 }
	fS, bwS, eeS, erS, nS, mS, fhS, bhS := rS(), rS(), rS(), rS(), rS(), rS(), rS(), rS()
	byS := dbyS(fsg1, fsg2)
	// Random bloodlineIDs and bloodline percentages
	BID, BP := rBarP()
	maxID := fhBID(BID, BP)
	tlS, thS, wS, aS := dtlSthSwSaS(fsg1, fsg2, maxID)
	// Output
	outLen := 5 + len(sC) + 6*4 + 10 + 2*len(BID) + 1
	out := make([]interface{}, 0, outLen)
	out = append(out, playerID, fsg1, fsg2, ssg1, ssg2)
	for _, v := range sC {
		out = append(out, v)
	}
	for _, c := range []C{habC, eC, tC, wsbC, wspC, aC} {
		out = append(out, habCeCtCwsbCwspCaCInts2Intf(c)...)
	}
	out = append(out, fS, bwS, eeS, erS, nS, mS, fhS, bhS, byS, tlS, thS, wS, aS)
	for _, p := range BP {
		out = append(out, p)
	}
	out = append(out, maxID)
	return out
}
// Format output as expected
func formatOutput(data []interface{}) string {
	parts := make([]string, len(data))
	for i, v := range data {
		switch val := v.(type) {
		case float64:
			parts[i] = fmt.Sprintf("%.1f", val)
		default:
			parts[i] = fmt.Sprint(v)
		}
	}
	return "[ca/:" + strings.Join(parts, ",") + "]"
}
// Append the generated NPC output to the text file
func appendToFile(text string) {
	f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("Error opening file:", err)
		return
	}
	defer f.Close()
	_, err = f.WriteString(text + "\n")
	if err != nil {
		fmt.Println("Error writing to file:", err)
	}
}
// Start NPC generation
func main() {
	rand.Seed(time.Now().UnixNano())
	fmt.Println("Press 'R' then Enter to generate a random NPC...")
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Print("> ")
		input, _ := reader.ReadString('\n')
		input = strings.TrimSpace(input)
		if strings.EqualFold(input, "r") {
			npc := gNPC()
			// parent ID = 1 means orphan
			npc = append(npc, 1, 1)
			output := formatOutput(npc)
			fmt.Println(output)
			appendToFile(output)
		} else {
			fmt.Println("Please press 'R' then Enter to generate a random NPC.")
		}
	}
}