package main

import (
	"fmt"
	"math"
	"math/rand"
)

// ПРИМЕЧАНИЕ: "Странник" — текстовая космоторговля, вдохновлённая
// классической Elite (процедурная галактика, торговля, апгрейды
// корабля, риск в пути), но НЕ является её копией: собственный
// алгоритм генерации галактики (не побайтовое воспроизведение
// оригинального 6502-алгоритма), собственные названия кораблей,
// рас и систем, свой список квестов. Настоящего 3D-полёта в
// реальном времени нет — это сознательное решение: честная 3D-
// графика в терминале через Bubble Tea потребовала бы отдельного
// рендер-движка сопоставимой с самой игрой сложности. Вместо этого
// перелёты и опасные встречи разрешаются текстом с выбором.

// WayfarerSystemID — идентификатор звёздной системы: галактика +
// порядковый номер внутри неё. Все свойства системы выводятся из
// этого идентификатора детерминированно (как исходная генерация
// Elite) — хранить нужно только идентификатор, не сами данные.
type WayfarerSystemID struct {
	Galaxy int
	Index  int
}

// MarshalText/UnmarshalText — encoding/json не умеет сериализовать
// map с ключом-структурой напрямую; эти методы позволяют
// WayfarerSystemID быть ключом map[WayfarerSystemID]bool (поле
// Visited) при сохранении карьеры в JSON.
func (id WayfarerSystemID) MarshalText() ([]byte, error) {
	return []byte(fmt.Sprintf("%d:%d", id.Galaxy, id.Index)), nil
}

func (id *WayfarerSystemID) UnmarshalText(text []byte) error {
	_, err := fmt.Sscanf(string(text), "%d:%d", &id.Galaxy, &id.Index)
	return err
}

const (
	WayfarerGalaxyCount      = 4
	WayfarerSystemsPerGalaxy = 128
	wayfarerGalaxyMapSize    = 500.0 // условные световые годы на сторону карты галактики
)

// WayfarerGovernment — тип правительства системы: влияет на
// опасность перелётов (шанс встретить пиратов) и на легальность
// некоторых товаров.
type WayfarerGovernment int

const (
	GovAnarchy WayfarerGovernment = iota
	GovFeudal
	GovDictatorship
	GovCommunist
	GovConfederacy
	GovDemocracy
	GovCorporateState
	wayfarerGovernmentCount
)

// wayfarerGovernmentDangerFactor — во сколько раз обычная опасность
// пути умножается для системы с этим правительством (анархия
// заметно опаснее корпоративного государства).
var wayfarerGovernmentDangerFactor = map[WayfarerGovernment]float64{
	GovAnarchy:        2.2,
	GovFeudal:         1.5,
	GovDictatorship:   1.3,
	GovCommunist:      1.1,
	GovConfederacy:    0.9,
	GovDemocracy:      0.8,
	GovCorporateState: 0.6,
}

// WayfarerSystem — производные (детерминированно вычисленные)
// данные одной звёздной системы.
type WayfarerSystem struct {
	ID         WayfarerSystemID
	Name       string
	Government WayfarerGovernment
	TechLevel  int     // 0-15
	Industrial float64 // 0 (чисто аграрная) .. 1 (чисто промышленная)
	Wealth     float64 // 0 (бедная) .. 1 (богатая)
	Population float64 // в миллионах, условно
	X, Y       float64 // координаты на карте галактики (условные св. годы)
}

// wayfarerGalaxySeed — необязательная "пасхалка": если рядом с
// исполняемым файлом (или в текущей рабочей директории) найден файл
// .env со строкой WAYFARER_GALAXY_SEED=<число>, вся галактика
// перегенерируется заново с другим содержимым (как изменение
// исходных чисел генератора в оригинальной Elite). По умолчанию 0 —
// это ничего не меняет в хэше (см. systemSeed) и даёт ту же самую
// галактику, что и без файла .env вовсе.
var wayfarerGalaxySeed uint64

// systemSeed детерминированно превращает идентификатор системы в
// 64-битное зерно генератора случайности — одна и та же система при
// одном и том же wayfarerGalaxySeed всегда даёт одни и те же данные.
func systemSeed(id WayfarerSystemID) int64 {
	// простой, но достаточно хорошо перемешивающий детерминированный
	// хэш (вариант splitmix64) — не претендует на криптостойкость,
	// только на визуально несвязанный разброс результатов
	x := uint64(id.Galaxy)*1000003 + uint64(id.Index) + 0x9E3779B97F4A7C15 + wayfarerGalaxySeed
	x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9
	x = (x ^ (x >> 27)) * 0x94D049BB133111EB
	x = x ^ (x >> 31)
	return int64(x)
}

var wayfarerNameSyllables = []string{
	"di", "so", "lav", "en", "ra", "or", "bi", "lei", "ce", "ta",
	"zon", "quen", "ve", "usu", "phi", "gal", "ari", "nex", "tor", "eth",
	"in", "us", "on", "ax", "yr", "ul", "im", "os", "an", "el",
}

// generateSystemName собирает произносимое "инопланетное" название
// из 2-3 слогов на основе персонального генератора системы.
func generateSystemName(rng *rand.Rand) string {
	syllables := 2 + rng.Intn(2)
	name := ""
	for i := 0; i < syllables; i++ {
		name += wayfarerNameSyllables[rng.Intn(len(wayfarerNameSyllables))]
	}
	return capitalizeFirst(name)
}

func capitalizeFirst(s string) string {
	if s == "" {
		return s
	}
	r := []rune(s)
	if r[0] >= 'a' && r[0] <= 'z' {
		r[0] = r[0] - 'a' + 'A'
	}
	return string(r)
}

// GenerateSystem детерминированно вычисляет все данные системы по
// её идентификатору.
func GenerateSystem(id WayfarerSystemID) WayfarerSystem {
	rng := rand.New(rand.NewSource(systemSeed(id)))
	return WayfarerSystem{
		ID:         id,
		Name:       generateSystemName(rng),
		Government: WayfarerGovernment(rng.Intn(int(wayfarerGovernmentCount))),
		TechLevel:  rng.Intn(16),
		Industrial: rng.Float64(),
		Wealth:     rng.Float64(),
		Population: math.Round(rng.Float64()*450*10) / 10,
		X:          rng.Float64() * wayfarerGalaxyMapSize,
		Y:          rng.Float64() * wayfarerGalaxyMapSize,
	}
}

// Distance — расстояние между двумя системами в условных световых
// годах (координаты считаются в пределах одной и той же галактики;
// межгалактический прыжок обрабатывается отдельно).
func (s WayfarerSystem) Distance(other WayfarerSystem) float64 {
	dx := s.X - other.X
	dy := s.Y - other.Y
	return math.Sqrt(dx*dx + dy*dy)
}

// DangerFactor — во сколько раз обычная базовая опасность перелёта
// из/в эту систему умножается из-за её правительства.
func (s WayfarerSystem) DangerFactor() float64 {
	return wayfarerGovernmentDangerFactor[s.Government]
}

func (s WayfarerSystem) String() string {
	return fmt.Sprintf("%s (G%d/%d)", s.Name, s.ID.Galaxy, s.ID.Index)
}
